"""Video generation REST API.

Endpoints:
  POST   /api/video                  — kick off a render, returns {job_id}
  GET    /api/video/jobs             — list recent jobs
  GET    /api/video/jobs/{job_id}    — get one job's live state
  DELETE /api/video/jobs/{job_id}    — cancel a running job (best-effort)

Long renders run as background asyncio tasks. State lives in the in-process
`VideoJobRegistry` (see agntspace.video.jobs). Restart loses job metadata but
each completed render's mp4 is the durable artifact on disk.
"""

from __future__ import annotations

import asyncio
import logging

from fastapi import APIRouter, HTTPException, Request
from pydantic import BaseModel

log = logging.getLogger(__name__)

router = APIRouter(prefix="/video", tags=["video"])


class CreateVideoBody(BaseModel):
    target: str
    duration_seconds: int = 60
    voice: str = "liam"
    style: str = "walkthrough"


@router.post("")
async def create_video_route(request: Request, body: CreateVideoBody) -> dict:
    """Kick off a narrated video render. Returns immediately with a job_id.

    Target formats: ``wiki:slug``, ``kb:slug``, ``project:slug``,
    ``journal:YYYY-MM-DD``, ``url:https://...``.

    Requires the [video] extra — returns 503 with an install hint if deps are missing.
    """

    # Lazy imports so the route file itself loads even without [video] extra
    try:
        from agntspace.video import engine as video_engine
        from agntspace.video import jobs as video_jobs
    except ImportError as exc:
        raise HTTPException(
            status_code=503,
            detail=f"Video subsystem unavailable: {exc}. Install with: pip install 'agntspace[video]'",
        ) from exc

    target = body.target.strip()
    if ":" not in target:
        raise HTTPException(
            status_code=400,
            detail="Invalid target. Use 'wiki:slug' / 'kb:slug' / 'project:slug' / 'journal:YYYY-MM-DD' / 'url:https://...'",
        )
    duration = max(5, min(int(body.duration_seconds), 300))  # hard-clamp to [5, 300]s

    registry = video_jobs.get_registry()
    job = video_jobs.VideoJobState(
        job_id=video_jobs.new_job_id(),
        state="pending",
        started_at=video_jobs._iso_now(),
        target=target,
        duration_seconds=duration,
        voice=body.voice,
        style=body.style,
    )
    await registry.register(job)

    state = request.app.state

    async def _runner() -> None:
        try:
            await registry.update(job.job_id, state="running", phase="adapter")
            # Resolve vault root from the VaultReader's VaultPaths resolver.
            # `.vault_dir` points at the internals dir (<root>/agntspace/); the
            # user-facing agntspace-* siblings live at `.paths.root_dir`.
            vault = getattr(state, "vault", None)
            vault_root = None
            if vault is not None:
                paths = getattr(vault, "paths", None)
                if paths is not None and getattr(paths, "root_dir", None):
                    vault_root = paths.root_dir
                else:
                    vd = getattr(vault, "vault_dir", None)
                    vault_root = vd.parent if vd else None
            # LLM is owned by the Agent; adapters just need .complete()
            agent = getattr(state, "agent", None)
            llm = getattr(agent, "_llm", None) if agent else None
            result = await video_engine.create_video(
                target=target,
                duration_seconds=duration,
                voice=body.voice,
                style=body.style,
                base_url="http://localhost:8000",
                skill_loader=getattr(state, "skills", None),
                vault_reader=vault,
                llm=llm,
                vault_root=vault_root,
            )
            await registry.finish(
                job.job_id,
                state="completed",
                path=str(result.path),
                duration_actual=result.duration_seconds,
                narration_word_count=result.narration_word_count,
                tts_provider_used=result.tts_provider_used,
                warnings=list(result.warnings),
                scenes_total=result.scene_count,
                scenes_recorded=result.scene_count,
                phase="",
            )
        except Exception as exc:  # noqa: BLE001
            log.exception("Video render failed for job %s", job.job_id)
            await registry.finish(job.job_id, state="failed", error=str(exc), phase="")

    asyncio.create_task(_runner())

    return {
        "job_id": job.job_id,
        "state": "pending",
        "target": target,
        "progress_url": f"/api/video/jobs/{job.job_id}",
    }


@router.get("/jobs")
async def list_jobs(request: Request, limit: int = 20) -> dict:
    """Newest-first list of recent video render jobs."""

    try:
        from agntspace.video import jobs as video_jobs
    except ImportError:
        return {"jobs": [], "available": False}

    registry = video_jobs.get_registry()
    items = await registry.list(limit=limit)
    return {"jobs": [j.to_public_dict() for j in items], "available": True}


@router.get("/jobs/{job_id}")
async def get_job(request: Request, job_id: str) -> dict:
    """Live state of one video render job."""

    try:
        from agntspace.video import jobs as video_jobs
    except ImportError as exc:
        raise HTTPException(status_code=503, detail="Video subsystem unavailable") from exc

    registry = video_jobs.get_registry()
    job = await registry.get(job_id)
    if job is None:
        raise HTTPException(status_code=404, detail=f"Job {job_id!r} not found")
    return job.to_public_dict()


@router.delete("/jobs/{job_id}")
async def cancel_job(request: Request, job_id: str) -> dict:
    """Best-effort cancel — marks job cancelled. In-flight Playwright tasks
    are not forcibly killed; the next registry.update call will see the
    cancelled state and the runner exits gracefully on its next checkpoint."""

    try:
        from agntspace.video import jobs as video_jobs
    except ImportError as exc:
        raise HTTPException(status_code=503, detail="Video subsystem unavailable") from exc

    registry = video_jobs.get_registry()
    job = await registry.get(job_id)
    if job is None:
        raise HTTPException(status_code=404, detail=f"Job {job_id!r} not found")
    if job.state in ("completed", "failed", "cancelled"):
        return {"job_id": job_id, "state": job.state, "already_terminal": True}
    await registry.finish(job_id, state="cancelled", error="Cancelled by user", phase="")
    return {"job_id": job_id, "state": "cancelled"}
