from __future__ import annotations

import re
from pathlib import Path


class TaskModeRouter:
    _FIX_KEYWORDS = ("fix", "repair", "resolve", "patch", "correct", "debug")
    _ANALYSIS_KEYWORDS = ("summarize", "inspect", "explain", "analyze", "review", "describe")
    _FEATURE_KEYWORDS = ("build", "implement", "add", "create", "develop")
    _REFACTOR_KEYWORDS = ("refactor", "clean up", "cleanup", "restructure", "simplify")

    def detect_mode(self, *, goal: str, repo_path: Path, test_command: str | None = None) -> str:
        lowered = goal.lower()
        mentioned_files = self._mentioned_files(goal, repo_path)
        mentioned_source_files = [path for path in mentioned_files if path.suffix in {".py", ".js", ".ts", ".tsx", ".jsx"}]
        has_fix_keyword = any(keyword in lowered for keyword in self._FIX_KEYWORDS)
        has_analysis_keyword = any(keyword in lowered for keyword in self._ANALYSIS_KEYWORDS)
        has_feature_keyword = any(keyword in lowered for keyword in self._FEATURE_KEYWORDS)
        has_refactor_keyword = any(keyword in lowered for keyword in self._REFACTOR_KEYWORDS)
        repo_has_tests = (repo_path / "tests").exists() or any(path.name.startswith("test_") for path in repo_path.rglob("*.py"))

        if has_analysis_keyword and not any((has_fix_keyword, has_feature_keyword, has_refactor_keyword)):
            return "analysis_only"
        if has_refactor_keyword:
            return "refactor"
        if has_fix_keyword and len(mentioned_source_files) == 1:
            return "focused_file_repair"
        if has_fix_keyword and (test_command or repo_has_tests or "test" in lowered or "validation" in lowered):
            return "failing_test_repair"
        if has_feature_keyword:
            return "feature_build"
        return "general_coding"

    def _mentioned_files(self, goal: str, repo_path: Path) -> list[Path]:
        relevant_files = sorted(path for path in repo_path.rglob("*") if path.is_file() and self._is_relevant(path, repo_path))
        indexed_paths = {
            path.relative_to(repo_path).as_posix().lower(): path
            for path in relevant_files
        }
        by_name: dict[str, list[Path]] = {}
        for file_path in relevant_files:
            by_name.setdefault(file_path.name.lower(), []).append(file_path)

        matches: list[Path] = []
        seen: set[Path] = set()
        for raw_match in re.findall(r"[\w./\\-]+\.[A-Za-z0-9]+", goal):
            normalized = raw_match.strip("`'\"").replace("\\", "/").lstrip("./").lower()
            direct = indexed_paths.get(normalized)
            if direct is not None and direct not in seen:
                matches.append(direct)
                seen.add(direct)
                continue

            file_name = Path(normalized).name
            for candidate in by_name.get(file_name, []):
                if candidate not in seen:
                    matches.append(candidate)
                    seen.add(candidate)
        return matches

    @staticmethod
    def _is_relevant(file_path: Path, repo_path: Path) -> bool:
        ignored_parts = {".git", ".venv", "node_modules", "__pycache__", "artifacts", "data"}
        relative_parts = file_path.relative_to(repo_path).parts
        return not any(part in ignored_parts for part in relative_parts)
