"""Terminal recording API routes."""

import asyncio
import base64
from pathlib import Path

from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from pydantic import BaseModel

from ..terminal import get_manager

router = APIRouter()


class StartTerminalRequest(BaseModel):
    """Request to start a terminal recording session."""

    session_id: str
    output_path: str
    width: int = 120
    height: int = 40
    recording_start_ms: int | None = None


class StartTerminalResponse(BaseModel):
    """Response from starting a terminal session."""

    success: bool
    session_id: str | None = None
    error: str | None = None


class StopTerminalResponse(BaseModel):
    """Response from stopping a terminal session."""

    success: bool
    session_id: str | None = None
    output_path: str | None = None
    duration_ms: int | None = None
    error: str | None = None


class TerminalStatusResponse(BaseModel):
    """Status of a terminal session."""

    session_id: str
    is_running: bool
    width: int
    height: int


class ResizeRequest(BaseModel):
    """Request to resize a terminal."""

    width: int
    height: int


@router.post("/start", response_model=StartTerminalResponse)
async def start_terminal(request: StartTerminalRequest) -> StartTerminalResponse:
    """Start a new terminal recording session."""
    try:
        manager = get_manager()
        try:
            session = manager.create_session(
                session_id=request.session_id,
                output_path=Path(request.output_path),
                width=request.width,
                height=request.height,
                recording_start_ms=request.recording_start_ms,
            )
        except TypeError:
            # Backward-compatible for tests or custom managers without the new parameter.
            session = manager.create_session(
                session_id=request.session_id,
                output_path=Path(request.output_path),
                width=request.width,
                height=request.height,
            )
        session.start()
        return StartTerminalResponse(success=True, session_id=session.session_id)
    except Exception as e:
        return StartTerminalResponse(success=False, error=str(e))


@router.post("/stop/{session_id}", response_model=StopTerminalResponse)
async def stop_terminal(session_id: str) -> StopTerminalResponse:
    """Stop a terminal recording session."""
    try:
        manager = get_manager()
        result = manager.stop_session(session_id)
        if result:
            return StopTerminalResponse(
                success=True,
                session_id=result["session_id"],
                output_path=result["output_path"],
                duration_ms=result["duration_ms"],
            )
        return StopTerminalResponse(success=False, error="Session not found")
    except Exception as e:
        return StopTerminalResponse(success=False, error=str(e))


@router.get("/status/{session_id}", response_model=TerminalStatusResponse | dict)
async def terminal_status(session_id: str) -> TerminalStatusResponse | dict:
    """Get the status of a terminal session."""
    manager = get_manager()
    session = manager.get_session(session_id)
    if session:
        return TerminalStatusResponse(
            session_id=session.session_id,
            is_running=session.is_running,
            width=session.width,
            height=session.height,
        )
    return {"error": "Session not found"}


@router.post("/resize/{session_id}")
async def resize_terminal(session_id: str, request: ResizeRequest) -> dict:
    """Resize a terminal session."""
    manager = get_manager()
    session = manager.get_session(session_id)
    if session:
        session.resize(request.width, request.height)
        return {"success": True}
    return {"success": False, "error": "Session not found"}


@router.get("/sessions")
async def list_sessions() -> dict:
    """List all active terminal sessions."""
    manager = get_manager()
    return {"sessions": manager.list_sessions()}


@router.websocket("/ws/{session_id}")
async def terminal_websocket(websocket: WebSocket, session_id: str) -> None:
    """WebSocket endpoint for real-time terminal I/O.

    Messages from client:
    - {"type": "input", "data": "base64-encoded-input"}
    - {"type": "resize", "cols": 120, "rows": 40}

    Messages to client:
    - {"type": "output", "data": "base64-encoded-output"}
    - {"type": "exit"}
    """
    await websocket.accept()

    manager = get_manager()
    session = manager.get_session(session_id)

    if not session:
        await websocket.send_json({"type": "error", "message": "Session not found"})
        await websocket.close()
        return

    async def read_output() -> None:
        """Background task to read and send terminal output."""
        while session.is_running:
            data = session.read_output(timeout=0.05)
            if data:
                await websocket.send_json(
                    {
                        "type": "output",
                        "data": base64.b64encode(data).decode("ascii"),
                    }
                )
            await asyncio.sleep(0.01)
        await websocket.send_json({"type": "exit"})

    # Start output reader task
    output_task = asyncio.create_task(read_output())

    try:
        while True:
            message = await websocket.receive_json()

            if message.get("type") == "input":
                data = base64.b64decode(message["data"])
                session.write_input(data)

            elif message.get("type") == "resize":
                cols = message.get("cols", 120)
                rows = message.get("rows", 40)
                session.resize(cols, rows)

    except WebSocketDisconnect:
        pass
    finally:
        output_task.cancel()
        try:
            await output_task
        except asyncio.CancelledError:
            pass


@router.get("/health")
async def terminal_health() -> dict:
    """Terminal API health check."""
    manager = get_manager()
    return {
        "status": "ok",
        "module": "terminal",
        "active_sessions": len(manager.list_sessions()),
    }
