"""Symbols mixin for MCGhidra.

Provides tools for symbol table operations including labels, imports, and exports.
"""

from typing import Any, Dict, List, Optional

from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool

from ..config import get_config
from .base import MCGhidraMixinBase


class SymbolsMixin(MCGhidraMixinBase):
    """Mixin for symbol table operations.

    Provides tools for:
    - Listing all symbols with pagination
    - Querying imported symbols (external references)
    - Querying exported symbols (entry points)
    """

    @mcp_tool()
    def symbols_list(
        self,
        port: Optional[int] = None,
        page_size: int = 50,
        grep: Optional[str] = None,
        grep_ignorecase: bool = True,
        return_all: bool = False,
        fields: Optional[List[str]] = None,
        ctx: Optional[Context] = None,
    ) -> Dict[str, Any]:
        """List symbols with cursor-based pagination.

        Args:
            port: Ghidra instance port (optional)
            page_size: Symbols per page (default: 50, max: 500)
            grep: Regex pattern to filter symbol names
            grep_ignorecase: Case-insensitive grep (default: True)
            return_all: Return all symbols without pagination
            fields: Field names to keep (e.g. ['name', 'address']). Reduces response size.
            ctx: FastMCP context (auto-injected)

        Returns:
            Paginated list of symbols
        """
        try:
            port = self.get_instance_port(port)
        except ValueError as e:
            return {"success": False, "error": {"code": "NO_INSTANCE", "message": str(e)}}

        config = get_config()
        cap = config.resource_caps.get("symbols", 1000)
        response = self.safe_get(port, "symbols", {"limit": cap})
        simplified = self.simplify_response(response)

        if not simplified.get("success", True):
            return simplified

        symbols = simplified.get("result", [])
        if not isinstance(symbols, list):
            symbols = []

        query_params = {"tool": "symbols_list", "port": port, "grep": grep}
        session_id = self._get_session_id(ctx)

        return self.filtered_paginate(
            data=symbols,
            query_params=query_params,
            tool_name="symbols_list",
            session_id=session_id,
            page_size=min(page_size, config.max_page_size),
            grep=grep,
            grep_ignorecase=grep_ignorecase,
            return_all=return_all,
            fields=fields,
        )

    @mcp_tool()
    def symbols_imports(
        self,
        port: Optional[int] = None,
        page_size: int = 50,
        grep: Optional[str] = None,
        grep_ignorecase: bool = True,
        return_all: bool = False,
        fields: Optional[List[str]] = None,
        ctx: Optional[Context] = None,
    ) -> Dict[str, Any]:
        """List imported symbols (external references) with pagination.

        Args:
            port: Ghidra instance port (optional)
            page_size: Imports per page (default: 50, max: 500)
            grep: Regex pattern to filter import names
            grep_ignorecase: Case-insensitive grep (default: True)
            return_all: Return all imports without pagination
            fields: Field names to keep. Reduces response size.
            ctx: FastMCP context (auto-injected)

        Returns:
            Paginated list of imported symbols
        """
        try:
            port = self.get_instance_port(port)
        except ValueError as e:
            return {"success": False, "error": {"code": "NO_INSTANCE", "message": str(e)}}

        config = get_config()
        cap = config.resource_caps.get("symbols", 1000)
        response = self.safe_get(port, "symbols/imports", {"limit": cap})
        simplified = self.simplify_response(response)

        if not simplified.get("success", True):
            return simplified

        imports = simplified.get("result", [])
        if not isinstance(imports, list):
            imports = []

        query_params = {"tool": "symbols_imports", "port": port, "grep": grep}
        session_id = self._get_session_id(ctx)

        return self.filtered_paginate(
            data=imports,
            query_params=query_params,
            tool_name="symbols_imports",
            session_id=session_id,
            page_size=min(page_size, config.max_page_size),
            grep=grep,
            grep_ignorecase=grep_ignorecase,
            return_all=return_all,
            fields=fields,
        )

    @mcp_tool()
    def symbols_exports(
        self,
        port: Optional[int] = None,
        page_size: int = 50,
        grep: Optional[str] = None,
        grep_ignorecase: bool = True,
        return_all: bool = False,
        fields: Optional[List[str]] = None,
        ctx: Optional[Context] = None,
    ) -> Dict[str, Any]:
        """List exported symbols (entry points) with pagination.

        Args:
            port: Ghidra instance port (optional)
            page_size: Exports per page (default: 50, max: 500)
            grep: Regex pattern to filter export names
            grep_ignorecase: Case-insensitive grep (default: True)
            return_all: Return all exports without pagination
            fields: Field names to keep. Reduces response size.
            ctx: FastMCP context (auto-injected)

        Returns:
            Paginated list of exported symbols
        """
        try:
            port = self.get_instance_port(port)
        except ValueError as e:
            return {"success": False, "error": {"code": "NO_INSTANCE", "message": str(e)}}

        config = get_config()
        cap = config.resource_caps.get("symbols", 1000)
        response = self.safe_get(port, "symbols/exports", {"limit": cap})
        simplified = self.simplify_response(response)

        if not simplified.get("success", True):
            return simplified

        exports = simplified.get("result", [])
        if not isinstance(exports, list):
            exports = []

        query_params = {"tool": "symbols_exports", "port": port, "grep": grep}
        session_id = self._get_session_id(ctx)

        return self.filtered_paginate(
            data=exports,
            query_params=query_params,
            tool_name="symbols_exports",
            session_id=session_id,
            page_size=min(page_size, config.max_page_size),
            grep=grep,
            grep_ignorecase=grep_ignorecase,
            return_all=return_all,
            fields=fields,
        )

    @mcp_tool()
    def symbols_create(
        self,
        name: str,
        address: str,
        port: Optional[int] = None,
    ) -> Dict[str, Any]:
        """Create a new label/symbol at the specified address.

        Args:
            name: Name for the new symbol
            address: Memory address in hex format
            port: Ghidra instance port (optional)

        Returns:
            Created symbol information
        """
        if not name:
            return {
                "success": False,
                "error": {
                    "code": "MISSING_PARAMETER",
                    "message": "name parameter is required",
                },
            }
        if not address:
            return {
                "success": False,
                "error": {
                    "code": "MISSING_PARAMETER",
                    "message": "address parameter is required",
                },
            }

        try:
            port = self.get_instance_port(port)
        except ValueError as e:
            return {"success": False, "error": {"code": "NO_INSTANCE", "message": str(e)}}

        payload = {"name": name, "address": address}
        response = self.safe_post(port, "symbols", payload)
        return self.simplify_response(response)

    @mcp_tool()
    def symbols_rename(
        self,
        address: str,
        new_name: str,
        port: Optional[int] = None,
    ) -> Dict[str, Any]:
        """Rename the primary symbol at the specified address.

        Args:
            address: Memory address of the symbol in hex format
            new_name: New name for the symbol
            port: Ghidra instance port (optional)

        Returns:
            Operation result with old and new names
        """
        if not address:
            return {
                "success": False,
                "error": {
                    "code": "MISSING_PARAMETER",
                    "message": "address parameter is required",
                },
            }
        if not new_name:
            return {
                "success": False,
                "error": {
                    "code": "MISSING_PARAMETER",
                    "message": "new_name parameter is required",
                },
            }

        try:
            port = self.get_instance_port(port)
        except ValueError as e:
            return {"success": False, "error": {"code": "NO_INSTANCE", "message": str(e)}}

        payload = {"name": new_name}
        response = self.safe_patch(port, f"symbols/{address}", payload)
        return self.simplify_response(response)

    @mcp_tool()
    def symbols_delete(
        self,
        address: str,
        port: Optional[int] = None,
    ) -> Dict[str, Any]:
        """Delete the primary symbol at the specified address.

        Args:
            address: Memory address of the symbol in hex format
            port: Ghidra instance port (optional)

        Returns:
            Operation result
        """
        if not address:
            return {
                "success": False,
                "error": {
                    "code": "MISSING_PARAMETER",
                    "message": "address parameter is required",
                },
            }

        try:
            port = self.get_instance_port(port)
        except ValueError as e:
            return {"success": False, "error": {"code": "NO_INSTANCE", "message": str(e)}}

        response = self.safe_delete(port, f"symbols/{address}")
        return self.simplify_response(response)

    # Resources

    @mcp_resource(uri="ghidra://instance/{port}/symbols")
    def resource_symbols_list(self, port: Optional[int] = None) -> Dict[str, Any]:
        """MCP Resource: List symbols (capped).

        Args:
            port: Ghidra instance port

        Returns:
            List of symbols (capped)
        """
        try:
            port = self.get_instance_port(port)
        except ValueError as e:
            return {"error": str(e)}

        config = get_config()
        cap = config.resource_caps.get("symbols", 1000)

        response = self.safe_get(port, "symbols", {"limit": cap})
        simplified = self.simplify_response(response)

        if not simplified.get("success", True):
            return simplified

        symbols = simplified.get("result", [])
        if not isinstance(symbols, list):
            symbols = []

        return {
            "symbols": symbols[:cap],
            "count": len(symbols),
            "capped_at": cap if len(symbols) >= cap else None,
            "_hint": "Use symbols_list() tool for full pagination" if len(symbols) >= cap else None,
        }

    @mcp_resource(uri="ghidra://instance/{port}/symbols/imports")
    def resource_symbols_imports(self, port: Optional[int] = None) -> Dict[str, Any]:
        """MCP Resource: List imported symbols (capped).

        Args:
            port: Ghidra instance port

        Returns:
            List of imported symbols (capped)
        """
        try:
            port = self.get_instance_port(port)
        except ValueError as e:
            return {"error": str(e)}

        config = get_config()
        cap = config.resource_caps.get("symbols", 1000)

        response = self.safe_get(port, "symbols/imports", {"limit": cap})
        simplified = self.simplify_response(response)

        if not simplified.get("success", True):
            return simplified

        imports = simplified.get("result", [])
        if not isinstance(imports, list):
            imports = []

        return {
            "imports": imports[:cap],
            "count": len(imports),
            "capped_at": cap if len(imports) >= cap else None,
            "_hint": "Use symbols_imports() tool for full pagination"
            if len(imports) >= cap
            else None,
        }

    @mcp_resource(uri="ghidra://instance/{port}/symbols/exports")
    def resource_symbols_exports(self, port: Optional[int] = None) -> Dict[str, Any]:
        """MCP Resource: List exported symbols (capped).

        Args:
            port: Ghidra instance port

        Returns:
            List of exported symbols (capped)
        """
        try:
            port = self.get_instance_port(port)
        except ValueError as e:
            return {"error": str(e)}

        config = get_config()
        cap = config.resource_caps.get("symbols", 1000)

        response = self.safe_get(port, "symbols/exports", {"limit": cap})
        simplified = self.simplify_response(response)

        if not simplified.get("success", True):
            return simplified

        exports = simplified.get("result", [])
        if not isinstance(exports, list):
            exports = []

        return {
            "exports": exports[:cap],
            "count": len(exports),
            "capped_at": cap if len(exports) >= cap else None,
            "_hint": "Use symbols_exports() tool for full pagination"
            if len(exports) >= cap
            else None,
        }
