use ed25519_dalek::SigningKey;
use ontoref_commit::{verify_witness, CommitLayer};
use ontoref_types::{
    operation::{OpBody, OpPayload, Operation},
    AttrId, EntityId, Hlc, PublicKey, Value,
};

fn signing_key() -> SigningKey {
    SigningKey::from_bytes(&[11u8; 32])
}

fn assert_op(entity: &str, attrs: Vec<(&str, Value)>, tx: Hlc) -> Operation {
    let key = signing_key();
    let body = OpBody {
        actor: PublicKey::from_signing_key(&key),
        parents: vec![],
        payload: OpPayload::EntityAssert {
            attrs: attrs
                .into_iter()
                .map(|(name, value)| (AttrId::new(name), value))
                .collect(),
            entity: EntityId::new(entity),
            valid_from: None,
        },
        timestamp: tx,
    };
    Operation::sign(body, &key)
}

fn fixture_ops() -> Vec<Operation> {
    vec![
        assert_op(
            "alice",
            vec![
                ("name", Value::Str("Alice".into())),
                ("age", Value::Int(30)),
            ],
            Hlc::new(1, 0),
        ),
        assert_op(
            "bob",
            vec![("name", Value::Str("Bob".into())), ("age", Value::Int(25))],
            Hlc::new(2, 0),
        ),
        assert_op("alice", vec![("age", Value::Int(31))], Hlc::new(3, 0)),
    ]
}

#[test]
fn determinism_same_ops_same_root() {
    let mut a = CommitLayer::new();
    let mut b = CommitLayer::new();
    for op in fixture_ops() {
        a.apply(&op);
        b.apply(&op);
    }
    assert_eq!(a.root(), b.root());
}

#[test]
fn determinism_empty_root_is_fixed() {
    let layer = CommitLayer::new();
    let expected = *blake3::hash(b"empty-tree-v1").as_bytes();
    assert_eq!(layer.root(), expected);
}

#[test]
fn determinism_root_changes_with_value_overwrite() {
    let mut layer = CommitLayer::new();
    layer.apply(&assert_op(
        "e",
        vec![("k", Value::Str("v1".into()))],
        Hlc::new(1, 0),
    ));
    let r1 = layer.root();
    layer.apply(&assert_op(
        "e",
        vec![("k", Value::Str("v2".into()))],
        Hlc::new(2, 0),
    ));
    let r2 = layer.root();
    assert_ne!(
        r1, r2,
        "root must change when an existing cell's value changes"
    );
}

#[test]
fn witness_verify_succeeds_for_present_cell() {
    let mut layer = CommitLayer::new();
    for op in fixture_ops() {
        layer.apply(&op);
    }
    let root = layer.root();

    let witness = layer
        .witness(&EntityId::new("alice"), &AttrId::new("name"))
        .unwrap();
    assert!(verify_witness(&witness, &root));
}

#[test]
fn witness_verify_succeeds_after_overwrite() {
    let mut layer = CommitLayer::new();
    for op in fixture_ops() {
        layer.apply(&op);
    }
    let root = layer.root();

    // alice's age was overwritten from 30 to 31; the witness should reflect 31.
    let witness = layer
        .witness(&EntityId::new("alice"), &AttrId::new("age"))
        .unwrap();
    assert!(verify_witness(&witness, &root));

    // The witnessed value should be the canonical encoding of Int(31).
    let expected = ontoref_types::canonical::encode(&Value::Int(31)).unwrap();
    assert_eq!(witness.value, expected);
}

#[test]
fn witness_verify_fails_for_tampered_value() {
    let mut layer = CommitLayer::new();
    for op in fixture_ops() {
        layer.apply(&op);
    }
    let root = layer.root();

    let mut witness = layer
        .witness(&EntityId::new("alice"), &AttrId::new("name"))
        .unwrap();
    witness.value[0] ^= 0x01;
    assert!(!verify_witness(&witness, &root));
}

