//! Byte-exact parity tests for `create_signed_overlay_admin_token` vs
//! `@bsv/sdk 1.10.1`'s `pushdrop.lock(...)` output.
//!
//! The deployed TS reference at `nanostore.babbage.systems` +
//! `overlay-us-1.bsvb.tech` runs `@bsv/sdk 1.10.1` + `@bsv/overlay 0.6.0`.
//! `@bsv/overlay-discovery-services/src/WalletAdvertiser.ts:70-92` calls
//! `pushdrop.lock(fields, [2, protocol_name], '1', 'anyone', forSelf=true,
//! includeSignature=true, lockPosition='before')`. Its output is what
//! `SHIPTopicManager` / `SLAPTopicManager` admit.
//!
//! These tests load TS-generated byte sequences from
//! `tests/vectors/overlay_admin_token_ts_parity.json` (regenerated from
//! the `bsv-storage-cloudflare` sibling's
//! `tests/parity/ts-fixtures/generate.mjs`) and assert
//! `create_signed_overlay_admin_token(...)` reproduces them verbatim.
//!
//! # Quality-gate semantics
//!
//! If this test flips red, our TS-parity bytes have drifted and every
//! downstream consumer (rust-overlay's CloudflareAdvertiser and
//! anything built on top) will emit SHIP/SLAP tokens that peer
//! validators reject. Silent federation break. Blocking gate; do not
//! publish a version that fails this test.
//!
//! # Regenerating vectors
//!
//! The vectors are TS-emitted, not Rust-emitted. To update:
//!
//! ```bash
//! cd ~/bsv/bsv-storage-cloudflare/tests/parity/ts-fixtures
//! npm ci               # pinned @bsv/sdk 1.10.1
//! node generate.mjs    # regenerates fixtures/parity/*.json
//! # Then run the bundler shown in the vector file's _source_fixture
//! # and replace tests/vectors/overlay_admin_token_ts_parity.json.
//! ```

#![cfg(feature = "overlay")]
#![allow(clippy::unwrap_used)]

use bsv_rs::overlay::{create_signed_overlay_admin_token, Protocol};
use bsv_rs::primitives::PrivateKey;
use serde::Deserialize;
use std::fs;

#[derive(Debug, Deserialize)]
struct Case {
    description: String,
    protocol: String,
    admin_priv_hex: String,
    domain: String,
    topic_or_service: String,
    expected_locking_script_hex: String,
}

#[derive(Debug, Deserialize)]
struct Vectors {
    cases: Vec<Case>,
}

fn load_vectors() -> Vectors {
    let raw = fs::read_to_string("tests/vectors/overlay_admin_token_ts_parity.json")
        .expect("tests/vectors/overlay_admin_token_ts_parity.json must exist");
    serde_json::from_str(&raw).expect("parity vectors must parse")
}

#[test]
fn create_signed_overlay_admin_token_matches_at_bsv_sdk_1_10_1() {
    let v = load_vectors();
    assert!(
        !v.cases.is_empty(),
        "parity vector file must not be empty — regenerate from ts-fixtures"
    );
    for case in &v.cases {
        let protocol = match case.protocol.as_str() {
            "SHIP" => Protocol::Ship,
            "SLAP" => Protocol::Slap,
            other => panic!("{}: unknown protocol {other}", case.description),
        };
        let root = PrivateKey::from_hex(&case.admin_priv_hex)
            .unwrap_or_else(|e| panic!("{}: invalid admin_priv_hex: {e}", case.description));

        let got = create_signed_overlay_admin_token(
            &root,
            protocol,
            &case.domain,
            &case.topic_or_service,
        )
        .to_hex();

        assert_eq!(
            got, case.expected_locking_script_hex,
            "PARITY DRIFT — case: {}\n  ours : {}\n  theirs: {}\n  fix: our bytes no longer match @bsv/sdk 1.10.1's pushdrop.lock output. Run the bsv-storage-cloudflare ts-fixtures generator and diff to find what changed.",
            case.description, got, case.expected_locking_script_hex
        );
    }
}

/// Determinism guard: two calls with the same inputs must emit the
/// same bytes. RFC-6979 deterministic-k is what makes the parity
/// assertion above meaningful; if signing ever becomes random, the
/// parity test flakes.
#[test]
fn create_signed_overlay_admin_token_is_deterministic_per_input() {
    let v = load_vectors();
    for case in &v.cases {
        let protocol = match case.protocol.as_str() {
            "SHIP" => Protocol::Ship,
            "SLAP" => Protocol::Slap,
            _ => continue,
        };
        let root = PrivateKey::from_hex(&case.admin_priv_hex).unwrap();
        let a = create_signed_overlay_admin_token(
            &root,
            protocol,
            &case.domain,
            &case.topic_or_service,
        );
        let b = create_signed_overlay_admin_token(
            &root,
            protocol,
            &case.domain,
            &case.topic_or_service,
        );
        assert_eq!(
            a.to_hex(),
            b.to_hex(),
            "{}: non-deterministic signing",
            case.description
        );
    }
}
