import json
import time
from typing import Any, Dict, Optional, TYPE_CHECKING, List
from blocks_control_sdk.parsers.base_messages import BLOCKS_PROVIDER_SPECIFIC_FIELDS__IS_FINAL_MESSAGE
from blocks_control_sdk.utils import to_pascal_case

if TYPE_CHECKING:
    from litellm.utils import ModelResponse

# Cursor tool name constants
CURSOR_TOOLS__READ = "Read"
CURSOR_TOOLS__WRITE = "Write"
CURSOR_TOOLS__EDIT = "Edit"
CURSOR_TOOLS__BASH = "Bash"
CURSOR_TOOLS__GREP = "Grep"
CURSOR_TOOLS__GLOB = "Glob"
CURSOR_TOOLS__TODOWRITE = "TodoWrite"

# Tool type key to normalized name mapping
CURSOR_TOOL_TYPE_MAPPINGS: Dict[str, str] = {
    "readToolCall": CURSOR_TOOLS__READ,
    "writeToolCall": CURSOR_TOOLS__WRITE,
    "editToolCall": CURSOR_TOOLS__EDIT,
    "bashToolCall": CURSOR_TOOLS__BASH,
    "shellToolCall": "Shell",
    "grepToolCall": CURSOR_TOOLS__GREP,
    "globToolCall": CURSOR_TOOLS__GLOB,
    "listFilesToolCall": "ListFiles",
    "searchFilesToolCall": "SearchFiles",
}


def is_tool_event(blob: Dict[str, Any]) -> bool:
    """Check if event is a tool_call event."""
    return blob.get("type") == "tool_call"


def is_tool_started(blob: Dict[str, Any]) -> bool:
    """Check if event is a tool_call started event."""
    return blob.get("type") == "tool_call" and blob.get("subtype") == "started"


def extract_tool_info(blob: Dict[str, Any]) -> Optional[Dict[str, Any]]:
    """
    Extract tool information from a tool_call event.

    Event format examples:
        {"type":"tool_call","subtype":"started","call_id":"...","tool_call":{"readToolCall":{"args":{...}}}}
        {"type":"tool_call","subtype":"started","call_id":"...","tool_call":{"mcpToolCall":{"toolName":"...","args":{...}}}}

    Returns:
        {"tool_name": str, "args": dict, "call_id": str, "provider": str (optional)} or None
    """
    if blob.get("type") != "tool_call":
        return None

    def filter_arg_values(args: dict, extra_unwanted_keys: List[str] = []) -> dict:
        unwanted_keys = ["hasInputRedirect", "hasOutputRedirect", "hasRedirects", "hasCommandSubstitution", "fileOutputThresholdBytes", "timeoutBehavior", "parsingResult", "simpleCommands", *extra_unwanted_keys]
        for key in unwanted_keys:
            if key in args:
                del args[key]
        return args

    tool_call_data = blob.get("tool_call", {})
    call_id = blob.get("call_id", "")

    # Format 1: Standard function call format (from assistant message tool_calls)
    if "function" in tool_call_data:
        try:
            args = json.loads(tool_call_data["function"].get("arguments", "{}"))
        except json.JSONDecodeError:
            args = {}
        return {
            "tool_name": tool_call_data["function"].get("name", "Unknown"),
            "args": filter_arg_values(args),
            "call_id": call_id
        }

    # Format 2: Cursor-specific *ToolCall formats
    for key, tool_name in CURSOR_TOOL_TYPE_MAPPINGS.items():
        if key in tool_call_data:
            return {
                "tool_name": tool_name,
                "args": filter_arg_values(tool_call_data[key].get("args", {})),
                "call_id": call_id
            }

    # Format 3: MCP tool calls
    if "mcpToolCall" in tool_call_data:
        mcp_data = tool_call_data["mcpToolCall"]
        args = mcp_data.get("args", {})
        name = args.get("name").replace("blocks-internal-mcp-", "")
        return {
            "tool_name": to_pascal_case(name),
            "args": filter_arg_values(args, ["name", "providerIdentifier", "toolName", "toolCallId"]),
            "call_id": call_id,
            "provider": mcp_data.get("providerIdentifier")
        }

    # Fallback: unknown *ToolCall pattern
    for key in tool_call_data.keys():
        if key.endswith("ToolCall"):
            return {
                "tool_name": key.replace("ToolCall", ""),
                "args": filter_arg_values(tool_call_data[key].get("args", {})),
                "call_id": call_id
            }

    return None


def to_model_response(blob: Dict[str, Any], *, default_model: str = "cursor-model") -> "ModelResponse":
    """
    Convert Cursor CLI stream-json events to LiteLLM ModelResponse format.
    
    Cursor emits events in NDJSON format with types:
    - "assistant": Contains message with content array
    - "result": Terminal result event
    - "tool_call": Tool invocation event (handled separately)
    """
    from litellm.utils import ModelResponse
    from litellm.types.utils import Message
    
    blocks_provider_specific_fields = {
        BLOCKS_PROVIDER_SPECIFIC_FIELDS__IS_FINAL_MESSAGE: False
    }
    
    is_final = blob.get("type") == "result"
    
    # Handle different event types
    if blob.get("type") == "result":
        # Terminal result event
        content = blob.get("result", "")
        blocks_provider_specific_fields[BLOCKS_PROVIDER_SPECIFIC_FIELDS__IS_FINAL_MESSAGE] = True
        
    elif blob.get("type") == "assistant":
        # Assistant message event
        message_data = blob.get("message", {})
        content_list = message_data.get("content", [])
        
        # Extract text content from content array
        content = ""
        for item in content_list:
            if isinstance(item, dict) and item.get("type") == "text":
                content += item.get("text", "")
            elif isinstance(item, str):
                content += item
                
    else:
        # Unknown event type - return empty response
        content = ""
    
    # Build ModelResponse
    mr = ModelResponse()
    mr.choices[0].message = Message(
        role="assistant",
        content=content or None,
        provider_specific_fields=blocks_provider_specific_fields
    )
    mr.choices[0].finish_reason = "stop"
    mr.model = blob.get("model", default_model)
    mr.created = int(time.time())
    mr.usage = blob.get("usage", {}) or {}
    
    return mr
