"""Variables mixin for MCGhidra.

Provides tools for querying global and function-local variables.
"""

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 VariablesMixin(MCGhidraMixinBase):
    """Mixin for variable operations.

    Provides tools for:
    - Listing global and function variables
    - Querying local variables and parameters for a specific function
    """

    @mcp_tool()
    def variables_list(
        self,
        global_only: bool = False,
        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 variables with cursor-based pagination.

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

        Returns:
            Paginated list of variables
        """
        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("variables", 1000)
        params = {"limit": cap}
        if global_only:
            params["global_only"] = "true"

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

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

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

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

        return self.filtered_paginate(
            data=variables,
            query_params=query_params,
            tool_name="variables_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 functions_variables(
        self,
        address: str,
        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 local variables and parameters for a specific function.

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

        Returns:
            Paginated list of function variables
        """
        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)}}

        config = get_config()
        response = self.safe_get(port, f"functions/{address}/variables")
        simplified = self.simplify_response(response)

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

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

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

        return self.filtered_paginate(
            data=variables,
            query_params=query_params,
            tool_name="functions_variables",
            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 variables_rename(
        self,
        function_address: str,
        variable_name: str,
        new_name: str,
        new_type: Optional[str] = None,
        port: Optional[int] = None,
    ) -> Dict[str, Any]:
        """Rename a variable (and optionally retype) within a function.

        Args:
            function_address: Function address in hex format
            variable_name: Current name of the variable
            new_name: New name for the variable
            new_type: New data type (optional, e.g. "int", "char*")
            port: Ghidra instance port (optional)

        Returns:
            Operation result
        """
        if not function_address:
            return {
                "success": False,
                "error": {
                    "code": "MISSING_PARAMETER",
                    "message": "function_address parameter is required",
                },
            }
        if not variable_name:
            return {
                "success": False,
                "error": {
                    "code": "MISSING_PARAMETER",
                    "message": "variable_name 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)}}

        from urllib.parse import quote

        payload: dict = {"name": new_name}
        if new_type:
            payload["data_type"] = new_type

        endpoint = f"functions/{function_address}/variables/{quote(variable_name)}"
        response = self.safe_patch(port, endpoint, payload)
        return self.simplify_response(response)

    # Resources

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

        Args:
            port: Ghidra instance port

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

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

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

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

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

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