#![allow(
    clippy::assertions_on_constants,
    clippy::assertions_on_result_states,
    clippy::clone_on_copy,
    clippy::decimal_literal_representation,
    clippy::doc_markdown,
    clippy::empty_line_after_doc_comments,
    clippy::field_reassign_with_default,
    clippy::get_unwrap,
    clippy::identity_op,
    clippy::inconsistent_digit_grouping,
    clippy::indexing_slicing,
    clippy::integer_division,
    clippy::len_zero,
    clippy::let_underscore_must_use,
    clippy::manual_div_ceil,
    clippy::manual_let_else,
    clippy::manual_range_contains,
    clippy::modulo_arithmetic,
    clippy::needless_range_loop,
    clippy::non_ascii_literal,
    clippy::redundant_clone,
    clippy::shadow_reuse,
    clippy::shadow_same,
    clippy::shadow_unrelated,
    clippy::single_match_else,
    clippy::str_to_string,
    clippy::string_slice,
    clippy::tests_outside_test_module,
    clippy::too_many_lines,
    clippy::unnecessary_wraps,
    clippy::unseparated_literal_suffix,
    clippy::use_debug,
    clippy::useless_vec,
    clippy::wildcard_enum_match_arm,
    clippy::else_if_without_else,
    clippy::expect_used,
    clippy::missing_const_for_fn,
    clippy::similar_names,
    clippy::type_complexity,
    clippy::collapsible_else_if,
    clippy::doc_lazy_continuation,
    clippy::items_after_test_module,
    clippy::map_clone,
    clippy::multiple_unsafe_ops_per_block,
    clippy::unwrap_or_default,
    clippy::assign_op_pattern,
    clippy::needless_borrow,
    clippy::println_empty_string,
    clippy::unnecessary_cast,
    clippy::used_underscore_binding,
    clippy::create_dir,
    clippy::implicit_saturating_sub,
    clippy::exit,
    clippy::expect_fun_call,
    clippy::too_many_arguments,
    clippy::unnecessary_map_or,
    clippy::unwrap_used,
    dead_code,
    unused_imports,
    unused_variables,
    clippy::cloned_ref_to_slice_refs,
    clippy::neg_multiply,
    clippy::while_let_loop,
    clippy::bool_assert_comparison,
    clippy::excessive_precision,
    clippy::trivially_copy_pass_by_ref,
    clippy::op_ref,
    clippy::redundant_closure,
    clippy::unnecessary_lazy_evaluations,
    clippy::if_then_some_else_none,
    clippy::unnecessary_to_owned,
    clippy::single_component_path_imports,
)]
/// Wave 16S-V14: Checkpoint Regression Investigation Test
///
/// This test validates checkpoint saving behavior with volatile validation loss patterns.
/// Tests the hypothesis that V13's lower checkpoint count (10/12 vs V12's 12/12) is
/// EXPECTED BEHAVIOR due to fewer validation loss improvements, not a bug.
use std::sync::{Arc, Mutex};
use tracing::info;

#[derive(Debug, Clone)]
struct CheckpointRecord {
    epoch: usize,
    val_loss: f64,
    checkpoint_type: String, // "BEST" or "PERIODIC"
}

struct MockCheckpointTracker {
    best_val_loss: f64,
    checkpoints: Arc<Mutex<Vec<CheckpointRecord>>>,
}

impl MockCheckpointTracker {
    fn new() -> Self {
        Self {
            best_val_loss: f64::INFINITY,
            checkpoints: Arc::new(Mutex::new(Vec::new())),
        }
    }

    /// Simulates the actual checkpoint logic from dqn.rs lines 1187-1189
    fn process_epoch(&mut self, epoch: usize, val_loss: f64, checkpoint_frequency: usize) {
        let mut records = self.checkpoints.lock().unwrap();

        // Best model checkpoint: Save if validation loss improved
        // This matches the actual logic: if val_loss < self.best_val_loss
        if val_loss < self.best_val_loss {
            self.best_val_loss = val_loss;
            records.push(CheckpointRecord {
                epoch,
                val_loss,
                checkpoint_type: "BEST".to_string(),
            });
        }

        // Periodic checkpoint: Save every N epochs
        // This matches the actual logic: if checkpoint_frequency > 0 && (epoch + 1) % checkpoint_frequency == 0
        if checkpoint_frequency > 0 && epoch % checkpoint_frequency == 0 {
            records.push(CheckpointRecord {
                epoch,
                val_loss,
                checkpoint_type: "PERIODIC".to_string(),
            });
        }
    }

