"""Build a SelfModel from current store state.

The snapshot reads:

- ``governance_policy_hash`` — from the most recent
  ``governance_policy_set`` audit event, or ``"unset"`` if absent.
- ``model_profile_hash`` — from the most recent
  ``providence_query`` audit event body's
  ``model_profile_hash`` field, or ``"unset"`` if no queries have
  run yet.
- ``verifier_method_root`` — derived from the set of distinct
  ``verifier_method`` values that appear on live ``providence_cache``
  rows; SHA-256 over the canonical-JSON sorted list.
- ``canonicalization_version`` / ``chunking_version`` — from the
  most recent ``providence_cache`` row, falling back to the v9.8
  defaults declared in the SelfModel canonical schema.

This module deliberately keeps the snapshotter side-effect-free:
returns a :class:`SelfModel`. Persistence is the caller's job
(via :func:`arborist.selfmodel.store_snapshot`).
"""

from __future__ import annotations

import hashlib
import json
import sqlite3
from typing import Optional

from arborist.selfmodel.canonical import SCHEMA_VERSION, SelfModel


_DEFAULT_CANONICALIZATION = "norm-v1"
_DEFAULT_CHUNKING = "tok-512-v1"


def _verifier_method_root(conn: sqlite3.Connection) -> str:
    rows = conn.execute(
        "SELECT DISTINCT verifier_method FROM providence_cache "
        "WHERE falsification_state = 'live' AND verifier_method IS NOT NULL"
    ).fetchall()
    methods = sorted({r["verifier_method"] for r in rows})
    payload = json.dumps(
        {"verifier_methods": methods},
        sort_keys=True,
        separators=(",", ":"),
        ensure_ascii=False,
    )
    return hashlib.sha256(
        payload.encode("utf-8", errors="surrogatepass")
    ).hexdigest()


def _latest_provenance_field(
    conn: sqlite3.Connection, field: str
) -> Optional[str]:
    row = conn.execute(
        f"SELECT {field} FROM providence_cache "
        "ORDER BY created_at DESC LIMIT 1"
    ).fetchone()
    if row is None:
        return None
    return row[field]


def _latest_governance_policy_hash(
    conn: sqlite3.Connection,
) -> Optional[str]:
    row = conn.execute(
        "SELECT governance_policy_hash FROM providence_cache "
        "ORDER BY created_at DESC LIMIT 1"
    ).fetchone()
    if row is None:
        return None
    return row["governance_policy_hash"]


def _latest_model_profile_hash(
    conn: sqlite3.Connection,
) -> Optional[str]:
    row = conn.execute(
        "SELECT model_profile_hash FROM providence_cache "
        "ORDER BY created_at DESC LIMIT 1"
    ).fetchone()
    if row is None:
        return None
    return row["model_profile_hash"]


def _parent_selfmodel_root(conn: sqlite3.Connection) -> Optional[str]:
    row = conn.execute(
        "SELECT selfmodel_root FROM selfmodel_records "
        "WHERE state = 'live' "
        "ORDER BY created_at DESC LIMIT 1"
    ).fetchone()
    if row is None:
        return None
    return row["selfmodel_root"]


def _latest_memory_root(conn: sqlite3.Connection) -> Optional[str]:
    """Read the most recent live memory_root, if memory_records exists."""
    has_table = conn.execute(
        "SELECT name FROM sqlite_master "
        "WHERE type='table' AND name='memory_records'"
    ).fetchone()
    if has_table is None:
        return None
    row = conn.execute(
        "SELECT memory_root FROM memory_records "
        "WHERE state = 'live' "
        "ORDER BY created_at DESC LIMIT 1"
    ).fetchone()
    if row is None:
        return None
    return row["memory_root"]


def snapshot(
    conn: sqlite3.Connection,
    *,
    accepted_patch_root: Optional[str] = None,
    rejected_patch_root: Optional[str] = None,
) -> SelfModel:
    """Build a :class:`SelfModel` from current store state.

    Patch roots default to ``None`` (no patches applied) — operators
    pass them in once a patch ledger lands. Capability claims are
    attached separately via :func:`arborist.selfmodel.canonical.with_claims`
    before storing.
    """
    parent = _parent_selfmodel_root(conn)
    governance = _latest_governance_policy_hash(conn) or "unset"
    model_profile = _latest_model_profile_hash(conn) or "unset"
    verifier = _verifier_method_root(conn)
    canonicalization = (
        _latest_provenance_field(conn, "canonicalization_version")
        or _DEFAULT_CANONICALIZATION
    )
    chunking = (
        _latest_provenance_field(conn, "chunking_version")
        or _DEFAULT_CHUNKING
    )
    memory = _latest_memory_root(conn)

    return SelfModel(
        schema_version=SCHEMA_VERSION,
        parent_selfmodel_root=parent,
        model_profile_hash=model_profile,
        verifier_method_root=verifier,
        governance_policy_hash=governance,
        canonicalization_version=canonicalization,
        chunking_version=chunking,
        accepted_patch_root=accepted_patch_root,
        rejected_patch_root=rejected_patch_root,
        memory_root=memory,
    )
