"""The review process document, read at runtime so the page cannot drift from it.

Sources of truth: this module, `docs/REVIEW-PROCESS.md` (the authority), `routers/review.py`
(`GET /api/review/process`), `tests/test_process_doc.py`.

`docs/REVIEW-PROCESS.md` is the one file a review pass reads before it proposes anything: the
boundary between what a rule may do alone and what must be proposed, the grouping default for
each vertical, what a proposal must carry, and what a person spot-checks. The review area's first
tab shows it to a person who is not going to read the repository.

**It is READ, never restated.** A second copy of a rule in TypeScript is a copy that goes stale
the first time the doc changes, and the doc's own version line is a rules version -- changing it
is a deploy and, when the boundary moves, a rederive. So the server parses the file into its
sections and hands the markdown over; the panel renders it. When the doc changes, the page
changes at the next deploy and nobody has to remember.

The file ships in the image (`Dockerfile`, the runtime stage copies this one doc); in a checkout
it is found beside the package. When neither exists the reader says so rather than inventing
content, and the page shows the sentence instead of a blank tab.
"""

from __future__ import annotations

import pathlib
import re
from typing import Any

from app.services.markdown import heading_id, render_markdown

#: The container's copy first, then the checkout's (`main/docs/`), as `services/items.py` does.
_CANDIDATES = (
    pathlib.Path("/srv/app/docs/REVIEW-PROCESS.md"),
    pathlib.Path(__file__).resolve().parents[2] / "docs" / "REVIEW-PROCESS.md",
)
_HEADING = re.compile(r"^##\s+(?:(\d+)\.\s*)?(.+?)\s*$")
_VERSION = re.compile(r"\*\*Version\s+(\d+)\s*,\s*([0-9-]+)\*\*")


def path() -> pathlib.Path | None:
    return next((p for p in _CANDIDATES if p.is_file()), None)


def _split(source: str) -> tuple[str, list[tuple[str | None, str, list[str]]]]:
    """The text before the first `##` heading, then one entry per section."""
    preamble: list[str] = []
    sections: list[tuple[str | None, str, list[str]]] = []
    current: list[str] | None = None
    for line in source.splitlines():
        match = _HEADING.match(line)
        if match:
            current = []
            sections.append((match.group(1), match.group(2), current))
        elif current is not None:
            current.append(line)
        else:
            preamble.append(line)
    return "\n".join(preamble), sections


def read() -> dict[str, Any]:
    """The document as the page shows it: its version, and a section per `##` heading with its
    number, title, anchor and rendered body. Writes nothing and reads no database."""
    found = path()
    if found is None:
        return {"version": None, "version_line": None, "path": None, "missing": True, "sections": []}
    source = found.read_text(encoding="utf-8")
    preamble, sections = _split(source)
    version = _VERSION.search(preamble)
    # The preamble's first heading and the version line are shown as the tab's own standing, not
    # as a section; the rest of it is the standing rules every section obeys.
    body = "\n".join(line for line in preamble.splitlines() if not line.startswith("# "))
    out = [{"number": None, "title": "The standing rules", "anchor": "standing",
            "html": render_markdown(body.strip())}]
    for number, title, lines in sections:
        out.append({"number": number, "title": title, "anchor": heading_id(f"{number}. {title}" if number else title),
                    "html": render_markdown("\n".join(lines).strip())})
    return {
        "version": version.group(1) if version else None,
        "version_line": version.group(0) if version else None,
        "dated": version.group(2) if version else None,
        "path": "docs/REVIEW-PROCESS.md",
        "missing": False,
        "sections": out,
    }


def passes(db) -> list[dict[str, Any]]:
    """Every pass that has written proposals, with what is waiting, so each block on the tab can
    link to the rows it produced rather than describing them. One query per column; no writes."""
    from collections import Counter

    from sqlalchemy import func, select

    from app.models import Proposal, ProposalPass

    rows = {p.id: p for p in db.scalars(select(ProposalPass))}
    tallies: dict[int, Counter] = {}
    brands: dict[int, set[str]] = {}
    for pass_id, status, brand_slug, n in db.execute(
            select(Proposal.pass_id, Proposal.status, Proposal.brand_slug, func.count(Proposal.id))
            .group_by(Proposal.pass_id, Proposal.status, Proposal.brand_slug)):
        tallies.setdefault(pass_id, Counter())[status] += n
        tallies[pass_id]["total"] += n
        if status == "open" and brand_slug:
            brands.setdefault(pass_id, set()).add(brand_slug)
    out = []
    for pass_id, row in rows.items():
        counts = tallies.get(pass_id, Counter())
        out.append({"name": row.name, "kind": row.kind, "generator": row.generator,
                    "process_version": row.process_version, "rules_version": row.rules_version,
                    "withdrawn": row.withdrawn_at is not None,
                    "open": int(counts.get("open", 0)), "total": int(counts.get("total", 0)),
                    "brands": sorted(brands.get(pass_id, ()))[:8], "brand_count": len(brands.get(pass_id, ()))})
    out.sort(key=lambda r: (-r["open"], r["name"]))
    return out
