"""Cross-references mixin for MCGhidra.

Provides tools for cross-reference (xref) operations.
"""

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 XrefsMixin(MCGhidraMixinBase):
    """Mixin for cross-reference operations.

    Provides tools for:
    - Listing references to an address
    - Listing references from an address
    - Filtering by reference type
    """

    @mcp_tool()
    def xrefs_list(
        self,
        to_addr: Optional[str] = None,
        from_addr: Optional[str] = None,
        type: Optional[str] = None,
        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 cross-references with filtering and pagination.

        Args:
            to_addr: Filter references to this address (hex)
            from_addr: Filter references from this address (hex)
            type: Filter by reference type ("CALL", "READ", "WRITE", etc.)
            port: Ghidra instance port (optional)
            page_size: Items per page (default: 50, max: 500)
            grep: Regex pattern to filter results
            grep_ignorecase: Case-insensitive grep (default: True)
            return_all: Return all results without pagination
            fields: Field names to keep (e.g. ['fromAddress', 'toAddress']). Reduces response size.
            ctx: FastMCP context (auto-injected)

        Returns:
            Cross-references with pagination metadata
        """
        if not to_addr and not from_addr:
            return {
                "success": False,
                "error": {
                    "code": "MISSING_PARAMETER",
                    "message": "Either to_addr or from_addr parameter is required",
                },
            }

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

        config = get_config()

        params = {"offset": 0, "limit": 10000}
        if to_addr:
            params["to_addr"] = to_addr
        if from_addr:
            params["from_addr"] = from_addr
        if type:
            params["type"] = type

        response = self.safe_get(port, "xrefs", params)
        simplified = self.simplify_response(response)

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

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

        query_params = {
            "tool": "xrefs_list",
            "port": port,
            "to_addr": to_addr,
            "from_addr": from_addr,
            "type": type,
            "grep": grep,
        }
        session_id = self._get_session_id(ctx)

        return self.filtered_paginate(
            data=all_xrefs,
            query_params=query_params,
            tool_name="xrefs_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,
        )

    # Resources

    @mcp_resource(uri="ghidra://instance/{port}/xrefs/to/{address}")
    def resource_xrefs_to(
        self,
        port: Optional[int] = None,
        address: Optional[str] = None,
    ) -> Dict[str, Any]:
        """MCP Resource: Get references to an address (capped).

        Args:
            port: Ghidra instance port
            address: Target address

        Returns:
            References to the address (capped at 1000)
        """
        if not address:
            return {"error": "Address is required"}

        try:
            port = self.get_instance_port(port)
        except ValueError as e:
            return {"error": str(e)}

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

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

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

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

        return {
            "address": address,
            "xrefs_to": xrefs[:cap],
            "count": len(xrefs),
            "capped_at": cap if len(xrefs) >= cap else None,
            "_hint": "Use xrefs_list(to_addr=...) for full pagination" if len(xrefs) >= cap else None,
        }

    @mcp_resource(uri="ghidra://instance/{port}/xrefs/from/{address}")
    def resource_xrefs_from(
        self,
        port: Optional[int] = None,
        address: Optional[str] = None,
    ) -> Dict[str, Any]:
        """MCP Resource: Get references from an address (capped).

        Args:
            port: Ghidra instance port
            address: Source address

        Returns:
            References from the address (capped at 1000)
        """
        if not address:
            return {"error": "Address is required"}

        try:
            port = self.get_instance_port(port)
        except ValueError as e:
            return {"error": str(e)}

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

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

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

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

        return {
            "address": address,
            "xrefs_from": xrefs[:cap],
            "count": len(xrefs),
            "capped_at": cap if len(xrefs) >= cap else None,
            "_hint": "Use xrefs_list(from_addr=...) for full pagination" if len(xrefs) >= cap else None,
        }
