"""Grep search tool — search file contents with regex (pure Python)."""

from __future__ import annotations

import re
from pathlib import Path
from typing import Any

from tsumugi.tools.base import BaseTool, PermissionLevel, ToolSpec

# Extensions to skip (binary files)
BINARY_EXTENSIONS = {
    ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ico", ".svg",
    ".woff", ".woff2", ".ttf", ".eot", ".otf",
    ".pdf", ".zip", ".gz", ".tar", ".rar", ".7z",
    ".exe", ".dll", ".so", ".dylib", ".o",
    ".pyc", ".pyo", ".class",
    ".mp3", ".mp4", ".avi", ".mov", ".mkv",
    ".db", ".sqlite", ".sqlite3",
}


class GrepSearchTool(BaseTool):
    """Search file contents using regex patterns."""

    def spec(self) -> ToolSpec:
        return ToolSpec(
            name="grep_search",
            description=(
                "Search file contents for a regex pattern. "
                "Returns matching lines with file paths and line numbers. "
                "Supports file type filtering with glob."
            ),
            parameters={
                "type": "object",
                "properties": {
                    "pattern": {
                        "type": "string",
                        "description": "Regular expression pattern to search for",
                    },
                    "path": {
                        "type": "string",
                        "description": "File or directory to search in. Default: current directory",
                    },
                    "glob": {
                        "type": "string",
                        "description": 'Glob to filter files (e.g. "*.py", "*.{ts,tsx}")',
                    },
                    "case_insensitive": {
                        "type": "boolean",
                        "description": "Case insensitive search. Default: false",
                    },
                },
                "required": ["pattern"],
            },
            permission=PermissionLevel.READ,
        )

    def execute(self, **kwargs: Any) -> str:
        pattern_str = kwargs["pattern"]
        search_path = Path(kwargs.get("path", ".")).resolve()
        file_glob = kwargs.get("glob")
        case_insensitive = kwargs.get("case_insensitive", False)

        flags = re.IGNORECASE if case_insensitive else 0
        try:
            regex = re.compile(pattern_str, flags)
        except re.error as e:
            return f"Error: Invalid regex pattern: {e}"

        if not search_path.exists():
            return f"Error: Path not found: {search_path}"

        # Collect files to search
        if search_path.is_file():
            files = [search_path]
        else:
            if file_glob:
                files = list(search_path.rglob(file_glob))
            else:
                files = list(search_path.rglob("*"))
            files = [f for f in files if f.is_file()]

        # Filter out binary files
        files = [
            f for f in files
            if f.suffix.lower() not in BINARY_EXTENSIONS
        ]

        results: list[str] = []
        max_matches = 500
        match_count = 0

        for filepath in sorted(files):
            if match_count >= max_matches:
                break

            try:
                text = filepath.read_text(encoding="utf-8", errors="replace")
            except OSError:
                continue

            for line_num, line in enumerate(text.splitlines(), 1):
                if match_count >= max_matches:
                    break
                if regex.search(line):
                    try:
                        rel = filepath.relative_to(search_path)
                    except ValueError:
                        rel = filepath
                    # Truncate long lines
                    display_line = line.strip()
                    if len(display_line) > 200:
                        display_line = display_line[:200] + "..."
                    results.append(f"{rel}:{line_num}: {display_line}")
                    match_count += 1

        if not results:
            return f"No matches for '{pattern_str}'"

        header = f"Found {match_count} match(es) for '{pattern_str}':"
        if match_count >= max_matches:
            header += f" (showing first {max_matches})"
        return header + "\n" + "\n".join(results)
