"""The review (Stream K4): `/api/review`, the sheet a person reads and the approval, reject and
undo they act with. Owner only (`services/access.py`, every route in the owner class); the page is
`/review`.

Sources of truth: `services/proposals.py` (every read and write here), `services/decisions/` (the
writer and undo behind it), `docs/REVIEW-PROCESS.md`. The reads write nothing: the effective
decision, drift and staleness are computed per request. The writes record who acted from the
session, never from the payload, and each is one batch a person can undo as one.
"""

from __future__ import annotations

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

from app.db import get_db
from app.models.schemas import (ReviewApproveIn, ReviewApproveOut, ReviewBatchDetailOut, ReviewDecisionOut, ReviewHintsOut,
                                ReviewLineTableOut, ReviewNotesOut, ReviewSheetOut, ReviewSheetSummaryOut, ReviewUndoIn, ReviewUndoOut)
from app.services import audit_log, identity
from app.services import proposals as review
from app.services.decisions.writer import FIELDS, Refused

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


def _refused(exc: Refused) -> HTTPException:
    code = exc.code
    status = 404 if code.endswith("_NOT_FOUND") or code in ("BRAND_MISSING", "PASS_NOT_FOUND") else 422 if code == "VALUE_INVALID" else 409
    return HTTPException(status_code=status, detail={"error_code": code, "summary": exc.summary})


@router.get("/sheets", response_model=list[ReviewSheetSummaryOut])
def read_sheets(db: Session = Depends(get_db)) -> list[dict]:
    return review.sheets(db)


@router.get("/sheets/{brand_slug}", response_model=ReviewSheetOut)
def read_sheet(brand_slug: str, pass_name: str | None = Query(default=None, alias="pass", max_length=120),
               db: Session = Depends(get_db)) -> dict:
    try:
        return review.sheet(db, brand_slug, pass_name)
    except Refused as exc:
        raise _refused(exc) from exc


@router.get("/hints/{brand_slug}", response_model=ReviewHintsOut)
def read_hints(brand_slug: str, rule: str | None = Query(default=None, max_length=60),
               limit: int = Query(default=500, ge=1, le=5000), db: Session = Depends(get_db)) -> dict:
    """What the word lists would have suggested for this brand: what the AI pass reads, and what
    rian no longer has to answer (K11.2). Nothing here is applied and nothing here is a question."""
    try:
        return review.hints(db, brand_slug, rule=rule, limit=limit)
    except Refused as exc:
        raise _refused(exc) from exc


@router.get("/sheets/{brand_slug}/line", response_model=ReviewLineTableOut)
def read_line_table(brand_slug: str, ref: str = Query(max_length=500),
                    pass_name: str | None = Query(default=None, alias="pass", max_length=120),
                    db: Session = Depends(get_db)) -> dict:
    """The product line table a pass emits: the unit of review (K11.5, K11.6)."""
    try:
        return review.line_table(db, brand_slug, ref, pass_name)
    except Refused as exc:
        raise _refused(exc) from exc


@router.get("/notes", response_model=ReviewNotesOut)
def read_notes(brand: str | None = Query(default=None, max_length=160), field: str | None = Query(default=None, max_length=80),
               key: str | None = Query(default=None, max_length=500), limit: int = Query(default=200, ge=1, le=2000),
               db: Session = Depends(get_db)) -> dict:
    """The notes a deferred or rejected question carries: what the next pass must read before it
    asks again (K11.4). Read-only."""
    try:
        return review.deferred_notes(db, brand, field=field, natural_key=key, limit=limit)
    except Refused as exc:
        raise _refused(exc) from exc


@router.get("/batches/{batch_uid}", response_model=ReviewBatchDetailOut)
def read_batch(batch_uid: str, db: Session = Depends(get_db)) -> dict:
    try:
        return review.batch_detail(db, batch_uid)
    except Refused as exc:
        raise _refused(exc) from exc


@router.get("/decisions", response_model=list[ReviewDecisionOut])
def read_decisions(entity: str = Query(pattern=r"^[a-z_]+:[0-9]{1,18}$"), limit: int = Query(default=200, ge=1, le=1000),
                   db: Session = Depends(get_db)) -> list[dict]:
    entity_type, _, entity_id = entity.partition(":")
    if entity_type not in FIELDS:
        raise HTTPException(status_code=422, detail={"error_code": "VALUE_INVALID", "summary": f"No entity type {entity_type!r}."})
    return review.decisions_for(db, entity_type, int(entity_id), limit)


@router.post("/sheets/{brand_slug}/approve", response_model=ReviewApproveOut)
def approve_sheet(brand_slug: str, payload: ReviewApproveIn, request: Request, db: Session = Depends(get_db)) -> dict:
    who = identity.actor(request)
    scope = payload.scope if payload.scope == "all" or payload.scope is None else payload.scope.model_dump(exclude_none=True)
    try:
        result = review.approve(db, brand_slug, payload.pass_name, scope=scope, by=who.id, corrections=payload.corrections,
                                reject=payload.reject, defer=payload.defer, note=payload.note, force=payload.force)
    except Refused as exc:
        db.rollback()
        raise _refused(exc) from exc
    audit_log.record("review.approve", entity_type="brand", entity_key=brand_slug,
                     detail={"pass": payload.pass_name, "batch": result["batch_uid"], "counts": result["counts"]})
    return result


@router.post("/batches/{batch_uid}/undo", response_model=ReviewUndoOut)
def undo_batch(batch_uid: str, payload: ReviewUndoIn, request: Request, db: Session = Depends(get_db)) -> dict:
    who = identity.actor(request)
    try:
        result = review.undo_batch(db, batch_uid, who.id, payload.reason, force=payload.force)
    except Refused as exc:
        db.rollback()
        raise _refused(exc) from exc
    audit_log.record("review.undo_batch", entity_type="batch", entity_key=batch_uid,
                     detail={"undo_batch": result["undo_batch_uid"], "reason": payload.reason})
    return result


@router.post("/decisions/{decision_id}/undo", response_model=ReviewUndoOut)
def undo_decision(decision_id: int, payload: ReviewUndoIn, request: Request, db: Session = Depends(get_db)) -> dict:
    who = identity.actor(request)
    try:
        result = review.undo_decision(db, decision_id, who.id, payload.reason, force=payload.force)
    except Refused as exc:
        db.rollback()
        raise _refused(exc) from exc
    audit_log.record("review.undo_decision", entity_type="decision", entity_key=str(decision_id),
                     detail={"undo_batch": result["undo_batch_uid"], "reason": payload.reason})
    return result
