"""Event parsing utilities for WebSocket agent events.

This module provides functions to parse SessionEvent records from the database
and convert them into dashboard-ready event formats for real-time broadcasting.

Ported from TypeScript in sources/pixel-agents/server/openclawParser.ts
"""

from __future__ import annotations

import json
import re
import asyncio
from typing import Any


def format_tool_status(tool_name: str, args: dict[str, Any]) -> str:
    """Format a tool call into a human-readable status string.
    
    Ported from TypeScript formatToolStatus().
    
    Args:
        tool_name: The tool name (e.g., "Read", "Edit", "exec")
        args: Tool arguments dictionary
        
    Returns:
        Human-readable status string
    """
    if tool_name in ("Read", "read"):
        file_path = args.get("file_path") or args.get("path")
        if file_path:
            return f"Reading {file_path}"
        return "Reading"
    
    if tool_name in ("Edit", "edit"):
        file_path = args.get("file_path") or args.get("path")
        if file_path:
            return f"Editing {file_path}"
        return "Editing"
    
    if tool_name in ("Write", "write"):
        file_path = args.get("file_path") or args.get("path")
        if file_path:
            return f"Writing {file_path}"
        return "Writing"
    
    if tool_name == "exec":
        return "Running command"
    
    if tool_name == "web_fetch":
        url = args.get("url", "")
        if isinstance(url, str):
            url = url[:60] if len(url) > 60 else url
        return f"Fetching {url}"
    
    if tool_name == "browser":
        return "Using browser"
    
    if tool_name == "sessions_spawn":
        return "Spawning sub-agent"
    
    if tool_name == "memory_search":
        return "Searching memory"
    
    if tool_name == "memory_get":
        return "Reading memory"
    
    if tool_name == "message":
        return "Sending message"
    
    if tool_name == "image":
        return "Analyzing image"
    
    if tool_name == "pdf":
        return "Analyzing PDF"
    
    if tool_name == "tts":
        return "Speaking"
    
    return f"Using {tool_name}"


def extract_task_from_message(content: Any) -> str:
    """Extract a meaningful task description from a user message.
    
    Ported from TypeScript extractTaskFromUserMessage().
    OpenClaw wraps user messages in metadata - we strip that to find actual content.
    
    Args:
        content: The message content (string or array of blocks)
        
    Returns:
        Cleaned task text, or empty string if no content found
    """
    text = ""
    
    if isinstance(content, str):
        text = content
    elif isinstance(content, list):
        for block in content:
            if isinstance(block, dict) and block.get("type") == "text":
                if isinstance(block.get("text"), str):
                    text = block["text"]
                    break
    
    if not text:
        return ""
    
    # Strip OpenClaw conversation metadata wrapper
    cleaned = text
    
    # Strip inter-session message headers
    inter_session_match = re.match(
        r"\[Inter-session message\].*?\n.*?\n([\s\S]*)",
        cleaned,
        re.MULTILINE
    )
    if inter_session_match:
        cleaned = inter_session_match.group(1).strip()
    
    # Strip "Conversation info" metadata block
    conv_match = re.match(
        r"Conversation info \(untrusted.*?\n```json\n[\s\S]*?```\n([\s\S]*)",
        cleaned,
        re.MULTILINE
    )
    if conv_match:
        cleaned = conv_match.group(1).strip()
    
    # Strip "Sender" metadata block
    sender_match = re.match(
        r"Sender \(untrusted.*?\n```json\n[\s\S]*?```\n([\s\S]*)",
        cleaned,
        re.MULTILINE
    )
    if sender_match:
        cleaned = sender_match.group(1).strip()
    
    # Strip chat history block
    hist_match = re.match(
        r"Chat history.*?\n```json\n[\s\S]*?```\n([\s\S]*)",
        cleaned,
        re.MULTILINE
    )
    if hist_match:
        cleaned = hist_match.group(1).strip()
    
    # Strip @mentions at the start
    cleaned = re.sub(r"^@\S+\s*", "", cleaned).strip()
    
    # Strip untrusted context blocks
    cleaned = re.sub(r"Untrusted context \(metadata[^)]*\):\s*", "", cleaned).strip()
    cleaned = re.sub(
        r"<<<EXTERNAL_UNTRUSTED_CONTENT[\s\S]*?<<<END_EXTERNAL_UNTRUSTED_CONTENT[^>]*>>>",
        "",
        cleaned
    ).strip()
    
    # If it's a sessions_send payload, try to get the message body
    try:
        parsed = json.loads(cleaned)
        if isinstance(parsed, dict) and (parsed.get("messages") or parsed.get("message")):
            cleaned = parsed.get("message", "")
            if isinstance(cleaned, list):
                cleaned = ""
            elif isinstance(cleaned, dict):
                cleaned = ""
    except json.JSONDecodeError:
        pass
    
    # Truncate to something reasonable
    if len(cleaned) > 150:
        cleaned = cleaned[:147] + "..."
    
    return cleaned


