//! Binary-Merkle commitment backend (default `CommitmentBackend` impl).
//!
//! Maintains a `BTreeMap<CellKey, Vec<u8>>` of the latest assertion per cell
//! (latest by application order — callers must apply ops in HLC order to keep
//! the resulting state root deterministic across instances). Roots and
//! witnesses are derived on demand from the current state. The state root
//! is computed as a binary Merkle tree over the sorted set of
//! `(cell_key, encoded_value)` pairs.
//!
//! Previously exported as `CommitLayer`; the legacy alias is preserved in
//! `lib.rs` for backwards compatibility.

use std::collections::{BTreeMap, BTreeSet};

use ontoref_types::{canonical, operation::OpPayload, AttrId, EntityId, Operation};

use crate::error::{Error, Result};
use crate::tree::{self, CellKey, StateRoot, Witness};
use crate::CommitmentBackend;

/// Per-cell CRDT merge semantics (ADR-041). The default `Lww` preserves the
/// original last-writer-wins-by-HLC behaviour exactly; the others fold a
/// cell's concurrent writes commutatively, so the resulting state root is
/// independent of arrival order across actors.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum CellMergeKind {
    /// Last assertion in HLC-order replay wins (overwrite). Default.
    #[default]
    Lww,
    /// Grow-only set (GSet, ADR-027): the cell holds the union of every
    /// asserted element. Commutative, associative, idempotent.
    Union,
    /// Text merge (TextMerge, ADR-027): keep the longer value — a simple
    /// proxy for "more edited". Commutative, associative, idempotent.
    MaxLen,
}

/// Maps an attribute (the domain key) to its [`CellMergeKind`]. An attribute
/// with no entry folds as `Lww`, so a policy left empty reproduces the
/// pre-ADR-041 behaviour and every prior state root.
#[derive(Clone, Debug, Default)]
pub struct MergePolicy {
    by_attr: BTreeMap<String, CellMergeKind>,
}

impl MergePolicy {
    /// Empty policy — every cell folds last-writer-wins.
    pub fn new() -> Self {
        Self::default()
    }

    /// Declare `attr`'s merge kind (builder style).
    pub fn with(mut self, attr: impl Into<String>, kind: CellMergeKind) -> Self {
        self.by_attr.insert(attr.into(), kind);
        self
    }

    /// Merge kind for `attr` — `Lww` when undeclared.
    pub fn kind_for(&self, attr: &AttrId) -> CellMergeKind {
        self.by_attr.get(attr.as_str()).copied().unwrap_or_default()
    }
}

/// Fold an asserted `element` into a `Union` cell: decode the existing set
/// (empty when absent), insert the element, re-encode. Deterministic — the
/// `BTreeSet` is sorted, so the bytes (and thus the root) are independent of
/// insertion order.
fn union_merge(existing: Option<&Vec<u8>>, element: &[u8]) -> Vec<u8> {
    let mut set: BTreeSet<Vec<u8>> = existing
        .and_then(|b| canonical::decode::<BTreeSet<Vec<u8>>>(b).ok())
        .unwrap_or_default();
    set.insert(element.to_vec());
    canonical::encode(&set).expect("canonical encoding never fails for owned types")
}

/// Hand-rolled binary-Merkle commitment backend — the default impl of
/// [`CommitmentBackend`].
#[derive(Default)]
pub struct BinaryMerkle {
    cells: BTreeMap<CellKey, Vec<u8>>,
    policy: MergePolicy,
}

impl BinaryMerkle {
    /// Empty commit layer.
    pub fn new() -> Self {
        Self::default()
    }

    /// Borrow the raw cell map — `(entity,attr)` cell key → canonical value
    /// bytes. Used to serialise a durable snapshot (bl-015) so a restart can
    /// restore the layer without replaying the whole oplog.
    pub fn cells(&self) -> &BTreeMap<CellKey, Vec<u8>> {
        &self.cells
    }

    /// Rebuild a commit layer directly from a previously-snapshotted cell
    /// map, skipping the replay that produced them. The caller is
    /// responsible for replaying only the operations appended after the
    /// snapshot boundary.
    pub fn from_cells(cells: BTreeMap<CellKey, Vec<u8>>) -> Self {
        Self {
            cells,
            policy: MergePolicy::default(),
        }
    }

    /// Attach a [`MergePolicy`] so cells fold by their attribute's declared
    /// CRDT kind (ADR-041). Without this the layer folds everything `Lww`,
    /// identical to the pre-ADR-041 behaviour.
    pub fn with_policy(mut self, policy: MergePolicy) -> Self {
        self.policy = policy;
        self
    }

    /// Apply an operation. `EntityAssert` writes one cell per (attr, value)
    /// pair, overwriting any prior value; `Retract` removes the cell for
    /// each named attribute (ADR-041); all other payloads contribute
    /// nothing. Applied in HLC order, an assert and a retract to the same
    /// cell resolve last-writer-wins by HLC.
    pub fn apply(&mut self, op: &Operation) {
        match &op.body.payload {
            OpPayload::EntityAssert { entity, attrs, .. } => {
                for (attr, value) in attrs {
                    let key = tree::cell_key(entity, attr);
                    let incoming = tree::encode_value(value);
                    let merged = match self.policy.kind_for(attr) {
                        CellMergeKind::Lww => incoming,
                        CellMergeKind::Union => union_merge(self.cells.get(&key), &incoming),
                        CellMergeKind::MaxLen => match self.cells.get(&key) {
                            Some(prev) if prev.len() >= incoming.len() => prev.clone(),
                            _ => incoming,
                        },
                    };
                    self.cells.insert(key, merged);
                }
            }
            OpPayload::Retract { entity, attrs } => {
                // Removal is defined for Lww cells (drop the cell). Element-
                // level removal for Union (OR-Set tags) is a later 29b slice;
                // here a retract drops the whole cell regardless of kind.
                for attr in attrs {
                    self.cells.remove(&tree::cell_key(entity, attr));
                }
            }
            _ => {}
        }
    }

    /// Number of cells currently committed.
    pub fn len(&self) -> usize {
        self.cells.len()
    }

    /// Whether the commit layer is empty.
    pub fn is_empty(&self) -> bool {
        self.cells.is_empty()
    }

    /// Current state root.
    pub fn root(&self) -> StateRoot {
        let entries = self.entries();
        tree::merkle_root(&entries)
    }

    /// Build an inclusion proof for the cell `(entity, attr)`.
    pub fn witness(&self, entity: &EntityId, attr: &AttrId) -> Result<Witness> {
        let key = tree::cell_key(entity, attr);
        let entries = self.entries();
        tree::build_witness(&entries, &key).ok_or(Error::CellNotFound)
    }

    fn entries(&self) -> Vec<(CellKey, &[u8])> {
        self.cells.iter().map(|(k, v)| (*k, v.as_slice())).collect()
    }
}

impl CommitmentBackend for BinaryMerkle {
    fn apply(&mut self, op: &Operation) {
        BinaryMerkle::apply(self, op)
    }

    fn root(&self) -> StateRoot {
        BinaryMerkle::root(self)
    }

    fn witness(&self, entity: &EntityId, attr: &AttrId) -> Result<Witness> {
        BinaryMerkle::witness(self, entity, attr)
    }

    fn verify_witness(witness: &Witness, root: &StateRoot) -> bool {
        tree::verify_witness(witness, root)
    }
}