    fn get_checkpoint_summary(&self) -> (usize, usize, usize) {
        let records = self.checkpoints.lock().unwrap();
        let total = records.len();
        let best_count = records.iter().filter(|r| r.checkpoint_type == "BEST").count();
        let periodic_count = records
            .iter()
            .filter(|r| r.checkpoint_type == "PERIODIC")
            .count();
        (total, best_count, periodic_count)
    }

    fn print_checkpoint_log(&self) {
        let records = self.checkpoints.lock().unwrap();
        info!("Checkpoint Log:");
        for record in records.iter() {
            info!(
                epoch = record.epoch,
                checkpoint_type = %record.checkpoint_type,
                val_loss = record.val_loss,
                "Checkpoint record"
            );
        }
    }
}

#[test]
fn test_v12_decreasing_loss_pattern() {
    // V12 Pattern: Steadily decreasing validation loss (6 improvements)
    // Expected: 6 BEST checkpoints + 5 PERIODIC = 11 total
    let v12_losses = vec![
        12980.33, // Epoch 1: BEST (first epoch always saves)
        12783.01, // Epoch 2: BEST + PERIODIC (improvement + checkpoint_frequency)
        6498.01,  // Epoch 3: BEST (improvement)
        3111.01,  // Epoch 4: BEST + PERIODIC (improvement + checkpoint_frequency)
        1336.24,  // Epoch 5: BEST (improvement)
        34611.01, // Epoch 6: PERIODIC only (spike, no improvement)
        1749.28,  // Epoch 7: no save (higher than epoch 5)
        26107.40, // Epoch 8: PERIODIC only (spike, no improvement)
        1938.18,  // Epoch 9: no save (higher than epoch 5)
        865.29,   // Epoch 10: BEST + PERIODIC (improvement + checkpoint_frequency)
    ];

    let mut tracker = MockCheckpointTracker::new();
    for (idx, &loss) in v12_losses.iter().enumerate() {
        tracker.process_epoch(idx + 1, loss, 2); // checkpoint_frequency = 2
    }

    let (total, best, periodic) = tracker.get_checkpoint_summary();
    tracker.print_checkpoint_log();

    info!(total_checkpoints = total, best_checkpoints = best, periodic_checkpoints = periodic, "V12 Results");

    // Expected: 6 BEST + 5 PERIODIC = 11 total (matches actual V12 logs)
    assert_eq!(
        best, 6,
        "V12 should have 6 BEST checkpoints (epochs 1,2,3,4,5,10)"
    );
    assert_eq!(
        periodic, 5,
        "V12 should have 5 PERIODIC checkpoints (epochs 2,4,6,8,10)"
    );
    assert_eq!(total, 11, "V12 should have 11 total checkpoints");
}

#[test]
fn test_v13_volatile_loss_pattern() {
    // V13 Pattern: Volatile validation loss with fewer improvements (4 improvements)
    // Expected: 4 BEST checkpoints + 5 PERIODIC = 9 total
    let v13_losses = vec![
        24713.61, // Epoch 1: BEST (first epoch always saves)
        14304.86, // Epoch 2: BEST + PERIODIC (improvement + checkpoint_frequency)
        25258.00, // Epoch 3: no save (spike, worse than epoch 2)
        5553.62,  // Epoch 4: BEST + PERIODIC (improvement + checkpoint_frequency)
        30351.40, // Epoch 5: no save (spike, worse than epoch 4)
        967.53,   // Epoch 6: BEST + PERIODIC (improvement + checkpoint_frequency)
        15262.83, // Epoch 7: no save (spike, worse than epoch 6)
        8294.97,  // Epoch 8: PERIODIC only (spike, no improvement)
        1308.29,  // Epoch 9: no save (worse than epoch 6)
        8265.35,  // Epoch 10: PERIODIC only (spike, no improvement)
    ];

    let mut tracker = MockCheckpointTracker::new();
    for (idx, &loss) in v13_losses.iter().enumerate() {
        tracker.process_epoch(idx + 1, loss, 2); // checkpoint_frequency = 2
    }

    let (total, best, periodic) = tracker.get_checkpoint_summary();
    tracker.print_checkpoint_log();

    info!(total_checkpoints = total, best_checkpoints = best, periodic_checkpoints = periodic, "V13 Results");

    // Expected: 4 BEST + 5 PERIODIC = 9 total (matches actual V13 logs)
    assert_eq!(
        best, 4,
        "V13 should have 4 BEST checkpoints (epochs 1,2,4,6)"
    );
    assert_eq!(
        periodic, 5,
        "V13 should have 5 PERIODIC checkpoints (epochs 2,4,6,8,10)"
    );
    assert_eq!(total, 9, "V13 should have 9 total checkpoints");
}

