//! Golden vector tests — verify bit-for-bit match with Python implementation.
//!
//! Reads `tests/cross_lang/golden_vectors.json` (generated by
//! `scripts/generate_golden_vectors.py`) and asserts that every Rust output
//! matches the Python reference output.

use serde::Deserialize;
use std::fs;
use std::path::PathBuf;

use attest_core::{compute_chain_hash, compute_claim_id, compute_content_id, normalize_entity_id, tier1_confidence};

#[derive(Deserialize)]
struct GoldenVectors {
    normalization: Vec<NormalizationCase>,
    hashing: Vec<HashingCase>,
    #[serde(default)]
    chain_hash: Vec<ChainHashCase>,
    confidence: Vec<ConfidenceCase>,
}

#[derive(Deserialize)]
struct NormalizationCase {
    input: String,
    expected: String,
    description: String,
}

#[derive(Deserialize)]
struct HashingCase {
    #[serde(rename = "type")]
    hash_type: String,
    args: serde_json::Value,
    expected: String,
    description: String,
}

#[derive(Deserialize)]
struct ChainHashCase {
    prev_chain_hash: String,
    claim_id: String,
    expected: String,
    description: String,
}

#[derive(Deserialize)]
struct ConfidenceCase {
    source_type: String,
    expected: f64,
    description: String,
}

fn load_golden_vectors() -> GoldenVectors {
    // Find the golden vectors file relative to the workspace root
    let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    path.pop(); // attest-core -> rust
    path.pop(); // rust -> project root
    path.push("tests");
    path.push("cross_lang");
    path.push("golden_vectors.json");

    let data = fs::read_to_string(&path)
        .unwrap_or_else(|e| panic!("Failed to read {}: {e}. Run `uv run python scripts/generate_golden_vectors.py` first.", path.display()));

    serde_json::from_str(&data).expect("Failed to parse golden_vectors.json")
}

#[test]
fn test_normalization_golden_vectors() {
    let vectors = load_golden_vectors();
    let mut passed = 0;
    let mut failed = 0;

    for case in &vectors.normalization {
        let result = normalize_entity_id(&case.input);
        if result != case.expected {
            eprintln!(
                "FAIL [{}]: normalize_entity_id({:?}) = {:?}, expected {:?}",
                case.description, case.input, result, case.expected
            );
            failed += 1;
        } else {
            passed += 1;
        }
    }

    assert_eq!(
        failed, 0,
        "{failed} normalization vectors failed out of {} total",
        passed + failed
    );
    eprintln!("All {passed} normalization golden vectors passed");
}

#[test]
fn test_hashing_golden_vectors() {
    let vectors = load_golden_vectors();
    let mut passed = 0;
    let mut failed = 0;

    for case in &vectors.hashing {
        let args = case.args.as_array().expect("args should be an array");
        let result = if case.hash_type == "claim_id" {
            assert_eq!(args.len(), 6, "claim_id needs 6 args: {}", case.description);
            compute_claim_id(
                args[0].as_str().unwrap(),
                args[1].as_str().unwrap(),
                args[2].as_str().unwrap(),
                args[3].as_str().unwrap(),
                args[4].as_str().unwrap(),
                args[5].as_i64().unwrap(),
            )
        } else {
            assert_eq!(args.len(), 3, "content_id needs 3 args: {}", case.description);
            compute_content_id(
                args[0].as_str().unwrap(),
                args[1].as_str().unwrap(),
                args[2].as_str().unwrap(),
            )
        };

        if result != case.expected {
            eprintln!(
                "FAIL [{}]: {}({:?}) = {:?}, expected {:?}",
                case.description, case.hash_type, args, result, case.expected
            );
            failed += 1;
        } else {
            passed += 1;
        }
    }

    assert_eq!(
        failed, 0,
        "{failed} hashing vectors failed out of {} total",
        passed + failed
    );
    eprintln!("All {passed} hashing golden vectors passed");
}

#[test]
fn test_chain_hash_golden_vectors() {
    let vectors = load_golden_vectors();
    let mut passed = 0;
    let mut failed = 0;

    for case in &vectors.chain_hash {
        let result = compute_chain_hash(&case.prev_chain_hash, &case.claim_id);
        if result != case.expected {
            eprintln!(
                "FAIL [{}]: compute_chain_hash({:?}, {:?}) = {:?}, expected {:?}",
                case.description, case.prev_chain_hash, case.claim_id, result, case.expected
            );
            failed += 1;
        } else {
            passed += 1;
        }
    }

    assert_eq!(
        failed, 0,
        "{failed} chain hash vectors failed out of {} total",
        passed + failed
    );
    eprintln!("All {passed} chain hash golden vectors passed");
}

#[test]
fn test_confidence_golden_vectors() {
    let vectors = load_golden_vectors();
    let mut passed = 0;
    let mut failed = 0;

    for case in &vectors.confidence {
        let result = tier1_confidence(&case.source_type);
        if (result - case.expected).abs() > f64::EPSILON {
            eprintln!(
                "FAIL [{}]: tier1_confidence({:?}) = {}, expected {}",
                case.description, case.source_type, result, case.expected
            );
            failed += 1;
        } else {
            passed += 1;
        }
    }

    assert_eq!(
        failed, 0,
        "{failed} confidence vectors failed out of {} total",
        passed + failed
    );
    eprintln!("All {passed} confidence golden vectors passed");
}
