"""Index candidates (Stream K6; plan W18): `/api/review/index`, the panel on `/review` where a
person approves which generated pages search engines are shown. Owner only (`services/access.py`).

Sources of truth: `services/publish.py` (every read and write here), `services/indexnow.py` (the
one ping an approval earns). The read writes nothing; the writes record who acted from the
session, never from the payload, and each is one batch a person can undo as one from the
review page's batch list.
"""

from __future__ import annotations

from typing import Literal

from fastapi import APIRouter, Depends, Query, Request
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session

from app.config import settings
from app.db import get_db
from app.services import audit_log, identity, indexnow, publish

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

Kind = Literal["brand", "product_line", "place"]


class IndexCandidateOut(BaseModel):
    suggestion_id: int
    kind: Kind
    #: `approve` (the page meets its rule) or `remove` (indexed, and now under its rule).
    action: Literal["approve", "remove"]
    slug: str
    name: str
    path: str
    why: str
    #: The page's three facts (plan W18), and the fourth.
    reachable: bool
    quality: bool | None = None
    indexed: bool
    hidden: bool


class IndexDeskOut(BaseModel):
    totals: dict[str, int]
    rows: list[IndexCandidateOut]


class IndexDecideIn(BaseModel):
    suggestion_ids: list[int] = Field(min_length=1, max_length=5000)
    #: `approve` acts on each suggestion as it reads (index it, or remove it); `dismiss` answers no.
    action: Literal["approve", "dismiss"]


class IndexRefusalOut(BaseModel):
    page: str
    error_code: str
    summary: str


class IndexDecideOut(BaseModel):
    message: str
    counts: dict[str, int]
    batch_uids: list[str]
    refusals: list[IndexRefusalOut]
    pinged: int


@router.get("/candidates", response_model=IndexDeskOut)
def read_candidates(kind: Kind | None = None, brand: str | None = Query(default=None, max_length=160),
                    limit: int = Query(default=200, ge=1, le=1000), offset: int = Query(default=0, ge=0),
                    db: Session = Depends(get_db)) -> dict:
    return publish.desk(db, kind=kind, limit=limit, offset=offset, brand=brand)


@router.post("/candidates/decide", response_model=IndexDecideOut)
def decide_candidates(payload: IndexDecideIn, request: Request, db: Session = Depends(get_db)) -> dict:
    who = identity.actor(request)
    wanted = set(payload.suggestion_ids)
    rows = [s for s in publish.open_suggestions(db) if s.id in wanted]
    if payload.action == "dismiss":
        n = publish.dismiss(db, [s.id for s in rows], who.id)
        db.commit()
        audit_log.record("review.index_dismiss", entity_type="suggestion", entity_key=str(n), detail={"dismissed": n})
        return {"message": f"{n} dismissed", "counts": {"dismissed": n}, "batch_uids": [], "refusals": [], "pinged": 0}
    counts = {"changed": 0, "unchanged": 0, "refused": 0}
    uids: list[str] = []
    refusals: list[dict] = []
    ping: list[str] = []
    for indexed, reason in ((True, publish.REASON_ADD), (False, publish.REASON_REMOVE)):
        pages = [(publish.KIND_OF_LEVEL[s.level], s.left_id) for s in rows if s.reason == reason]
        if not pages:
            continue
        result = publish.set_indexed(db, pages, indexed, who.id, batch_kind="desk")
        for key in counts:
            counts[key] += result["counts"][key]
        uids.extend([result["batch_uid"]] if result["batch_uid"] else [])
        refusals.extend(result["refusals"])
        ping.extend(result["ping"])
    db.commit()
    sent = indexnow.ping_approved(ping, settings.public_base_url.rstrip("/"), settings.indexnow_key if settings.has_indexnow else "")
    audit_log.record("review.index_decide", entity_type="batch", entity_key=",".join(uids) or "-", detail={"counts": counts})
    return {"message": f"{counts['changed']} decided, {counts['unchanged']} already so, {counts['refused']} refused", "counts": counts,
            "batch_uids": uids, "refusals": refusals, "pinged": sum(n for n, _ in sent)}
