import uuid

from fastapi import APIRouter, Depends, HTTPException, Response, status
from sqlalchemy import select
from sqlalchemy.orm import noload
from sqlalchemy.ext.asyncio import AsyncSession

from api.database import get_session
from api.models.domain import Domain
from api.models.topic import Topic, TopicStatus
from api.schemas.topic import TopicCreate, TopicRead, TopicUpdate, TopicWithWorkstreams

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


async def _resolve_domain_id(domain_slug: str, session: AsyncSession) -> uuid.UUID:
    """Resolve a domain slug to its UUID. Raises 404 if not found."""
    result = await session.execute(select(Domain).where(Domain.slug == domain_slug))
    domain = result.scalar_one_or_none()
    if domain is None:
        raise HTTPException(status_code=404, detail=f"Domain '{domain_slug}' not found")
    return domain.id


@router.get("/", response_model=list[TopicRead])
async def list_topics(
    response: Response,
    status: TopicStatus | None = None,
    session: AsyncSession = Depends(get_session),
) -> list[Topic]:
    response.headers["Cache-Control"] = "max-age=60, stale-while-revalidate=30"
    q = select(Topic).options(
        noload(Topic.workplans),
        noload(Topic.decisions),
        noload(Topic.progress_events),
    )
    if status:
        q = q.where(Topic.status == status)
    q = q.order_by(Topic.created_at)
    result = await session.execute(q)
    return list(result.scalars().all())


@router.post("/", response_model=TopicRead, status_code=status.HTTP_201_CREATED)
async def create_topic(
    body: TopicCreate,
    session: AsyncSession = Depends(get_session),
) -> Topic:
    domain_id = await _resolve_domain_id(body.domain, session)
    existing = await session.execute(select(Topic).where(Topic.slug == body.slug))
    if existing.scalar_one_or_none():
        raise HTTPException(status_code=409, detail=f"Topic slug '{body.slug}' already exists")
    topic = Topic(
        slug=body.slug,
        title=body.title,
        description=body.description,
        domain_id=domain_id,
        status=body.status,
    )
    session.add(topic)
    await session.commit()
    await session.refresh(topic)
    return topic


@router.get("/{topic_id}", response_model=TopicWithWorkstreams)
async def get_topic(
    topic_id: uuid.UUID,
    session: AsyncSession = Depends(get_session),
) -> Topic:
    topic = await session.get(Topic, topic_id)
    if topic is None:
        raise HTTPException(status_code=404, detail="Topic not found")
    return topic


@router.patch("/{topic_id}", response_model=TopicRead)
async def update_topic(
    topic_id: uuid.UUID,
    body: TopicUpdate,
    session: AsyncSession = Depends(get_session),
) -> Topic:
    topic = await session.get(Topic, topic_id)
    if topic is None:
        raise HTTPException(status_code=404, detail="Topic not found")
    updates = body.model_dump(exclude_unset=True)
    if "domain" in updates:
        topic.domain_id = await _resolve_domain_id(updates.pop("domain"), session)
    for field, value in updates.items():
        setattr(topic, field, value)
    await session.commit()
    await session.refresh(topic)
    return topic


@router.delete("/{topic_id}", response_model=TopicRead)
async def archive_topic(
    topic_id: uuid.UUID,
    session: AsyncSession = Depends(get_session),
) -> Topic:
    topic = await session.get(Topic, topic_id)
    if topic is None:
        raise HTTPException(status_code=404, detail="Topic not found")
    topic.status = TopicStatus.archived
    await session.commit()
    await session.refresh(topic)
    return topic
