//! UC-16e integration tests — `#[derive(Artifact)]` end-to-end.
//!
//! The unit tests in `core/sdk-derive/src/artifact.rs::tests` cover
//! the proc-macro's canonicalization shape (which strings produce
//! which hashes). This file covers the integration: the generated
//! `impl Artifact` actually works with `save` / `load`, and the
//! hash is in fact different from a hand-maintained one for the same
//! shape (sanity-check that the derive is doing something).

use maat_sdk_derive::Artifact as ArtifactDerive;
use maat_sdk::artifact::{load, save, Artifact, LoadOutcome};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ArtifactDerive)]
struct UC16eSimple {
    id: u64,
    name: String,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ArtifactDerive)]
struct UC16eWithDefault {
    id: u64,
    name: String,
    #[serde(default)]
    extra: u32,
}

#[test]
fn uc_16e_simple_struct_compiles_and_has_blake3_hash() {
    // Should be a 64-char hex string (Blake3 32-byte digest).
    let hash = <UC16eSimple as Artifact>::SCHEMA_HASH;
    assert_eq!(hash.len(), 64, "Blake3 hex should be 64 chars; got {}", hash.len());
    // Hex characters only.
    assert!(hash.chars().all(|c| c.is_ascii_hexdigit()));
}

#[test]
fn uc_16e_derived_round_trip() {
    let v = UC16eSimple { id: 7, name: "alice".into() };
    let text = save(&v).unwrap();
    let back: UC16eSimple = load(&text).unwrap();
    assert_eq!(back, v);
}

#[test]
fn uc_16e_load_with_outcome_reports_direct_match() {
    use maat_sdk::artifact::load_with_outcome;
    let v = UC16eSimple { id: 1, name: "a".into() };
    let text = save(&v).unwrap();
    let (back, outcome) = load_with_outcome::<UC16eSimple>(&text).unwrap();
    assert_eq!(back, v);
    assert_eq!(outcome, LoadOutcome::DirectMatch);
}

#[test]
fn uc_16e_hash_stable_across_compilations() {
    // The macro computes the hash at proc-macro expansion. Recompiling
    // the same source must produce the same hash; this test simply
    // asserts the constant is stable within one binary, which is
    // the necessary (but not sufficient) condition.
    let h1 = <UC16eSimple as Artifact>::SCHEMA_HASH;
    let h2 = <UC16eSimple as Artifact>::SCHEMA_HASH;
    assert_eq!(h1, h2);
}

#[test]
fn uc_16e_different_shapes_have_different_hashes() {
    let simple = <UC16eSimple as Artifact>::SCHEMA_HASH;
    let with_default = <UC16eWithDefault as Artifact>::SCHEMA_HASH;
    assert_ne!(simple, with_default, "different shapes must hash differently");
}

#[test]
fn uc_16e_load_serde_defaults_path_via_synthetic_other_hash() {
    // Save a UC16eSimple. Then "rename" the type at load time by
    // pretending the bytes belong to UC16eWithDefault — same shape
    // serde-wise (id + name), with `extra` defaulted.
    let v = UC16eSimple { id: 9, name: "b".into() };
    let _text = save(&v).unwrap();
    // Hand-rewrite the schema_hash field in the JSON so load sees a
    // mismatch and exercises the serde-defaults fallback (or the
    // serde-defaults path within the WithDefault deserialize).
    //
    // Easier: build a Stamped<UC16eWithDefault>-shaped envelope
    // where the stamped hash is UC16eSimple's hash but the data is
    // serde-compatible. With `extra` serde(default)'d, the load
    // succeeds via the fallback.
    let stamped_text = format!(
        r#"{{"schema_hash":"{}","data":{{"id":9,"name":"b"}}}}"#,
        <UC16eSimple as Artifact>::SCHEMA_HASH
    );
    let back: UC16eWithDefault = load(&stamped_text).unwrap();
    assert_eq!(back, UC16eWithDefault { id: 9, name: "b".into(), extra: 0 });
}

// Tuple struct support.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ArtifactDerive)]
#[serde(transparent)]
struct UC16eTuple(Vec<u32>);

#[test]
fn uc_16e_tuple_struct_round_trips() {
    let v = UC16eTuple(vec![1, 2, 3]);
    let text = save(&v).unwrap();
    let back: UC16eTuple = load(&text).unwrap();
    assert_eq!(back, v);
}

// Unit struct support.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ArtifactDerive)]
struct UC16eUnit;

#[test]
fn uc_16e_unit_struct_compiles_and_has_hash() {
    let hash = <UC16eUnit as Artifact>::SCHEMA_HASH;
    assert_eq!(hash.len(), 64);
}
