"""Read-only canonical-current-revision adapter for Personal Intelligence."""

from __future__ import annotations

import sqlite3
from pathlib import Path

from mountain_twin.activity.persistence import deserialize_activity_snapshot
from mountain_twin.activity.persistence.contracts import ActivityRevision

from .contracts import PersonalIntelligenceInput
from .engine import freeze_current_revisions


class StrictReadUnavailableError(RuntimeError):
    """Raised when immutable SQLite reading could omit committed WAL state."""


class ReadOnlyCurrentRevisionAdapter:
    """Reads current canonical revisions without migrations or durable writes.

    ``strict=True`` is an explicit diagnostic-only promise that the database is
    frozen for the lifetime of the connection.  It uses SQLite's immutable URI
    option, which avoids WAL shared-memory writes, but cannot safely observe
    committed frames that remain in a non-empty WAL.  Strict mode consequently
    rejects such a database instead of returning a potentially stale snapshot.
    """

    def __init__(self, database_path: str | Path, *, strict: bool = False):
        path = Path(database_path).resolve()
        if strict:
            self._require_checkpointed_wal(path)
        query = "mode=ro&immutable=1" if strict else "mode=ro"
        self._connection = sqlite3.connect(f"{path.as_uri()}?{query}", uri=True)
        self._connection.row_factory = sqlite3.Row
        self._connection.execute("PRAGMA query_only = ON")

    @staticmethod
    def _require_checkpointed_wal(path: Path) -> None:
        """Fail closed when immutable SQLite would not include WAL frames."""

        wal_path = path.with_name(f"{path.name}-wal")
        if wal_path.exists() and wal_path.stat().st_size != 0:
            raise StrictReadUnavailableError(
                "strict Personal Intelligence reads require an absent or empty SQLite WAL"
            )

    def close(self) -> None:
        self._connection.close()

    def __enter__(self) -> ReadOnlyCurrentRevisionAdapter:
        return self

    def __exit__(self, _type, _value, _traceback) -> None:
        self.close()

    def freeze_current_revisions(
        self, *, user_id: str, as_of, taxonomy_version: str
    ) -> PersonalIntelligenceInput:
        rows = self._connection.execute(
            """SELECT revision.*
               FROM activity_entities AS entity
               JOIN activity_revisions AS revision
                 ON revision.activity_revision_id = entity.current_revision_id
              WHERE entity.user_id = ?
              ORDER BY entity.activity_entity_id, revision.activity_revision_id""",
            (user_id,),
        ).fetchall()
        revisions = tuple(
            ActivityRevision(
                row["activity_revision_id"],
                row["activity_entity_id"],
                deserialize_activity_snapshot(row["canonical_activity_json"]),
                row["normalizer_version"],
                row["taxonomy_version"],
                row["normalized_at"],
            )
            for row in rows
        )
        return freeze_current_revisions(
            revisions, user_id=user_id, as_of=as_of, taxonomy_version=taxonomy_version
        )
