"""Grep tool — regex/literal search in files."""

from __future__ import annotations

import os
import re
from typing import Any

from pi_agents.agent.types import AgentTool, AgentToolResult
from pi_agents.ai.types import TextContent
from pi_agents.coding_agent.tools.truncate import truncate_head, truncate_line, GREP_MAX_LINE_LENGTH


GREP_TOOL_NAME = "grep"
GREP_TOOL_DESCRIPTION = "Search for patterns in files using regex or literal strings."

GREP_TOOL_PARAMETERS = {
    "type": "object",
    "properties": {
        "pattern": {"type": "string", "description": "Regex or literal string to search for"},
        "path": {"type": "string", "description": "Directory or file to search in"},
        "glob": {"type": "string", "description": "Filter by glob pattern"},
        "ignoreCase": {"type": "boolean", "description": "Case-insensitive search"},
        "literal": {"type": "boolean", "description": "Treat pattern as literal string"},
        "context": {"type": "integer", "description": "Lines of context around matches"},
        "limit": {"type": "integer", "description": "Max matches (default: 100)"},
    },
    "required": ["pattern"],
}


def create_grep_tool(cwd: str | None = None) -> AgentTool:
    """Create a grep tool for searching file contents."""

    class GrepTool(AgentTool):
        def __init__(self) -> None:
            super().__init__(
                name=GREP_TOOL_NAME,
                description=GREP_TOOL_DESCRIPTION,
                parameters=GREP_TOOL_PARAMETERS,
            )
            self.label = "Grep"

        async def execute(
            self,
            tool_call_id: str,
            params: dict[str, Any],
            signal: Any = None,
            on_update: Any = None,
        ) -> AgentToolResult:
            pattern = params.get("pattern", "")
            search_path = params.get("path", "")
            glob_filter = params.get("glob")
            ignore_case = params.get("ignoreCase", False)
            literal = params.get("literal", False)
            context = params.get("context", 0)
            limit = params.get("limit", 100)

            base = search_path or cwd or os.getcwd()
            if cwd and not os.path.isabs(base):
                base = os.path.join(cwd, base)

            flags = re.IGNORECASE if ignore_case else 0
            try:
                if literal:
                    regex = re.compile(re.escape(pattern), flags)
                else:
                    regex = re.compile(pattern, flags)
            except re.error as e:
                return AgentToolResult(
                    content=[TextContent(type="text", text=f"Invalid regex: {e}")]
                )

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

            def search_file(filepath: str) -> None:
                nonlocal match_count
                try:
                    with open(filepath, "r", encoding="utf-8", errors="replace") as f:
                        lines = f.readlines()
                except Exception:
                    return

                rel_path = os.path.relpath(filepath, base).replace("\\", "/")

                for i, line in enumerate(lines):
                    if match_count >= limit:
                        return
                    if regex.search(line):
                        match_count += 1
                        truncated, _ = truncate_line(line.rstrip("\n"), GREP_MAX_LINE_LENGTH)
                        results.append(f"{rel_path}:{i + 1}:{truncated}")

            if os.path.isfile(base):
                search_file(base)
            else:
                import glob as globmod
                if glob_filter:
                    files = globmod.glob(os.path.join(base, "**", glob_filter), recursive=True)
                else:
                    files = globmod.glob(os.path.join(base, "**", "*"), recursive=True)

                for fp in sorted(files):
                    if match_count >= limit:
                        break
                    if os.path.isfile(fp):
                        search_file(fp)

            result_text = "\n".join(results) if results else "(no matches)"
            tr = truncate_head(result_text)

            details: dict[str, Any] | None = None
            if match_count >= limit:
                details = {"matchLimitReached": limit}

            return AgentToolResult(
                content=[TextContent(type="text", text=tr.content)],
                details=details,
            )

    return GrepTool()
