#![allow(
    clippy::too_many_lines,
    clippy::cast_precision_loss,
    clippy::cast_sign_loss,
    clippy::missing_const_for_fn
)]

use std::{sync::Arc, time::Duration};

use tidaldb::{
    replication::ShardId,
    wal::{SignalEvent, WalConfig, WalHandle, segment},
};

fn test_config(dir: &std::path::Path) -> WalConfig {
    WalConfig {
        wal_dir_override: None,
        dir: dir.to_path_buf(),
        segment_size: 16 * 1024 * 1024,
        batch_size: 100,
        batch_timeout: Duration::from_millis(1),
        dedup_window: Duration::from_secs(30),
    }
}

fn make_event(id: u64) -> SignalEvent {
    SignalEvent {
        entity_id: id,
        signal_type: 1,
        weight: 1.0,
        timestamp_nanos: id * 1_000_000_000,
    }
}

// -- AC-1, AC-2: Wire format byte-level tests are in format.rs unit tests.
// These integration tests validate the full pipeline.

#[test]
fn wal_basic_round_trip() {
    let dir = tempfile::tempdir().expect("tempdir creation should succeed");
    let config = test_config(dir.path());

    // Write events
    let (handle, replayed, _) = WalHandle::open(config).expect("open should succeed");
    assert!(replayed.is_empty());

    for i in 1..=10 {
        handle.append(make_event(i)).expect("append should succeed");
    }
    handle.shutdown().expect("shutdown should succeed");

    // Reopen and verify replay
    let config = test_config(dir.path());
    let (handle, replayed, _) = WalHandle::open(config).expect("reopen should succeed");
    assert_eq!(replayed.len(), 10);
    for (i, event) in replayed.iter().enumerate() {
        assert_eq!(event.entity_id, (i + 1) as u64);
        assert_eq!(event.signal_type, 1);
        assert_eq!(event.weight.to_bits(), 1.0_f32.to_bits());
    }
    handle.shutdown().expect("shutdown should succeed");
}

// -- AC-10, AC-11: Deduplication
#[test]
fn wal_dedup_silent() {
    let dir = tempfile::tempdir().expect("tempdir creation should succeed");
    let config = test_config(dir.path());

    let (handle, _, _) = WalHandle::open(config).expect("open should succeed");

    let event = make_event(42);
    let seq1 = handle
        .append(event.clone())
        .expect("first append should succeed");
    let seq2 = handle
        .append(event.clone())
        .expect("second append should succeed");
    let seq3 = handle.append(event).expect("third append should succeed");

    assert!(seq1 > 0, "first event should get real sequence number");
    assert_eq!(seq2, 0, "duplicate should return seq=0");
    assert_eq!(seq3, 0, "duplicate should return seq=0");

    handle.shutdown().expect("shutdown should succeed");

    // Verify only one event on disk
    let config = test_config(dir.path());
    let (handle, replayed, _) = WalHandle::open(config).expect("reopen should succeed");
    assert_eq!(replayed.len(), 1, "only one unique event should be on disk");
    handle.shutdown().expect("shutdown should succeed");
}

// -- AC-12: No false positives
#[test]
fn wal_dedup_no_false_positives() {
    let dir = tempfile::tempdir().expect("tempdir creation should succeed");
    // Use a large batch size so batches fill quickly from concurrent writers.
    let config = WalConfig {
        wal_dir_override: None,
        dir: dir.path().to_path_buf(),
        segment_size: 16 * 1024 * 1024,
        batch_size: 256,
        batch_timeout: Duration::from_millis(1),
        dedup_window: Duration::from_secs(60),
    };

    let (handle, _, _) = WalHandle::open(config).expect("open should succeed");
    let handle = Arc::new(handle);

    let total_events: u64 = 1_000;
    let num_threads = 10u64;
    let per_thread = total_events / num_threads;

    let mut threads = Vec::new();
    for t in 0..num_threads {
        let handle = Arc::clone(&handle);
        threads.push(std::thread::spawn(move || {
            let mut count = 0u64;
            for i in 0..per_thread {
                let entity_id = t * per_thread + i;
                let event = SignalEvent {
                    entity_id,
                    signal_type: (entity_id % 256) as u8,
                    weight: entity_id as f32,
                    timestamp_nanos: entity_id * 1_000_000,
                };
                let seq = handle.append(event).expect("append should succeed");
                if seq > 0 {
                    count += 1;
                }
            }
            count
        }));
    }

    let mut real_seqs = 0u64;
    for thread in threads {
        real_seqs += thread.join().expect("thread should join");
    }

    let handle = Arc::try_unwrap(handle).expect("should be sole owner of WalHandle Arc");
    handle.shutdown().expect("shutdown should succeed");

    assert_eq!(
        real_seqs, total_events,
        "all {total_events} unique events must be accepted (no false positives)"
    );
}

// -- AC-5, AC-6: Segment rotation
#[test]
fn wal_segment_rotation() {
    let dir = tempfile::tempdir().expect("tempdir creation should succeed");
    // Use very small segment size to force rotation
    let config = WalConfig {
        wal_dir_override: None,
        dir: dir.path().to_path_buf(),
        segment_size: 256, // tiny: one batch exceeds this
        batch_size: 10,
        batch_timeout: Duration::from_millis(10),
        dedup_window: Duration::from_secs(30),
    };

    let (handle, _, _) = WalHandle::open(config).expect("open should succeed");

    // Write enough events to trigger multiple rotations
    for i in 1..=100 {
        handle.append(make_event(i)).expect("append should succeed");
    }
    handle.shutdown().expect("shutdown should succeed");

    // Check segment files exist
    let wal_dir = dir.path().join("wal");
    let segments = segment::list_segments(&wal_dir).expect("list should succeed");
    assert!(
        segments.len() > 1,
        "expected multiple segments, got {}",
        segments.len()
    );

    // Verify segment naming: all should match wal-{seq:020}.seg pattern
    for (seq, path) in &segments {
        let filename = path
            .file_name()
            .expect("should have filename")
            .to_str()
            .expect("should be valid UTF-8");
        assert_eq!(
            filename,
            segment::segment_filename(ShardId::SINGLE, *seq),
            "segment filename mismatch"
        );
    }

    // Verify replay gets all events
    let config = WalConfig {
        wal_dir_override: None,
        dir: dir.path().to_path_buf(),
        segment_size: 256,
        batch_size: 10,
        batch_timeout: Duration::from_millis(10),
        dedup_window: Duration::from_secs(30),
    };
    let (handle, replayed, _) = WalHandle::open(config).expect("reopen should succeed");
    assert_eq!(replayed.len(), 100, "all events should be replayed");
    handle.shutdown().expect("shutdown should succeed");
}