def parse_session_event(event: Any) -> list[dict[str, Any]]:
    """Parse a SessionEvent model instance into dashboard events.
    
    Ported from TypeScript parseOpenClawLine().
    Takes a SessionEvent (SQLModel instance) and returns a list of dashboard events.
    
    Args:
        event: SessionEvent model instance with fields:
            - event_type: "message", "session", "custom", etc.
            - metadata_: JSON dict containing the full record data
            
    Returns:
        List of dashboard event dicts with type and relevant fields
    """
    events = []
    
    try:
        # Parse metadata as the record
        metadata = event.metadata_ or {}
        record = metadata if isinstance(metadata, dict) else {}
        
        if not record:
            return events
        
        if record.get("type") == "message":
            msg = record.get("message") or {}
            
            if isinstance(msg, dict):
                role = msg.get("role")
                content = msg.get("content")
                
                if role == "assistant" and isinstance(content, list):
                    has_tool_call = False
                    chat_text = ""
                    
                    for block in content:
                        if not isinstance(block, dict):
                            continue
                        block_type = block.get("type")
                        
                        if block_type == "text" and isinstance(block.get("text"), str):
                            chat_text = block["text"].strip()
                            chat_text = re.sub(r"^\[\[reply_to[^\]]*\]\]\s*", "", chat_text).strip()
                            
                        if block_type == "toolCall" and block.get("id"):
                            has_tool_call = True
                            tool_id = block["id"]
                            tool_name = block.get("name", "unknown")
                            args = block.get("arguments", {}) or {}
                            status = format_tool_status(tool_name, args)
                            
                            events.append({
                                "type": "agentToolStart",
                                "id": event.agent_id or "unknown",
                                "toolId": tool_id,
                                "status": status,
                            })
                            
                            # Detect sub-agent spawns
                            if tool_name == "sessions_spawn":
                                label = args.get("label") or args.get("agentId") or "sub-agent"
                                task = args.get("task", "")
                                events.append({
                                    "type": "subagentSpawned",
                                    "id": event.agent_id or "unknown",
                                    "toolId": tool_id,
                                    "label": label,
                                    "task": task[:200] if task else "",
                                })
                        
                        if block_type == "toolResult" and block.get("toolCallId"):
                            tool_id = block["toolCallId"]
                            events.append({
                                "type": "agentToolDone",
                                "id": event.agent_id or "unknown",
                                "toolId": tool_id,
                            })
                    
                    if chat_text and chat_text not in ("NO_REPLY", "HEARTBEAT_OK", "ANNOUNCE_SKIP", "REPLY_SKIP"):
                        events.append({
                            "type": "agentChat",
                            "id": event.agent_id or "unknown",
                            "message": chat_text[:2000] if len(chat_text) > 2000 else chat_text,
                        })
                    
                    if has_tool_call:
                        events.append({
                            "type": "agentStatus",
                            "id": event.agent_id or "unknown",
                            "status": "active",
                        })
                
                elif role == "toolResult" and isinstance(content, dict):
                    # Tool completed
                    tool_id = content.get("toolCallId")
                    if tool_id:
                        events.append({
                            "type": "agentToolDone",
                            "id": event.agent_id or "unknown",
                            "toolId": tool_id,
                        })
                
                elif role == "user":
                    # New user message - new turn starting
                    task_text = extract_task_from_message(content) if content else ""
                    if task_text:
                        events.append({
                            "type": "agentTask",
                            "id": event.agent_id or "unknown",
                            "task": task_text,
                        })
                    events.append({
                        "type": "agentToolsClear",
                        "id": event.agent_id or "unknown",
                    })
                    events.append({
                        "type": "agentStatus",
                        "id": event.agent_id or "unknown",
                        "status": "active",
                    })
        
        elif record.get("type") == "session":
            # Session start record
            events.append({
                "type": "sessionStart",
                "id": event.agent_id or "unknown",
            })
            events.append({
                "type": "agentStatus",
                "id": event.agent_id or "unknown",
                "status": "active",
            })
        
        elif record.get("type") == "custom":
            # Handle custom events if needed
            pass
    
    except Exception:
        # Ignore malformed events
        pass
    
    return events
