//! Tests for ChaosNode functionality.

use super::ChaosNode;
use crate::fault_injection::{ClockController, NetworkController};
use std::sync::Arc;

#[tokio::test]
async fn test_chaos_node_creation() {
    let network = Arc::new(NetworkController::new());
    let clock = Arc::new(ClockController::new());

    let node = ChaosNode::new(0, network, clock).expect("create node");

    assert_eq!(node.index, 0);
    assert!(node.is_alive());
    assert_eq!(node.assertion_count(), 0);
}

#[tokio::test]
async fn test_write_and_sync() {
    let network = Arc::new(NetworkController::new());
    let clock = Arc::new(ClockController::new());

    let mut node0 = ChaosNode::new(0, network.clone(), clock.clone()).expect("node 0");
    let mut node1 = ChaosNode::new(1, network, clock).expect("node 1");

    // Write to node 0
    let hash = node0.write_assertion("test:subject", "predicate", 1000).await.expect("write");

    assert_eq!(node0.assertion_count(), 1);
    assert_eq!(node1.assertion_count(), 0);

    // Sync to node 1
    let merged = node1.sync_from(&node0).await.expect("sync");
    assert_eq!(merged, 1);
    assert_eq!(node1.assertion_count(), 1);

    // Same canonical root
    assert_eq!(node0.canonical_merkle_root(), node1.canonical_merkle_root());

    // Hash should be in both
    assert!(node0.leaves().contains(&hash));
    assert!(node1.leaves().contains(&hash));
}

#[tokio::test]
async fn test_partition_blocks_sync() {
    let network = Arc::new(NetworkController::new());
    let clock = Arc::new(ClockController::new());

    let mut node0 = ChaosNode::new(0, network.clone(), clock.clone()).expect("node 0");
    let mut node1 = ChaosNode::new(1, network.clone(), clock).expect("node 1");

    // Write to node 0
    node0.write_assertion("test:subject", "predicate", 1000).await.expect("write");

    // Partition nodes
    network.partition(&[0], &[1]);

    // Sync should be blocked
    let merged = node1.sync_from(&node0).await.expect("sync");
    assert_eq!(merged, 0);
    assert_eq!(node1.assertion_count(), 0);

    // Heal partition
    network.heal();

    // Sync should work now
    let merged = node1.sync_from(&node0).await.expect("sync");
    assert_eq!(merged, 1);
}

#[tokio::test]
async fn test_kill_and_revive() {
    let network = Arc::new(NetworkController::new());
    let clock = Arc::new(ClockController::new());

    let mut node = ChaosNode::new(0, network, clock).expect("node");

    assert!(node.is_alive());

    // Write works when alive
    node.write_assertion("test", "pred", 1000).await.expect("write");

    // Kill the node
    node.kill();
    assert!(!node.is_alive());

    // Write fails when dead
    let result = node.write_assertion("test2", "pred", 2000).await;
    assert!(result.is_err());

    // Revive
    node.revive();
    assert!(node.is_alive());

    // Write works again
    node.write_assertion("test3", "pred", 3000).await.expect("write");
}

#[tokio::test]
async fn test_clock_skew() {
    let network = Arc::new(NetworkController::new());
    let clock = Arc::new(ClockController::new());

    // Inject 5 second skew
    clock.inject_skew(0, 5000);

    let node = ChaosNode::new(0, network, clock).expect("node");

    assert_eq!(node.clock_offset_ms(), 5000);

    // Timestamp should be skewed
    let ts = node.current_hlc();
    assert!(ts.time_ntp64 > 0);
}
