use super::*;
use crossbeam::channel::bounded;

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

#[test]
fn writer_processes_single_event() {
    let dir = tempfile::tempdir().expect("tempdir creation should succeed");
    let (tx, rx) = bounded(100);
    let segment = SegmentWriter::open(dir.path(), ShardId::SINGLE, 1, 16 * 1024 * 1024)
        .expect("open should succeed");
    let dedup = DedupWindow::new(Duration::from_secs(30));
    let config = WriterConfig {
        dir: dir.path().to_path_buf(),
        segment_size: 16 * 1024 * 1024,
        batch_size: 100,
        batch_timeout: Duration::from_millis(10),
        dedup_window: Duration::from_secs(30),
        session_journal_path: None,
        shard_id: ShardId::SINGLE,
        region_id: RegionId::SINGLE,
    };

    let (reply_tx, reply_rx) = bounded(1);
    tx.send(WalCommand::Append {
        event: make_event(42),
        reply: reply_tx,
    })
    .expect("send should succeed");
    tx.send(WalCommand::Shutdown).expect("send should succeed");

    let handle = std::thread::spawn(move || run_writer(&rx, &config, segment, 1, dedup));

    let seq = reply_rx
        .recv()
        .expect("should receive reply")
        .expect("should be ok");
    assert_eq!(seq, 1);

    handle
        .join()
        .expect("thread should join")
        .expect("writer should succeed");
}

#[test]
fn writer_deduplicates_events() {
    let dir = tempfile::tempdir().expect("tempdir creation should succeed");
    let (tx, rx) = bounded(100);
    let segment = SegmentWriter::open(dir.path(), ShardId::SINGLE, 1, 16 * 1024 * 1024)
        .expect("open should succeed");
    let dedup = DedupWindow::new(Duration::from_secs(30));
    let config = WriterConfig {
        dir: dir.path().to_path_buf(),
        segment_size: 16 * 1024 * 1024,
        batch_size: 100,
        batch_timeout: Duration::from_millis(10),
        dedup_window: Duration::from_secs(30),
        session_journal_path: None,
        shard_id: ShardId::SINGLE,
        region_id: RegionId::SINGLE,
    };

    let event = make_event(42);

    let (reply_tx1, reply_rx1) = bounded(1);
    let (reply_tx2, reply_rx2) = bounded(1);
    tx.send(WalCommand::Append {
        event: event.clone(),
        reply: reply_tx1,
    })
    .expect("send should succeed");
    tx.send(WalCommand::Append {
        event,
        reply: reply_tx2,
    })
    .expect("send should succeed");
    tx.send(WalCommand::Shutdown).expect("send should succeed");

    let handle = std::thread::spawn(move || run_writer(&rx, &config, segment, 1, dedup));

    let seq1 = reply_rx1
        .recv()
        .expect("should receive")
        .expect("should be ok");
    let seq2 = reply_rx2
        .recv()
        .expect("should receive")
        .expect("should be ok");
    assert_eq!(seq1, 1);
    assert_eq!(seq2, 0); // deduplicated

    handle
        .join()
        .expect("thread should join")
        .expect("writer should succeed");
}

#[test]
fn writer_handles_channel_disconnect() {
    let dir = tempfile::tempdir().expect("tempdir creation should succeed");
    let (tx, rx) = bounded(100);
    let segment = SegmentWriter::open(dir.path(), ShardId::SINGLE, 1, 16 * 1024 * 1024)
        .expect("open should succeed");
    let dedup = DedupWindow::new(Duration::from_secs(30));
    let config = WriterConfig {
        dir: dir.path().to_path_buf(),
        segment_size: 16 * 1024 * 1024,
        batch_size: 100,
        batch_timeout: Duration::from_millis(10),
        dedup_window: Duration::from_secs(30),
        session_journal_path: None,
        shard_id: ShardId::SINGLE,
        region_id: RegionId::SINGLE,
    };

    drop(tx); // Disconnect immediately

    let result = run_writer(&rx, &config, segment, 1, dedup);
    assert!(result.is_ok());
}

#[test]
fn writer_assigns_monotonic_sequences() {
    let dir = tempfile::tempdir().expect("tempdir creation should succeed");
    let (tx, rx) = bounded(100);
    let segment = SegmentWriter::open(dir.path(), ShardId::SINGLE, 1, 16 * 1024 * 1024)
        .expect("open should succeed");
    let dedup = DedupWindow::new(Duration::from_secs(30));
    let config = WriterConfig {
        dir: dir.path().to_path_buf(),
        segment_size: 16 * 1024 * 1024,
        batch_size: 100,
        batch_timeout: Duration::from_millis(10),
        dedup_window: Duration::from_secs(30),
        session_journal_path: None,
        shard_id: ShardId::SINGLE,
        region_id: RegionId::SINGLE,
    };

    let mut reply_rxs = Vec::new();
    for i in 0..5 {
        let (reply_tx, reply_rx) = bounded(1);
        tx.send(WalCommand::Append {
            event: make_event(i),
            reply: reply_tx,
        })
        .expect("send should succeed");
        reply_rxs.push(reply_rx);
    }
    tx.send(WalCommand::Shutdown).expect("send should succeed");

    let handle = std::thread::spawn(move || run_writer(&rx, &config, segment, 1, dedup));

    let mut seqs = Vec::new();
    for reply_rx in reply_rxs {
        let seq = reply_rx
            .recv()
            .expect("should receive")
            .expect("should be ok");
        seqs.push(seq);
    }

    // Verify monotonically increasing
    for window in seqs.windows(2) {
        assert!(window[0] < window[1], "seqs not monotonic: {seqs:?}");
    }
    assert_eq!(seqs[0], 1);

    handle
        .join()
        .expect("thread should join")
        .expect("writer should succeed");
}
