"""Provenance ledger.

A memory system must be able to show *why* it returned a given memory. This
module keeps an append-only mapping from memory id -> origin observation and
any later "rewrites" (consolidations). It never deletes an original entry, so
every answer is traceable to the earliest recorded fact.

This is the "evidence ledger" idea applied to an agent's own memory: no silent
editing, no fabricated sources.
"""

from __future__ import annotations

import time
from typing import Any, Dict, List, Optional


class ProvenanceLedger:
    def __init__(self) -> None:
        # id -> origin record
        self._origins: Dict[str, Dict[str, Any]] = {}
        # id -> list of {from_id, at, reason} consolidations
        self._derived: Dict[str, List[Dict[str, Any]]] = {}

    def record_origin(
        self,
        memory_id: str,
        source: str,
        observation_ts: Optional[float] = None,
        extra: Optional[Dict[str, Any]] = None,
    ) -> None:
        self._origins[memory_id] = {
            "memory_id": memory_id,
            "source": source,
            "observed_at": observation_ts or time.time(),
            "extra": extra or {},
        }

    def origin(self, memory_id: str) -> Optional[Dict[str, Any]]:
        return self._origins.get(memory_id)

    def record_consolidation(
        self,
        derived_id: str,
        from_ids: List[str],
        reason: str = "",
    ) -> None:
        self._derived.setdefault(derived_id, []).append(
            {"derived_from": list(from_ids), "reason": reason, "at": time.time()}
        )

    def lineage(self, memory_id: str) -> Dict[str, Any]:
        """Return the full provenance chain for a memory id."""
        origin = self._origins.get(memory_id)
        derived = self._derived.get(memory_id, [])
        # walk parents of the earliest origin if this is itself derived
        parents = []
        for rec in derived:
            for pid in rec.get("derived_from", []):
                po = self._origins.get(pid)
                parents.append({"id": pid, "origin": po})
        return {
            "memory_id": memory_id,
            "origin": origin,
            "derived_from": parents,
            "consolidations": derived,
        }

    def entries(self) -> List[Dict[str, Any]]:
        return [
            {"memory_id": mid, "origin": self._origins[mid]}
            for mid in sorted(self._origins)
        ]