#![cfg(feature = "hot-reload")]

//! Hot-reload integration: write a YAML approver file, mutate it, and
//! assert the registry picks up the newly-added key within 5 seconds.

use ed25519_dalek::SigningKey;
use klieo_ops::approver_registry::ApproverId;
use klieo_ops::key_registry::{Ed25519KeyRegistry, HotReloadableEd25519Registry};
use std::time::{Duration, Instant};

fn vk_b64(sk: &SigningKey) -> String {
    use base64::Engine as _;
    base64::engine::general_purpose::STANDARD.encode(sk.verifying_key().to_bytes())
}

#[tokio::test]
async fn yaml_mutation_triggers_reload() {
    let dir = tempfile::tempdir().expect("tempdir");
    let path = dir.path().join("approvers.yaml");

    let alice = SigningKey::from_bytes(&[1u8; 32]);
    std::fs::write(&path, format!("alice: \"{}\"\n", vk_b64(&alice))).expect("initial write");

    let registry = HotReloadableEd25519Registry::<ApproverId>::watch_yaml(&path, ApproverId)
        .expect("watch_yaml");

    // Initial state: alice present, bob absent.
    assert_eq!(
        registry
            .lookup(&ApproverId("alice".into()))
            .map(|v| v.to_bytes()),
        Some(alice.verifying_key().to_bytes()),
        "alice must resolve after initial load"
    );
    assert!(
        registry.lookup(&ApproverId("bob".into())).is_none(),
        "bob must be absent before mutation"
    );

    // Mutate: add bob.
    let bob = SigningKey::from_bytes(&[2u8; 32]);
    std::fs::write(
        &path,
        format!("alice: \"{}\"\nbob: \"{}\"\n", vk_b64(&alice), vk_b64(&bob),),
    )
    .expect("rewrite");

    // Poll for reload (debounce ~500ms + watcher latency); cap at 5s.
    let deadline = Instant::now() + Duration::from_secs(5);
    while Instant::now() < deadline {
        if registry.lookup(&ApproverId("bob".into())).is_some() {
            break;
        }
        tokio::time::sleep(Duration::from_millis(100)).await;
    }

    assert_eq!(
        registry
            .lookup(&ApproverId("bob".into()))
            .map(|v| v.to_bytes()),
        Some(bob.verifying_key().to_bytes()),
        "hot-reload must pick up newly-added approver within 5s"
    );

    // Alice's key must remain correct after the reload.
    assert_eq!(
        registry
            .lookup(&ApproverId("alice".into()))
            .map(|v| v.to_bytes()),
        Some(alice.verifying_key().to_bytes()),
        "alice's key must be stable across reload"
    );
}

#[tokio::test]
async fn parse_error_preserves_previous_snapshot() {
    let dir = tempfile::tempdir().expect("tempdir");
    let path = dir.path().join("approvers.yaml");

    let alice = SigningKey::from_bytes(&[3u8; 32]);
    std::fs::write(&path, format!("alice: \"{}\"\n", vk_b64(&alice))).expect("initial write");

    let registry = HotReloadableEd25519Registry::<ApproverId>::watch_yaml(&path, ApproverId)
        .expect("watch_yaml");

    // Confirm initial state.
    assert!(
        registry.lookup(&ApproverId("alice".into())).is_some(),
        "alice must resolve before corruption"
    );

    // Overwrite with malformed YAML — the reload must silently retain the
    // previous snapshot rather than swapping in an empty or partial map.
    std::fs::write(&path, "not: valid: yaml: ::\n").expect("corrupt write");

    // Wait well past the debounce window (500ms) for the watcher to fire.
    tokio::time::sleep(Duration::from_millis(1200)).await;

    // Alice must still resolve — the registry kept the previous snapshot.
    assert!(
        registry.lookup(&ApproverId("alice".into())).is_some(),
        "alice must still resolve after a parse-error reload attempt"
    );
}
