"""ProvidenceSource — recursive grounding on STRICT live providence records.

Promotes the system's own past Q&A records (those in `providence_cache`
that landed STRICT and are still live) into the document corpus so
retrieval can surface them as `self_reference_source` citations for
new thoughts.

See ``docs/self-reference-thought-chains-design.md`` for the full
architecture. Trust model summary: STRICT live records older than
the kindergarten window are emitted as documents. HYBRID / UNGROUNDED
records and falsified records (state ∈ {failed, stale, quarantined})
stay opaque to retrieval — the substrate trusts STRICT as fact unless
a verifier falsifies it.
"""

from __future__ import annotations

import sqlite3
import time
from typing import Iterator

from aborist.document import Document
from aborist.source import Source


# Mirrors the mesh-sync kindergarten convention: records younger
# than this many seconds are still "warm" and should not yet be
# recyclable substrate. Default 1 hour gives mesh broadcast time
# to settle and avoids tight echo-chamber loops.
DEFAULT_KINDERGARTEN_SECONDS = 3600

# URI scheme prefix for self-promoted providence documents. The
# query-side `_classify_source_role` matches this prefix to assign
# `self_reference_source`.
PROVIDENCE_URI_PREFIX = "aborist://providence/"


class ProvidenceSource(Source):
    """Yield STRICT live providence_cache records as Documents.

    Filters applied at iteration time:

    1. ``audit_mode == 'STRICT'`` — fully grounded only. HYBRID and
       UNGROUNDED records stay out of the substrate.
    2. ``falsification_state == 'live'`` — falsified rows excluded.
       The existing `aborist providence --falsify` machinery is the
       verifier-falsification mechanism: when a record's state flips,
       it stops being substrate on next ingest.
    3. ``now - created_at >= kindergarten_seconds`` (default 1h) —
       fresh thoughts cool before they're recyclable. Kills tight
       echo-chamber loops.
    4. **Anti-recursion** — records whose own answer text references a
       prior self-reference URI are excluded. First-generation only.
       Conservative; second-gen lands later if lazy-anchor /
       semantic-frame work proves the error rate is low enough.

    Each yielded Document:

    - URI: ``aborist://providence/<cache_key>`` (content-addressed,
      stable across runs, distinguishable from external URIs).
    - title: the question text, truncated to ~120 chars.
    - content: canonical layout ``Q: <question>\\n\\nA: <answer_text>``.
      Standard chunker + Merkle apply via the ingest pipeline.
    - source_type: ``"providence"``.
    """

    source_type: str = "providence"

    def __init__(
        self,
        conn: sqlite3.Connection,
        *,
        kindergarten_seconds: int = DEFAULT_KINDERGARTEN_SECONDS,
        now_seconds: float | None = None,
    ):
        self.conn = conn
        self.kindergarten_seconds = max(0, kindergarten_seconds)
        self._now_seconds = now_seconds  # injectable for tests

    def _now(self) -> float:
        return time.time() if self._now_seconds is None else self._now_seconds

    def iter_documents(self) -> Iterator[Document]:
        cutoff = self._now() - self.kindergarten_seconds
        # Pull only rows that pass all three hard gates. The age check
        # uses created_at (when the record first landed), not last_hit_at,
        # so a recently-hit-but-old record still counts as cooled.
        cursor = self.conn.execute(
            """
            SELECT cache_key, question_text, answer_text
              FROM providence_cache
             WHERE audit_mode = 'STRICT'
               AND falsification_state = 'live'
               AND created_at <= ?
             ORDER BY cache_key ASC
            """,
            (cutoff,),
        )
        for row in cursor:
            cache_key = row["cache_key"] if isinstance(row, sqlite3.Row) else row[0]
            question = row["question_text"] if isinstance(row, sqlite3.Row) else row[1]
            answer = row["answer_text"] if isinstance(row, sqlite3.Row) else row[2]
            if not question or not answer:
                continue
            # Anti-recursion: skip records whose own answer cites a
            # self-reference URI. First-generation only.
            if PROVIDENCE_URI_PREFIX in (answer or ""):
                continue
            uri = f"{PROVIDENCE_URI_PREFIX}{cache_key}"
            title = question[:120].strip()
            content = f"Q: {question}\n\nA: {answer}"
            yield Document(
                uri=uri,
                content=content,
                source_type=self.source_type,
                title=title,
            )
