"""What a record's own check MEANS — shared by rules and notes.

Two record types carry `verify_with` / `expires_when` / `verified_at`: rules
(milestone 312) and notes (milestone 317). What they share is BEHAVIOUR — how
a stamp is read, how old it is, what "never" means — not storage and not the
query. Note 3163 is the rule this file is an instance of: `semantic_search_*`
could never have been shared between them because a rule scopes by rulebook
ownership and a note scopes by the note ACL, so the two sweeps are siblings.
These functions are the part that genuinely is common, factored so it exists
once rather than twice.

Everything here is pure, sync and DUCK-TYPED: it reads `verify_with` and
`verified_at` off whatever it is handed. That is deliberate. A shared base
class or a protocol would tie two SQLAlchemy models together to share four
lines of date arithmetic — the DRY costume, in the same note's words.
"""
from datetime import datetime, timezone


def last_verified_label(record) -> str | None:
    """How long ago this record's check passed — None when it carries none.

    Three states, and the distinction between the last two is the whole point:

    - `None`  — this record is a DECISION. There is nothing to go and check,
      and the question does not apply. Most records.
    - "never" — it asserts a fact and NOBODY HAS EVER CONFIRMED IT. The one
      worth acting on, and why the sweep sorts these first.
    - a date  — somebody checked, then.

    Callers attach this to a payload only when it is not None, so "no key" and
    "never verified" do not become two states every client has to tell apart.
    """
    if not getattr(record, "verify_with", None):
        return None
    stamp = getattr(record, "verified_at", None)
    return stamp.date().isoformat() if stamp else "never"


def days_since_verified(record) -> int | None:
    """Whole days since the check last passed; None if it never has.

    Computed rather than left to the reader, because "2026-06-14" and "74
    days" prompt different reactions and only one of them is the question
    being asked.

    Naive stamps are read as UTC. Postgres hands these back tz-aware, but a
    restore, a fixture or a sqlite-backed test may not, and subtracting an
    aware datetime from a naive one raises — which would make the sweep fail
    on exactly the rows it exists to surface.
    """
    stamp = getattr(record, "verified_at", None)
    if stamp is None:
        return None
    if stamp.tzinfo is None:
        stamp = stamp.replace(tzinfo=timezone.utc)
    return (datetime.now(timezone.utc) - stamp).days
