"""FastAPI application for the voice control web interface."""

from __future__ import annotations

import threading
from typing import Any, Callable, Dict, List, Optional, TYPE_CHECKING

import uvicorn
from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse, Response

from .models import CommandRequest, CommandResponse
from .templates import HTML_TEMPLATE
from ..core.state import SharedState

if TYPE_CHECKING:
    from ..core.scene import SceneState

# Type alias for vocabulary getter
VocabularyGetter = Callable[[], Dict[str, List[str]]]


def create_api(
    state: SharedState,
    scene: "SceneState",
    vocabulary_getter: Optional[VocabularyGetter] = None,
) -> FastAPI:
    """Create FastAPI application with voice control endpoints."""
    app = FastAPI(
        title="Voice Control Debug API",
        description="Debug interface for the voice control node",
        version="0.1.0",
    )

    @app.get("/", response_class=HTMLResponse)
    def index() -> str:
        """Serve the web interface."""
        return HTML_TEMPLATE

    @app.get("/api/status")
    def get_status() -> dict:
        """Get current status."""
        try:
            return state.get_status()
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @app.get("/api/objects")
    def get_objects() -> dict:
        """Get all scene objects."""
        try:
            detected = [obj.to_dict() for obj in scene.query(source="detected")]
            config_objs = [obj.to_dict() for obj in scene.query(source="config")]
            return {
                "detected": detected,
                "static": config_objs,
            }
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @app.get("/api/scene")
    def get_scene() -> dict:
        """Get complete scene state including held object."""
        try:
            detected = [obj.to_dict() for obj in scene.query(source="detected")]
            config_objs = [obj.to_dict() for obj in scene.query(source="config")]
            held_id = state.get_held_object_id()
            held_obj = None
            if held_id:
                obj = scene.get(held_id)
                if obj:
                    held_obj = obj.to_dict()
            return {
                "detected": detected,
                "static": config_objs,
                "held_object_id": held_id,
                "held_object": held_obj,
                "update_mode": scene._update_mode if hasattr(scene, "_update_mode") else "unknown",
                "is_captured": scene.is_captured() if hasattr(scene, "is_captured") else False,
                "total_count": scene.count(),
            }
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @app.get("/api/queue")
    def get_queue() -> list:
        """Get the command queue."""
        try:
            return state.get_queue()
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @app.post("/api/queue/clear")
    def clear_queue() -> dict:
        """Clear the command queue."""
        try:
            state.clear_queue()
            return {"ok": True}
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @app.get("/api/history")
    def get_history() -> list:
        """Get command history."""
        try:
            return state.get_history()
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @app.get("/api/errors")
    def get_errors() -> list:
        """Get error log."""
        try:
            return state.get_errors()
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @app.post("/api/command", response_model=CommandResponse)
    def send_command(request: CommandRequest) -> CommandResponse:
        """Send a voice command."""
        try:
            callback = state.get_command_callback()
            if callback is None:
                return CommandResponse(ok=False, text="No command handler available", status="error")

            result = callback(request.text)
            return CommandResponse(
                ok=result.get("status") == "ok",
                text=result.get("text", ""),
                status=result.get("status", "error"),
            )
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @app.get("/api/image")
    def get_image() -> Response:
        """Get the latest camera image as JPEG."""
        try:
            image_data = state.get_image()
            if image_data is None:
                # Return a 1x1 transparent pixel if no image
                return Response(
                    content=b"",
                    media_type="image/jpeg",
                    status_code=204,
                )
            return Response(
                content=image_data,
                media_type="image/jpeg",
                headers={"Cache-Control": "no-cache, no-store, must-revalidate"},
            )
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @app.get("/api/image/info")
    def get_image_info() -> dict:
        """Get image metadata."""
        try:
            return {
                "has_image": state.get_image() is not None,
                "age_ms": state.get_image_age_ms(),
            }
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @app.get("/api/debug/state")
    def get_debug_state() -> dict:
        """Get full system state for debugging."""
        try:
            detected = [obj.to_dict() for obj in scene.query(source="detected")]
            config_objs = [obj.to_dict() for obj in scene.query(source="config")]
            return {
                "pose": state.get_pose(),
                "queue_size": state.queue_size(),
                "pending_command": state.get_pending_command(),
                "detected_objects": detected,
                "static_objects": config_objs,
                "scene_object_count": scene.count(),
                "last_voice_input": state.get_last_voice_input(),
                "last_parse_result": state.get_last_parse_result(),
            }
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @app.get("/api/debug/vocabulary")
    def get_vocabulary() -> dict:
        """Get all recognized keywords."""
        try:
            if vocabulary_getter is None:
                return {"error": "Vocabulary getter not configured"}
            return vocabulary_getter()
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    return app


def run_uvicorn(app: FastAPI, host: str, port: int) -> None:
    """Run uvicorn server (for use in background thread)."""
    config = uvicorn.Config(app, host=host, port=port, log_level="warning")
    server = uvicorn.Server(config)
    server.run()


def start_api_server(
    state: SharedState,
    scene: "SceneState",
    config: Any,
    vocabulary_getter: Optional[VocabularyGetter] = None,
) -> threading.Thread:
    """Start the API server in a background thread.

    Args:
        state: Shared state container
        scene: Scene state with object tracking
        config: API configuration with host and port
        vocabulary_getter: Optional function to get action keywords for debug endpoint
    """
    import time as _time

    app = create_api(state, scene, vocabulary_getter)
    api_thread = threading.Thread(
        target=run_uvicorn,
        args=(app, config.host, config.port),
        daemon=True,
    )
    api_thread.start()
    timestamp = _time.strftime("%H:%M:%S")
    print(f"[voice_control {timestamp}] Web interface at http://{config.host}:{config.port}", flush=True)
    return api_thread