#[test]
fn witness_verify_fails_for_tampered_path() {
    let mut layer = CommitLayer::new();
    for op in fixture_ops() {
        layer.apply(&op);
    }
    let root = layer.root();

    let mut witness = layer
        .witness(&EntityId::new("alice"), &AttrId::new("name"))
        .unwrap();
    if !witness.path.is_empty() {
        witness.path[0].sibling[0] ^= 0x01;
    } else {
        // For a 1-cell tree, witness.path is empty; force the tree larger so
        // the test exercises tampering at a real path step.
        panic!("fixture must produce a non-trivial Merkle path");
    }
    assert!(!verify_witness(&witness, &root));
}

#[test]
fn witness_verify_fails_against_wrong_root() {
    let mut layer = CommitLayer::new();
    for op in fixture_ops() {
        layer.apply(&op);
    }

    let witness = layer
        .witness(&EntityId::new("alice"), &AttrId::new("name"))
        .unwrap();
    let bogus_root = [0xFFu8; 32];
    assert!(!verify_witness(&witness, &bogus_root));
}

#[test]
fn cross_instance_determinism_with_independent_apply_order_per_entity() {
    // Two instances apply ops in the same logical order; their state roots
    // must be byte-identical.
    let ops = fixture_ops();

    let mut left = CommitLayer::new();
    for op in &ops {
        left.apply(op);
    }

    let mut right = CommitLayer::new();
    for op in &ops {
        right.apply(op);
    }

    assert_eq!(left.root(), right.root());
    assert_eq!(left.len(), right.len());
}

#[test]
fn cross_instance_witness_verifies_under_other_instance_root() {
    // A witness built on instance A verifies against instance B's root,
    // because both roots are identical.
    let ops = fixture_ops();

    let mut left = CommitLayer::new();
    let mut right = CommitLayer::new();
    for op in &ops {
        left.apply(op);
        right.apply(op);
    }

    let witness = left
        .witness(&EntityId::new("bob"), &AttrId::new("name"))
        .unwrap();
    let right_root = right.root();
    assert!(verify_witness(&witness, &right_root));
}

// ── ADR-041 / bl-029 29b: per-cell typed CRDT merge ──────────────────────────

#[test]
fn union_cell_accumulates_and_is_order_independent() {
    use ontoref_commit::{CellMergeKind, MergePolicy};

    // The "tags" attribute folds as a grow-only set (GSet).
    let policy = MergePolicy::new().with("tags", CellMergeKind::Union);

    // Two actors assert different tag elements on the same cell. Applied in
    // either order, the cell must hold BOTH and hash to the same root.
    let a = assert_op(
        "doc",
        vec![("tags", Value::Str("red".into()))],
        Hlc::new(1, 0),
    );
    let b = assert_op(
        "doc",
        vec![("tags", Value::Str("blue".into()))],
        Hlc::new(2, 0),
    );

    let mut forward = CommitLayer::new().with_policy(policy.clone());
    forward.apply(&a);
    forward.apply(&b);

    let mut reverse = CommitLayer::new().with_policy(policy.clone());
    reverse.apply(&b);
    reverse.apply(&a);

    assert_eq!(
        forward.root(),
        reverse.root(),
        "union merge is order-independent — actors converge regardless of arrival order"
    );

    // The union root differs from either single-element assertion: both
    // elements are retained, not last-writer-wins.
    let mut only_a = CommitLayer::new().with_policy(policy.clone());
    only_a.apply(&a);
    assert_ne!(
        forward.root(),
        only_a.root(),
        "the second actor's element is retained, not overwritten"
    );
}

#[test]
fn lww_is_unchanged_without_a_policy() {
    // Default (empty) policy reproduces last-writer-wins: the later (higher
    // HLC, applied last) assertion wins the cell.
    let a = assert_op("e", vec![("v", Value::Int(1))], Hlc::new(1, 0));
    let b = assert_op("e", vec![("v", Value::Int(2))], Hlc::new(2, 0));

    let mut layer = CommitLayer::new();
    layer.apply(&a);
    layer.apply(&b);

    // Equals a layer that only ever saw the winning (b) assertion.
    let mut only_b = CommitLayer::new();
    only_b.apply(&b);
    assert_eq!(
        layer.root(),
        only_b.root(),
        "default policy stays last-writer-wins"
    );
}
