"""How much of a person's judgement a suggestion needs, which is not how sure the machine is.

Sources of truth: this module, `models/decisions.py` (`ATTENTION_LEVELS`), `proposals.py` (the
sheet reads it), `docs/AI-REVIEW-GUIDELINES.md`, `tests/test_attention.py`.

Confidence measures the inference: how surely these words mean that value. Attention measures the
consequence: how much a person's judgement is worth spending here. They are different axes and
sorting by the wrong one buries the rows that matter. Rian, 18 Sep, naming the five:

    critical  the answer sets a precedent or makes a new kind of thing. The FIRST decision about a
              gift set is critical, because every later set is answered by it.
    high      a significant change, roughly as arguable either way, or with a large downstream
              effect such as overwriting attributes across many variants.
    medium    the proposal is probably right; a person does not really need to check it.
    low       there is barely a decision, but a person might catch what a pass missed.
    none      nothing to decide. The row exists so it can be marked seen.

**Critical is relative to what has already been answered.** The first set decision sets the
precedent; the tenth follows it. So a pass may propose `critical`, and this downgrades it to `high`
once a decision of that kind exists, which is what keeps the top of the list short as rian works
through it. Nothing upgrades on its own: a pass that said `critical` and a precedent that does not
exist yet stay critical.
"""

from __future__ import annotations

from typing import Any

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.models import Decision, Proposal
from app.models.decisions import ATTENTION_LEVELS

LEVELS = ATTENTION_LEVELS
#: What a person reads for each, and when it applies. Shown on the guide and beside the filter.
LEVEL_WHAT: dict[str, str] = {
    "critical": "sets a precedent, or makes a new kind of thing. Answer these first: every later "
                "question of the same kind is answered by what you decide here.",
    "high": "a real change, about as arguable either way, or one that rewrites a lot downstream.",
    "medium": "probably right. You do not need to check it, but you can.",
    "low": "barely a decision. Worth an eye in case the pass missed something.",
    "none": "nothing to decide. Listed so it can be marked seen.",
}


def precedent_key(row: Proposal) -> str:
    """The KIND of judgement a row is, which is what a precedent is set on.

    Two questions share a key when answering one should answer the other. A gift set folding into
    its bottle's line and another gift set doing the same are one kind; a gift set and a refill are
    not, because rian drew a distinction between them and each sets its own precedent.
    """
    if row.entity_type == "suggestion" and row.field == "decision":
        level = row.natural_key.split(":")[1] if row.natural_key.startswith("pair:") else "pair"
        return f"same-or-separate:{level}"
    if row.field.startswith("attribute:"):
        return f"attribute:{row.field[len('attribute:'):]}"
    if row.field == "product_line":
        # The word a set-like membership turns on. Each is its own precedent: rian ruled that a
        # refill serving one line and a coffret spanning four are not the same case.
        reason = (row.reason or "").lower()
        for word in ("coffret", "gift set", "gift", "refill", "duo", "trio", "kit", "set", "pack"):
            if word in reason:
                return f"membership:{word.replace(' ', '-')}"
        return "membership:plain"
    return f"{row.entity_type}:{row.field}"


def _precedents(db: Session) -> set[str]:
    """Every kind of judgement a person has already answered. A decision made by a person, not a
    replay of one: a replayed row is the same answer arriving on another host, and the precedent
    it set was set where it was made."""
    out: set[str] = set()
    for row in db.scalars(select(Proposal).where(Proposal.status.in_(("approved", "rejected", "deferred")))):
        out.add(precedent_key(row))
    return out


def _derive(row: Proposal, has_precedent: bool) -> str:
    """The level for a row whose pass did not set one. Deliberately cautious: where a rule cannot
    tell, it asks for more of a person rather than less."""
    if not has_precedent:
        return "critical"
    if row.field == "merged_into" or (row.entity_type == "suggestion" and row.natural_key.startswith("pair:product:")):
        return "high"  # variants become one, which changes what a price is compared against
    confidence = float(row.confidence) if row.confidence is not None else None
    if confidence is None or confidence < 0.7:
        return "high"
    if confidence >= 0.9:
        return "low"
    return "medium"


def for_rows(db: Session, rows: list[Proposal]) -> dict[int, str]:
    """The level for each row, by proposal id. One pass over the ledger, not one per row."""
    if not rows:
        return {}
    known = _precedents(db)
    out: dict[int, str] = {}
    for row in rows:
        key = precedent_key(row)
        settled = key in known
        level = row.attention
        if level not in LEVELS:
            level = _derive(row, settled)
        elif level == "critical" and settled:
            # The precedent exists, so this is no longer the question that sets it.
            level = "high"
        out[row.id] = level
    return out


def summary(levels: dict[int, str]) -> dict[str, int]:
    """How many rows sit at each level, highest first, for the sheet's own heading."""
    counts = {level: 0 for level in LEVELS}
    for level in levels.values():
        if level in counts:
            counts[level] += 1
    return counts


def describe() -> list[dict[str, Any]]:
    """The levels as the review guide shows them."""
    return [{"level": level, "what": LEVEL_WHAT[level]} for level in LEVELS]
