"""Shared state management for voice control node."""

from __future__ import annotations

import threading
from collections import deque
from dataclasses import dataclass, field
from typing import Any, Deque, Dict, List, Optional


@dataclass
class RobotStep:
    """A single step in the robot command queue."""

    action: str
    payload: Dict[str, Any]


@dataclass
class VoiceState:
    """Runtime state for voice control."""

    latest_pose: Optional[List[float]] = None
    latest_pose_at: Optional[float] = None
    pending_command: Optional[Dict[str, Any]] = None
    queue: Deque[RobotStep] = field(default_factory=deque)
    held_object_id: Optional[str] = None  # ID of currently held object


@dataclass
class DebugState:
    """Debug information for the web interface."""

    last_voice_input: Optional[str] = None
    last_voice_input_at: Optional[float] = None
    last_parse_result: Optional[Dict[str, Any]] = None
    last_robot_command: Optional[Dict[str, Any]] = None
    last_robot_command_at: Optional[float] = None
    command_history: List[Dict[str, Any]] = field(default_factory=list)
    error_log: List[Dict[str, Any]] = field(default_factory=list)
    latest_image: Optional[bytes] = None
    latest_image_at: Optional[float] = None


class SharedState:
    """Thread-safe shared state container.

    Note: Object state (detected/static objects) has moved to SceneState.
    This class now only handles robot queue, pose, and debug state.
    """

    def __init__(self) -> None:
        self._lock = threading.Lock()
        self._voice_state = VoiceState()
        self._debug_state = DebugState()
        self._command_callback: Optional[Any] = None

    def set_command_callback(self, callback: Any) -> None:
        """Set callback for sending commands from web interface."""
        with self._lock:
            self._command_callback = callback

    def get_command_callback(self) -> Optional[Any]:
        """Get the command callback."""
        with self._lock:
            return self._command_callback

    def get_status(self) -> Dict[str, Any]:
        """Get current status for web interface."""
        with self._lock:
            vs = self._voice_state
            ds = self._debug_state
            return {
                "has_pose": vs.latest_pose is not None,
                "pose": vs.latest_pose,
                "pose_age_ms": _age_ms(vs.latest_pose_at),
                "queue_size": len(vs.queue),
                "has_pending_command": vs.pending_command is not None,
                "pending_command": vs.pending_command,
                "last_voice_input": ds.last_voice_input,
                "last_voice_input_age_ms": _age_ms(ds.last_voice_input_at),
                "last_parse_result": ds.last_parse_result,
            }

    def get_queue(self) -> List[Dict[str, Any]]:
        """Get the command queue."""
        with self._lock:
            return [{"action": s.action, "payload": s.payload} for s in self._voice_state.queue]

    def clear_queue(self) -> None:
        """Clear the command queue."""
        with self._lock:
            self._voice_state.queue.clear()

    def pop_queue(self) -> Optional[RobotStep]:
        """Pop the next step from the queue. Returns None if empty."""
        with self._lock:
            if self._voice_state.queue:
                return self._voice_state.queue.popleft()
            return None

    def append_queue(self, step: RobotStep) -> None:
        """Append a step to the command queue."""
        with self._lock:
            self._voice_state.queue.append(step)

    def queue_size(self) -> int:
        """Get the size of the command queue."""
        with self._lock:
            return len(self._voice_state.queue)

    def get_pose(self) -> Optional[List[float]]:
        """Get the latest TCP pose."""
        with self._lock:
            return self._voice_state.latest_pose

    def update_pose(self, pose: List[float], timestamp: float) -> None:
        """Update the latest TCP pose."""
        with self._lock:
            self._voice_state.latest_pose = pose
            self._voice_state.latest_pose_at = timestamp

    def get_pending_command(self) -> Optional[Dict[str, Any]]:
        """Get the pending command."""
        with self._lock:
            return self._voice_state.pending_command

    def set_pending_command(self, command: Optional[Dict[str, Any]]) -> None:
        """Set the pending command."""
        with self._lock:
            self._voice_state.pending_command = command

    def get_history(self) -> List[Dict[str, Any]]:
        """Get command history."""
        with self._lock:
            return list(self._debug_state.command_history[-50:])

    def get_errors(self) -> List[Dict[str, Any]]:
        """Get error log."""
        with self._lock:
            return list(self._debug_state.error_log[-50:])

    def add_to_history(self, entry: Dict[str, Any]) -> None:
        """Add entry to command history."""
        with self._lock:
            self._debug_state.command_history.append(entry)
            if len(self._debug_state.command_history) > 100:
                self._debug_state.command_history = self._debug_state.command_history[-100:]

    def add_error(self, error: Dict[str, Any]) -> None:
        """Add entry to error log."""
        with self._lock:
            self._debug_state.error_log.append(error)
            if len(self._debug_state.error_log) > 100:
                self._debug_state.error_log = self._debug_state.error_log[-100:]

    def update_voice_input(self, text: str, parse_result: Dict[str, Any], timestamp: float) -> None:
        """Update last voice input info."""
        with self._lock:
            self._debug_state.last_voice_input = text
            self._debug_state.last_voice_input_at = timestamp
            self._debug_state.last_parse_result = parse_result

    def update_robot_command(self, command: Dict[str, Any], timestamp: float) -> None:
        """Update last robot command info."""
        with self._lock:
            self._debug_state.last_robot_command = command
            self._debug_state.last_robot_command_at = timestamp

    def update_image(self, image_bytes: bytes, timestamp: float) -> None:
        """Update latest camera image."""
        with self._lock:
            self._debug_state.latest_image = image_bytes
            self._debug_state.latest_image_at = timestamp

    def get_image(self) -> Optional[bytes]:
        """Get latest camera image."""
        with self._lock:
            return self._debug_state.latest_image

    def get_image_age_ms(self) -> Optional[int]:
        """Get age of latest image in milliseconds."""
        with self._lock:
            return _age_ms(self._debug_state.latest_image_at)

    def get_last_voice_input(self) -> Optional[str]:
        """Get the last voice input text."""
        with self._lock:
            return self._debug_state.last_voice_input

    def get_last_parse_result(self) -> Optional[Dict[str, Any]]:
        """Get the last parse result."""
        with self._lock:
            return self._debug_state.last_parse_result

    def get_held_object_id(self) -> Optional[str]:
        """Get the ID of the currently held object."""
        with self._lock:
            return self._voice_state.held_object_id

    def set_held_object_id(self, object_id: Optional[str]) -> None:
        """Set the ID of the currently held object."""
        with self._lock:
            self._voice_state.held_object_id = object_id

    def clear_held_object(self) -> None:
        """Clear the held object (after releasing)."""
        with self._lock:
            self._voice_state.held_object_id = None


def _age_ms(timestamp: Optional[float]) -> Optional[int]:
    """Calculate age in milliseconds from monotonic timestamp."""
    import time

    if timestamp is None:
        return None
    return int((time.monotonic() - timestamp) * 1000)
