"""Spec lifecycle API endpoints."""

from __future__ import annotations

from typing import Any

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

from ..services import artifact_storage
from ..services.artifacts import ArtifactType, validate_fqn
from ..services.spec_schema import VALID_PHASE_NAMES, SpecValidationError

router = APIRouter(tags=["specs"])


class CreateSpecBody(BaseModel):
    namespace: str = Field(min_length=1, max_length=64)
    name: str = Field(min_length=1, max_length=64)
    content: str = Field(min_length=1)


@router.get("/specs")
async def list_specs(request: Request) -> list[dict[str, Any]]:
    """List all spec artifacts with phase status summaries."""
    db = request.app.state.db
    results = artifact_storage.list_artifacts(db, artifact_type=ArtifactType.SPEC)
    for r in results:
        r.pop("content", None)
    return results


@router.post("/specs", status_code=201)
async def create_spec(request: Request, body: CreateSpecBody) -> dict[str, Any]:
    """Create a new spec artifact from YAML content."""
    db = request.app.state.db
    from ..services.spec_service import create_spec as _create_spec

    try:
        art = _create_spec(db, body.namespace, body.name, body.content)
    except SpecValidationError as exc:
        raise HTTPException(status_code=422, detail=str(exc))
    except Exception as exc:
        if "UNIQUE constraint" in str(exc):
            raise HTTPException(status_code=409, detail="Spec with this FQN already exists")
        raise
    return art


@router.get("/specs/{fqn:path}/stale")
async def get_spec_stale(request: Request, fqn: str) -> dict[str, Any]:
    """Get current stale state with derived reasons for each phase."""
    if not validate_fqn(fqn):
        raise HTTPException(status_code=400, detail="Invalid FQN format")

    db = request.app.state.db
    art = artifact_storage.get_artifact_by_fqn(db, fqn)
    if not art or art["type"] != "spec":
        raise HTTPException(status_code=404, detail="Spec not found")

    from ..services.spec_diff import derive_stale_reason
    from ..services.spec_schema import get_phase_status

    phases: dict[str, Any] = {}
    for phase in VALID_PHASE_NAMES:
        status = get_phase_status(art["metadata"], phase)
        reason = derive_stale_reason(db, art["id"], phase, art["metadata"]) if status.value == "stale" else None
        phases[phase] = {"status": status.value, "reason": reason}

    return {"fqn": fqn, "phases": phases}


@router.get("/specs/{fqn:path}/diff")
async def get_spec_diff(
    request: Request,
    fqn: str,
    v1: int | None = Query(None, ge=1),
    v2: int | None = Query(None, ge=1),
) -> dict[str, Any]:
    """Diff between two spec versions."""
    if not validate_fqn(fqn):
        raise HTTPException(status_code=400, detail="Invalid FQN format")

    db = request.app.state.db
    art = artifact_storage.get_artifact_by_fqn(db, fqn)
    if not art or art["type"] != "spec":
        raise HTTPException(status_code=404, detail="Spec not found")

    versions = artifact_storage.list_artifact_versions(db, art["id"])
    if len(versions) < 2:
        raise HTTPException(status_code=422, detail="Need at least 2 versions to diff")

    from ..services.spec_diff import diff_spec_versions
    from ..services.spec_schema import parse_spec_content

    # Default: diff previous vs current
    ver_to = v2 or versions[0]["version"]
    ver_from = v1 or (versions[1]["version"] if len(versions) >= 2 else 1)

    old_content = None
    new_content = None
    for v in versions:
        if v["version"] == ver_from:
            old_content = v["content"]
        if v["version"] == ver_to:
            new_content = v["content"]

    if old_content is None or new_content is None:
        raise HTTPException(status_code=404, detail="Version not found")

    old_spec = parse_spec_content(old_content)
    new_spec = parse_spec_content(new_content)
    diff = diff_spec_versions(old_spec, new_spec, version_from=ver_from, version_to=ver_to)

    return {
        "fqn": fqn,
        "version_from": diff.version_from,
        "version_to": diff.version_to,
        "phase_changes": [
            {"phase": pc.phase, "content_changed": pc.content_changed, "content_diff": pc.content_diff}
            for pc in diff.phase_changes
        ],
        "task_changes": [
            {
                "task_id": tc.task_id,
                "change_type": tc.change_type,
                "summary_before": tc.summary_before,
                "summary_after": tc.summary_after,
            }
            for tc in diff.task_changes
        ],
    }


@router.get("/specs/{fqn:path}")
async def get_spec(request: Request, fqn: str) -> dict[str, Any]:
    """Get a spec with parsed phase statuses and stale reasons."""
    if not validate_fqn(fqn):
        raise HTTPException(status_code=400, detail="Invalid FQN format")

    db = request.app.state.db
    from ..services.spec_service import get_spec as _get_spec

    result = _get_spec(db, fqn)
    if result is None:
        raise HTTPException(status_code=404, detail="Spec not found")

    art, spec_content = result
    versions = artifact_storage.list_artifact_versions(db, art["id"])
    art["version"] = versions[0]["version"] if versions else 1
    art["versions"] = versions

    from ..services.spec_diff import derive_stale_reason
    from ..services.spec_schema import get_phase_status

    phase_info: dict[str, Any] = {}
    for phase in VALID_PHASE_NAMES:
        status = get_phase_status(art["metadata"], phase)
        reason = derive_stale_reason(db, art["id"], phase, art["metadata"]) if status.value == "stale" else None
        phase_info[phase] = {"status": status.value, "reason": reason}
    art["phase_info"] = phase_info

    return art


@router.post("/specs/{fqn:path}/phases/{phase}/approve")
async def approve_spec_phase(request: Request, fqn: str, phase: str) -> dict[str, Any]:
    """Approve a spec phase."""
    if not validate_fqn(fqn):
        raise HTTPException(status_code=400, detail="Invalid FQN format")
    if phase not in VALID_PHASE_NAMES:
        raise HTTPException(status_code=400, detail=f"Invalid phase: must be one of {sorted(VALID_PHASE_NAMES)}")

    db = request.app.state.db
    from ..services.spec_service import approve_phase

    try:
        art = approve_phase(db, fqn, phase)
    except (ValueError, SpecValidationError) as exc:
        raise HTTPException(status_code=400, detail=str(exc))

    if art is None:
        raise HTTPException(status_code=404, detail="Spec not found")

    return {"status": "approved", "fqn": fqn, "phase": phase}


@router.post("/specs/{fqn:path}/phases/{phase}/unapprove")
async def unapprove_spec_phase(request: Request, fqn: str, phase: str) -> dict[str, Any]:
    """Reset a spec phase to draft."""
    if not validate_fqn(fqn):
        raise HTTPException(status_code=400, detail="Invalid FQN format")
    if phase not in VALID_PHASE_NAMES:
        raise HTTPException(status_code=400, detail=f"Invalid phase: must be one of {sorted(VALID_PHASE_NAMES)}")

    db = request.app.state.db
    from ..services.spec_service import unapprove_phase

    try:
        art = unapprove_phase(db, fqn, phase)
    except (ValueError, SpecValidationError) as exc:
        raise HTTPException(status_code=400, detail=str(exc))

    if art is None:
        raise HTTPException(status_code=404, detail="Spec not found")

    return {"status": "draft", "fqn": fqn, "phase": phase}
