"""Search operation handlers for MCP vector search server."""

from pathlib import Path
from typing import Any

from mcp.types import CallToolResult, TextContent

from ..core.search import SemanticSearchEngine


class SearchHandlers:
    """Handlers for search-related MCP tool operations."""

    def __init__(self, search_engine: SemanticSearchEngine, project_root: Path):
        """Initialize search handlers.

        Args:
            search_engine: Semantic search engine instance
            project_root: Project root directory
        """
        self.search_engine = search_engine
        self.project_root = project_root

    async def handle_search_code(self, args: dict[str, Any]) -> CallToolResult:
        """Handle search_code tool call.

        Args:
            args: Tool call arguments containing query, filters, etc.

        Returns:
            CallToolResult with search results or error
        """
        query = args.get("query", "")
        limit = args.get("limit", 10)
        similarity_threshold = args.get("similarity_threshold", 0.3)
        file_extensions = args.get("file_extensions")
        language = args.get("language")
        function_name = args.get("function_name")
        class_name = args.get("class_name")
        files = args.get("files")
        project_name = args.get("project_name")
        expand = args.get("expand", True)
        use_rerank = args.get("use_rerank", True)
        rerank_top_n = args.get("rerank_top_n", 50)
        include_git_blame = args.get("include_git_blame", False)

        if not query:
            return CallToolResult(
                content=[TextContent(type="text", text="Query parameter is required")],
                isError=True,
            )

        # Extension to language mapping
        ext_to_lang = {
            ".py": "python",
            ".pyw": "python",
            ".js": "javascript",
            ".mjs": "javascript",
            ".cjs": "javascript",
            ".ts": "typescript",
            ".tsx": "typescript",
            ".jsx": "javascript",
            ".java": "java",
            ".go": "go",
            ".rs": "rust",
            ".rb": "ruby",
            ".php": "php",
            ".dart": "dart",
            ".swift": "swift",
            ".kt": "kotlin",
            ".kts": "kotlin",
            ".scala": "scala",
            ".c": "c",
            ".h": "c",
            ".cpp": "cpp",
            ".cc": "cpp",
            ".cxx": "cpp",
            ".hpp": "cpp",
            ".cs": "csharp",
            ".lua": "lua",
            ".r": "r",
            ".sql": "sql",
            ".sh": "bash",
            ".bash": "bash",
            ".yml": "yaml",
            ".yaml": "yaml",
            ".json": "json",
            ".xml": "xml",
            ".html": "html",
            ".htm": "html",
            ".css": "css",
            ".scss": "scss",
            ".md": "markdown",
        }

        # Build filters
        filters = {}
        if file_extensions:
            # Convert file extensions to language names
            languages = [
                ext_to_lang.get(ext.lower(), ext.lower().lstrip("."))
                for ext in file_extensions
            ]
            languages = list(set(languages))  # dedupe
            if len(languages) == 1:
                filters["language"] = languages[0]
            else:
                filters["language"] = languages
        if language:
            filters["language"] = language
        if function_name:
            filters["function_name"] = function_name
        if class_name:
            filters["class_name"] = class_name
        if project_name:
            filters["subproject_name"] = project_name
        if files:
            filters["file_pattern"] = files

        # Get search mode and hybrid_alpha parameters
        search_mode_str = args.get("search_mode", "hybrid")
        hybrid_alpha = args.get("hybrid_alpha", 0.7)

        # Convert search_mode string to enum
        from ..core.search import SearchMode

        try:
            search_mode = SearchMode(search_mode_str.lower())
        except ValueError:
            search_mode = SearchMode.HYBRID

        # Perform search
        results = await self.search_engine.search(
            query=query,
            limit=limit,
            similarity_threshold=similarity_threshold,
            filters=filters,
            expand=expand,
            use_rerank=use_rerank,
            rerank_top_n=rerank_top_n,
            search_mode=search_mode,
            hybrid_alpha=hybrid_alpha,
        )

        # Enrich with git blame if requested
        if include_git_blame and results:
            from ..core.git_blame import enrich_with_git_blame

            await enrich_with_git_blame(results, self.project_root)

        # Format results
        response_text = self._format_search_results(results, query)
        return CallToolResult(content=[TextContent(type="text", text=response_text)])

    async def handle_search_similar(self, args: dict[str, Any]) -> CallToolResult:
        """Handle search_similar tool call.

        Args:
            args: Tool call arguments containing file_path, etc.

        Returns:
            CallToolResult with similar code results or error
        """
        file_path = args.get("file_path", "")
        function_name = args.get("function_name")
        limit = args.get("limit", 10)
        similarity_threshold = args.get("similarity_threshold", 0.3)
        search_mode = args.get("search_mode", "hybrid")
        hybrid_alpha = float(args.get("hybrid_alpha", 0.7))

        if not file_path:
            return CallToolResult(
                content=[
                    TextContent(type="text", text="file_path parameter is required")
                ],
                isError=True,
            )

        try:
            # Convert to Path object
            file_path_obj = Path(file_path)
            if not file_path_obj.is_absolute():
                file_path_obj = self.project_root / file_path_obj

            if not file_path_obj.exists():
                return CallToolResult(
                    content=[
                        TextContent(type="text", text=f"File not found: {file_path}")
                    ],
                    isError=True,
                )

            # Convert search_mode string to enum
            from ..core.search import SearchMode

            try:
                search_mode_enum = SearchMode(search_mode.lower())
            except ValueError:
                search_mode_enum = SearchMode.HYBRID

            # Run similar search
            results = await self.search_engine.search_similar(
                file_path=file_path_obj,
                function_name=function_name,
                limit=limit,
                similarity_threshold=similarity_threshold,
                search_mode=search_mode_enum,
                hybrid_alpha=hybrid_alpha,
            )

            # Format results
            if not results:
                return CallToolResult(
                    content=[
                        TextContent(
                            type="text", text=f"No similar code found for {file_path}"
                        )
                    ]
                )

            response_text = self._format_similar_results(results, file_path)
            return CallToolResult(
                content=[TextContent(type="text", text=response_text)]
            )

        except Exception as e:
            return CallToolResult(
                content=[
                    TextContent(type="text", text=f"Similar search failed: {str(e)}")
                ],
                isError=True,
            )

    async def handle_search_context(self, args: dict[str, Any]) -> CallToolResult:
        """Handle search_context tool call.

        Args:
            args: Tool call arguments containing description, focus_areas, etc.

        Returns:
            CallToolResult with contextual search results or error
        """
        description = args.get("description", "")
        focus_areas = args.get("focus_areas")
        limit = args.get("limit", 10)
        search_mode = args.get("search_mode", "hybrid")
        hybrid_alpha = float(args.get("hybrid_alpha", 0.7))

        if not description:
            return CallToolResult(
                content=[
                    TextContent(type="text", text="description parameter is required")
                ],
                isError=True,
            )

        try:
            # Convert search_mode string to enum
            from ..core.search import SearchMode

            try:
                search_mode_enum = SearchMode(search_mode.lower())
            except ValueError:
                search_mode_enum = SearchMode.HYBRID

            # Perform context search
            results = await self.search_engine.search_by_context(
                context_description=description,
                focus_areas=focus_areas,
                limit=limit,
                search_mode=search_mode_enum,
                hybrid_alpha=hybrid_alpha,
            )

            # Format results
            if not results:
                return CallToolResult(
                    content=[
                        TextContent(
                            type="text",
                            text=f"No contextually relevant code found for: {description}",
                        )
                    ]
                )

            response_text = self._format_context_results(
                results, description, focus_areas
            )
            return CallToolResult(
                content=[TextContent(type="text", text=response_text)]
            )

        except Exception as e:
            return CallToolResult(
                content=[
                    TextContent(type="text", text=f"Context search failed: {str(e)}")
                ],
                isError=True,
            )

    def _format_search_results(self, results: list, query: str) -> str:
        """Format search results for display.

        Args:
            results: List of search results
            query: Original search query

        Returns:
            Formatted text response
        """
        if not results:
            return f"No results found for query: '{query}'"

        response_lines = [f"Found {len(results)} results for query: '{query}'\n"]

        for i, result in enumerate(results, 1):
            response_lines.append(
                f"## Result {i} (Score: {result.similarity_score:.3f})"
            )
            response_lines.append(f"**File:** {result.file_path}")
            if result.function_name:
                response_lines.append(f"**Function:** {result.function_name}")
            if result.class_name:
                response_lines.append(f"**Class:** {result.class_name}")
            response_lines.append(f"**Lines:** {result.start_line}-{result.end_line}")
            response_lines.append("**Code:**")
            response_lines.append("```" + (result.language or ""))
            response_lines.append(result.content)
            response_lines.append("```\n")

        return "\n".join(response_lines)

    def _format_similar_results(self, results: list, file_path: str) -> str:
        """Format similar code results for display.

        Args:
            results: List of similar code results
            file_path: Original file path

        Returns:
            Formatted text response
        """
        response_lines = [
            f"Found {len(results)} similar code snippets for {file_path}\n"
        ]

        for i, result in enumerate(results, 1):
            response_lines.append(
                f"## Result {i} (Score: {result.similarity_score:.3f})"
            )
            response_lines.append(f"**File:** {result.file_path}")
            if result.function_name:
                response_lines.append(f"**Function:** {result.function_name}")
            if result.class_name:
                response_lines.append(f"**Class:** {result.class_name}")
            response_lines.append(f"**Lines:** {result.start_line}-{result.end_line}")
            response_lines.append("**Code:**")
            response_lines.append("```" + (result.language or ""))
            # Show more of the content for similar search
            content_preview = (
                result.content[:500] if len(result.content) > 500 else result.content
            )
            response_lines.append(
                content_preview + ("..." if len(result.content) > 500 else "")
            )
            response_lines.append("```\n")

        return "\n".join(response_lines)

    def _format_context_results(
        self, results: list, description: str, focus_areas: list[str] | None
    ) -> str:
        """Format contextual search results for display.

        Args:
            results: List of contextual search results
            description: Original context description
            focus_areas: Optional focus areas

        Returns:
            Formatted text response
        """
        response_lines = [f"Found {len(results)} contextually relevant code snippets"]
        if focus_areas:
            response_lines[0] += f" (focus: {', '.join(focus_areas)})"
        response_lines[0] += f" for: {description}\n"

        for i, result in enumerate(results, 1):
            response_lines.append(
                f"## Result {i} (Score: {result.similarity_score:.3f})"
            )
            response_lines.append(f"**File:** {result.file_path}")
            if result.function_name:
                response_lines.append(f"**Function:** {result.function_name}")
            if result.class_name:
                response_lines.append(f"**Class:** {result.class_name}")
            response_lines.append(f"**Lines:** {result.start_line}-{result.end_line}")
            response_lines.append("**Code:**")
            response_lines.append("```" + (result.language or ""))
            # Show more of the content for context search
            content_preview = (
                result.content[:500] if len(result.content) > 500 else result.content
            )
            response_lines.append(
                content_preview + ("..." if len(result.content) > 500 else "")
            )
            response_lines.append("```\n")

        return "\n".join(response_lines)
