from __future__ import annotations

from pathlib import Path

from ai_orchestrator.config.settings import Settings
from ai_orchestrator.schemas.tool_result import ToolResult


class FileTools:
    def __init__(self, settings: Settings) -> None:
        self.settings = settings

    def read_file(self, path: Path) -> ToolResult:
        try:
            content = path.read_text(encoding="utf-8")
        except FileNotFoundError:
            return ToolResult(success=False, stderr=f"File not found: {path}")
        return ToolResult(success=True, stdout=content, data={"path": str(path)})

    def write_file(self, path: Path, content: str) -> ToolResult:
        try:
            if path.exists() and path.is_dir():
                return ToolResult(success=False, stderr=f"Target path is a directory, not a file: {path}")
            path.parent.mkdir(parents=True, exist_ok=True)
            path.write_text(content, encoding="utf-8")
        except OSError as exc:
            return ToolResult(success=False, stderr=str(exc), data={"path": str(path)})
        return ToolResult(success=True, stdout=f"Wrote {path}", data={"path": str(path)})

    def search_repo(self, repo_path: Path, query: str) -> ToolResult:
        matches: list[dict[str, str | int]] = []
        for file_path in repo_path.rglob("*"):
            if not file_path.is_file() or self._is_ignored(file_path.relative_to(repo_path)):
                continue
            try:
                lines = file_path.read_text(encoding="utf-8").splitlines()
            except (UnicodeDecodeError, OSError):
                continue
            for line_number, line in enumerate(lines, start=1):
                if query.lower() in line.lower():
                    matches.append(
                        {
                            "path": str(file_path.relative_to(repo_path).as_posix()),
                            "line": line_number,
                            "text": line.strip(),
                        }
                    )
        return ToolResult(success=True, data={"matches": matches}, stdout=f"{len(matches)} matches")

    def inspect_repo(self, repo_path: Path) -> ToolResult:
        files: list[str] = []
        previews: list[dict[str, str]] = []
        for file_path in sorted(repo_path.rglob("*")):
            if len(files) >= self.settings.repo_scan_file_limit:
                break
            if not file_path.is_file() or self._is_ignored(file_path.relative_to(repo_path)):
                continue
            relative_path = file_path.relative_to(repo_path).as_posix()
            files.append(relative_path)
            if len(previews) >= self.settings.repo_preview_files:
                continue
            try:
                raw = file_path.read_text(encoding="utf-8")
            except (UnicodeDecodeError, OSError):
                continue
            previews.append(
                {
                    "path": relative_path,
                    "content": raw[: self.settings.repo_preview_bytes],
                }
            )

        readme_path = repo_path / "README.md"
        readme = None
        if readme_path.exists():
            try:
                readme = readme_path.read_text(encoding="utf-8")[: self.settings.repo_preview_bytes]
            except OSError:
                readme = None

        return ToolResult(
            success=True,
            stdout=f"Indexed {len(files)} files",
            data={
                "repo_path": str(repo_path),
                "file_count": len(files),
                "files": files,
                "previews": previews,
                "readme": readme,
                "is_git_repo": (repo_path / ".git").exists(),
            },
        )

    @staticmethod
    def _is_ignored(path: Path) -> bool:
        ignored_parts = {".git", ".venv", "node_modules", "__pycache__", "artifacts", "data"}
        return any(part in ignored_parts for part in path.parts)