#[test]
fn test_extreme_volatility_no_improvement() {
    // Edge case: High volatility with NO improvements after epoch 1
    // Expected: 1 BEST (epoch 1 only) + 5 PERIODIC = 6 total
    let extreme_losses = vec![
        1000.0,  // Epoch 1: BEST (first epoch)
        5000.0,  // Epoch 2: PERIODIC only (worse than epoch 1)
        8000.0,  // Epoch 3: no save
        3000.0,  // Epoch 4: PERIODIC only (worse than epoch 1)
        9000.0,  // Epoch 5: no save
        4000.0,  // Epoch 6: PERIODIC only (worse than epoch 1)
        7000.0,  // Epoch 7: no save
        2000.0,  // Epoch 8: PERIODIC only (worse than epoch 1)
        6000.0,  // Epoch 9: no save
        1500.0,  // Epoch 10: PERIODIC only (worse than epoch 1)
    ];

    let mut tracker = MockCheckpointTracker::new();
    for (idx, &loss) in extreme_losses.iter().enumerate() {
        tracker.process_epoch(idx + 1, loss, 2);
    }

    let (total, best, periodic) = tracker.get_checkpoint_summary();
    tracker.print_checkpoint_log();

    info!(total_checkpoints = total, best_checkpoints = best, periodic_checkpoints = periodic, "Extreme Volatility Results");

    // Expected: 1 BEST + 5 PERIODIC = 6 total
    assert_eq!(best, 1, "Should only have 1 BEST checkpoint (epoch 1)");
    assert_eq!(
        periodic, 5,
        "Should have 5 PERIODIC checkpoints (epochs 2,4,6,8,10)"
    );
    assert_eq!(total, 6, "Should have 6 total checkpoints");
}

#[test]
fn test_all_improving_loss() {
    // Edge case: Every epoch improves (monotonic decrease)
    // Expected: 10 BEST + 5 PERIODIC = 15 total (but some overlap)
    // Note: Epochs 2,4,6,8,10 will have BOTH best + periodic saves
    let improving_losses = vec![
        10000.0, // Epoch 1: BEST
        9000.0,  // Epoch 2: BEST + PERIODIC
        8000.0,  // Epoch 3: BEST
        7000.0,  // Epoch 4: BEST + PERIODIC
        6000.0,  // Epoch 5: BEST
        5000.0,  // Epoch 6: BEST + PERIODIC
        4000.0,  // Epoch 7: BEST
        3000.0,  // Epoch 8: BEST + PERIODIC
        2000.0,  // Epoch 9: BEST
        1000.0,  // Epoch 10: BEST + PERIODIC
    ];

    let mut tracker = MockCheckpointTracker::new();
    for (idx, &loss) in improving_losses.iter().enumerate() {
        tracker.process_epoch(idx + 1, loss, 2);
    }

    let (total, best, periodic) = tracker.get_checkpoint_summary();
    tracker.print_checkpoint_log();

    info!(total_checkpoints = total, best_checkpoints = best, periodic_checkpoints = periodic, "All Improving Results");

    // Expected: 10 BEST + 5 PERIODIC = 15 total
    assert_eq!(
        best, 10,
        "Should have 10 BEST checkpoints (all epochs improve)"
    );
    assert_eq!(
        periodic, 5,
        "Should have 5 PERIODIC checkpoints (epochs 2,4,6,8,10)"
    );
    assert_eq!(total, 15, "Should have 15 total checkpoints");
}

#[test]
fn test_checkpoint_frequency_zero() {
    // Edge case: checkpoint_frequency = 0 (disable periodic saves)
    // Expected: Only BEST checkpoints saved
    let losses = vec![
        10000.0, 9000.0, 8000.0, 7000.0, 6000.0, 5000.0, 4000.0, 3000.0, 2000.0, 1000.0,
    ];

    let mut tracker = MockCheckpointTracker::new();
    for (idx, &loss) in losses.iter().enumerate() {
        tracker.process_epoch(idx + 1, loss, 0); // checkpoint_frequency = 0
    }

    let (total, best, periodic) = tracker.get_checkpoint_summary();
    tracker.print_checkpoint_log();

    info!(total_checkpoints = total, best_checkpoints = best, periodic_checkpoints = periodic, "Checkpoint Frequency 0 Results");

    // Expected: 10 BEST + 0 PERIODIC = 10 total
    assert_eq!(
        best, 10,
        "Should have 10 BEST checkpoints (all epochs improve)"
    );
    assert_eq!(
        periodic, 0,
        "Should have 0 PERIODIC checkpoints (frequency=0)"
    );
    assert_eq!(total, 10, "Should have 10 total checkpoints");
}
