"""Public reads of editorial text: the article list, one article, and the two keyed blocks.

Sources of truth: `services/editorial.py` (the queries; published only), `models/editorial.py`
(the shapes). No route here writes. A draft answers 404 at its own address exactly like a
slug that never existed, so nothing about unpublished work is inferable from outside. The
airport and category blocks answer `null` rather than 404 when there is no text, because a
page component asks for them on every airport and category and an absence is normal.
"""

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session

from app.db import get_db
from app.models.editorial import ArticleOut, ArticlePage
from app.services import editorial

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


@router.get("", response_model=ArticlePage)
def list_articles(
    kind: str = Query(default="article", pattern="^(article|airport_writeup|category_intro)$"),
    limit: int = Query(default=24, ge=1, le=100),
    offset: int = Query(default=0, ge=0),
    db: Session = Depends(get_db),
) -> ArticlePage:
    total, rows = editorial.list_articles(db, kind=kind, limit=limit, offset=offset)
    return ArticlePage(
        total=total, limit=limit, offset=offset, items=[editorial.article_summary(a) for a in rows]
    )


@router.get("/airport/{iata}", response_model=ArticleOut | None)
def airport_writeup(iata: str, db: Session = Depends(get_db)) -> ArticleOut | None:
    """The published write-up for an airport page, or null."""
    if not (len(iata) == 3 and iata.isalpha()):
        raise HTTPException(status_code=404, detail="no such airport")
    row = editorial.airport_writeup(db, iata)
    return editorial.article_out(row) if row else None


@router.get("/category/{category}", response_model=ArticleOut | None)
def category_intro(category: str, db: Session = Depends(get_db)) -> ArticleOut | None:
    """The published intro for one of our category names, or null."""
    if not category or len(category) > 80:
        raise HTTPException(status_code=404, detail="no such category")
    row = editorial.category_intro(db, category)
    return editorial.article_out(row) if row else None


@router.get("/{slug}", response_model=ArticleOut)
def get_article(slug: str, db: Session = Depends(get_db)) -> ArticleOut:
    row = editorial.article_by_slug(db, slug) if len(slug) <= 160 else None
    if row is None:
        raise HTTPException(status_code=404, detail="no such article")
    return editorial.article_out(row)
