"""
Parser for OpenCode CLI JSON event output.

OpenCode CLI with --format json outputs JSONL with events like:
- {"type": "text", "timestamp": ..., "sessionID": ..., "part": {"type": "text", "text": "...", "time": {...}}}
- {"type": "tool_use", "timestamp": ..., "sessionID": ..., "part": {"type": "tool", "tool": "bash", "state": {...}}}
- {"type": "error", "timestamp": ..., "sessionID": ..., "error": {...}}
- {"type": "step_start", "timestamp": ..., "sessionID": ..., "part": {...}}
- {"type": "step_finish", "timestamp": ..., "sessionID": ..., "part": {...}}
"""

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

if TYPE_CHECKING:
    from litellm.utils import ModelResponse


# OpenCode tool name mapping (for notifications)
OPENCODE_TOOL_NAMES = {
    "bash": "Bash",
    "edit": "Edit",
    "write": "Write",
    "read": "Read",
    "glob": "Glob",
    "grep": "Grep",
    "list": "List",
    "todowrite": "TodoWrite",
    "todoread": "TodoRead",
    "websearch": "WebSearch",
}


def _normalize_opencode_event_to_openai_format(event: Dict[str, Any], *, default_model: str = "opencode") -> Dict[str, Any]:
    """
    Normalize OpenCode JSON event to standard OpenAI API response format.

    Args:
        event: OpenCode JSON event dict
        default_model: Default model name to use

    Returns:
        Dict in OpenAI chat completion format
    """
    event_type = event.get("type")
    event_id = f"msg_{int(time.time())}"
    session_id = event.get("sessionID", "")

    if event_type == "text":
        # Handle text message event
        # {"type": "text", "part": {"type": "text", "text": "...", "time": {"start": ..., "end": ...}}}
        part = event.get("part", {})
        text_content = part.get("text", "")

        return {
            "id": event_id,
            "object": "chat.completion",
            "created": int(time.time()),
            "model": default_model,
            "choices": [{
                "index": 0,
                "message": {
                    "role": "assistant",
                    "content": text_content
                },
                "finish_reason": "stop"
            }],
            "usage": {},
            "_opencode_session_id": session_id,
        }

    elif event_type == "tool_use":
        # Handle tool use event
        # {"type": "tool_use", "part": {"type": "tool", "tool": "bash", "state": {"status": "completed", "input": {...}, "output": "..."}}}
        part = event.get("part", {})
        tool_name = part.get("tool", "unknown")
        state = part.get("state", {})
        tool_input = state.get("input", {})

        # Serialize input as JSON string for tool_calls format
        arguments_str = json.dumps(tool_input) if isinstance(tool_input, dict) else str(tool_input)

        return {
            "id": event_id,
            "object": "chat.completion",
            "created": int(time.time()),
            "model": default_model,
            "choices": [{
                "index": 0,
                "message": {
                    "role": "assistant",
                    "content": None,
                    "tool_calls": [{
                        "id": f"call_{event_id}",
                        "type": "function",
                        "function": {
                            "name": tool_name,
                            "arguments": arguments_str
                        }
                    }]
                },
                "finish_reason": "tool_calls"
            }],
            "usage": {},
            "_opencode_session_id": session_id,
            "_opencode_tool_state": state,
        }

    elif event_type == "error":
        # Handle error event
        # {"type": "error", "error": {"name": "...", "message": "...", "data": {...}}}
        error = event.get("error", {})
        error_message = error.get("message", str(error.get("name", "Unknown error")))
        if "data" in error and isinstance(error["data"], dict):
            error_message = error["data"].get("message", error_message)

        return {
            "id": event_id,
            "object": "chat.completion",
            "created": int(time.time()),
            "model": default_model,
            "choices": [{
                "index": 0,
                "message": {
                    "role": "assistant",
                    "content": f"Error: {error_message}"
                },
                "finish_reason": "stop"
            }],
            "usage": {},
            "_opencode_session_id": session_id,
            "_opencode_error": error,
        }

    elif event_type in ("step_start", "step_finish"):
        # Handle step events - these are mainly for tracking, convert to assistant message
        part = event.get("part", {})
        step_type = part.get("type", event_type)

        return {
            "id": event_id,
            "object": "chat.completion",
            "created": int(time.time()),
            "model": default_model,
            "choices": [{
                "index": 0,
                "message": {
                    "role": "assistant",
                    "content": None  # No content for step events
                },
                "finish_reason": "stop"
            }],
            "usage": {},
            "_opencode_session_id": session_id,
            "_opencode_step_type": step_type,
        }

    else:
        # Unknown event type - pass through as generic message
        raise ValueError(f"Unsupported OpenCode event type: {event_type}")


def to_model_response(event: Dict[str, Any], *, default_model: str = "opencode") -> "ModelResponse":
    """
    Convert OpenCode JSON event to LiteLLM ModelResponse.

    Args:
        event: OpenCode JSON event dict (parsed from JSONL line)
        default_model: Default model name to use

    Returns:
        LiteLLM ModelResponse object
    """
    from litellm.utils import ModelResponse, convert_to_model_response_object

    # Normalize to OpenAI format
    normalized_response = _normalize_opencode_event_to_openai_format(event, default_model=default_model)

    # Create base ModelResponse
    model_response = ModelResponse()
    model_response.model = normalized_response.get("model", default_model)

    # Determine if this is a final message (text event with completed time)
    is_final_message = False
    event_type = event.get("type")
    if event_type == "text":
        part = event.get("part", {})
        time_info = part.get("time", {})
        is_final_message = time_info.get("end") is not None

    # Set provider-specific fields
    hidden_params = {
        "original_response": event,
        BLOCKS_PROVIDER_SPECIFIC_FIELDS__IS_FINAL_MESSAGE: is_final_message
    }

    # Use litellm utility to create response
    final_response = convert_to_model_response_object(
        response_object=normalized_response,
        model_response_object=model_response,
        response_type="completion",
        hidden_params=hidden_params
    )

    # Ensure provider-specific fields are set on the message
    if final_response.choices and final_response.choices[0].message:
        if not final_response.choices[0].message.provider_specific_fields:
            final_response.choices[0].message.provider_specific_fields = {}
        final_response.choices[0].message.provider_specific_fields[BLOCKS_PROVIDER_SPECIFIC_FIELDS__IS_FINAL_MESSAGE] = is_final_message

    return final_response


def extract_session_id(event: Dict[str, Any]) -> Optional[str]:
    """Extract session ID from an OpenCode event."""
    return event.get("sessionID")


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

    Returns:
        Dict with tool_name, input, output, status if event is tool_use, else None
    """
    if event.get("type") != "tool_use":
        return None

    part = event.get("part", {})
    state = part.get("state", {})

    return {
        "tool_name": part.get("tool", "unknown"),
        "input": state.get("input", {}),
        "output": state.get("output"),
        "status": state.get("status"),
        "title": state.get("title"),
    }


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


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


def is_error_event(event: Dict[str, Any]) -> bool:
    """Check if event is an error event."""
    return event.get("type") == "error"


def get_text_content(event: Dict[str, Any]) -> Optional[str]:
    """Extract text content from a text event."""
    if not is_text_event(event):
        return None
    part = event.get("part", {})
    return part.get("text")


def get_error_message(event: Dict[str, Any]) -> Optional[str]:
    """Extract error message from an error event."""
    if not is_error_event(event):
        return None
    error = event.get("error", {})
    message = error.get("message", str(error.get("name", "Unknown error")))
    if "data" in error and isinstance(error["data"], dict):
        message = error["data"].get("message", message)
    return message
