// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) 2025 lacklustr@protonmail.com https://github.com/eadf

use super::*;
use vector_traits::glam::{Vec3, Vec3A};

/// Helper to create a simple validator that always returns true
fn always_valid(_n1: Vec3A, _l1: f32, _n2: Vec3A, _l2: f32) -> bool {
    true
}

/// Helper to create a validator that always returns false
fn always_invalid(_n1: Vec3A, _l1: f32, _n2: Vec3A, _l2: f32) -> bool {
    false
}

/// Helper to create a simple triangle fan around origin
fn create_fan_vertices(count: usize) -> impl ExactSizeIterator<Item = Vec3A> {
    let v: Vec<_> = (0..count)
        .map(|i| {
            let angle = (i as f32) * std::f32::consts::PI * 2.0 / (count as f32);
            Vec3A::new(angle.cos(), angle.sin(), 0.0_f32)
        })
        .collect();
    v.into_iter().enumerate().map(|(i, v)| {
        let va: [f32; 3] = v.into();
        println!("giving V{i}: {:?}", va);
        v
    })
}

#[test]
fn test_skip_leading_basic() {
    let vertices = create_fan_vertices(5); // Creates 4 triangles
    let center = Vec3A::ZERO;

    // Skip first triangle, check remaining 2 pairs
    let result = IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
        vertices,
        center,
        1, // skip_leading
        0, // skip_trailing
        always_valid,
    );

    assert!(result);
}

#[test]
fn test_skip_trailing_basic() {
    let vertices = create_fan_vertices(5); // Creates 4 triangles
    let center = Vec3A::ZERO;

    // Skip last triangle, check first 2 pairs
    let result = IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
        vertices,
        center,
        1, // skip_leading (required by debug_assert)
        1, // skip_trailing
        always_valid,
    );

    assert!(result);
}

#[test]
fn test_skip_both_ends() {
    let vertices = create_fan_vertices(6); // Creates 5 triangles
    let center = Vec3A::ZERO;

    // Skip first 2 and last 2 triangles, check middle 1 pair
    let result = IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
        vertices,
        center,
        2, // skip_leading
        2, // skip_trailing
        always_valid,
    );

    assert!(result);
}

#[test]
fn test_skip_leaves_no_pairs() {
    let vertices = create_fan_vertices(4); // Creates 3 triangles
    let center = Vec3A::ZERO;

    // Skip 2 leading + 1 trailing = only 0 pairs to check
    let result = IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
        vertices,
        center,
        2,              // skip_leading
        1,              // skip_trailing
        always_invalid, // Should not be called
    );

    // Returns true when no pairs to validate
    assert!(result);
}

#[test]
fn test_skip_excessive() {
    let vertices = create_fan_vertices(4); // Creates 3 triangles
    let center = Vec3A::ZERO;

    // Skip more than available
    let result = IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
        vertices,
        center,
        10, // skip_leading (excessive)
        10, // skip_trailing (excessive)
        always_invalid,
    );

    assert!(result); // Returns true when nothing to validate
}

#[test]
fn test_minimum_valid_fan() {
    // Minimum fan: 3 vertices = 2 triangles = 1 pair
    let vertices = [
        Vec3A::new(1.0, 0.0, 0.0),
        Vec3A::new(0.0, 1.0, 0.0),
        Vec3A::new(-1.0, 0.0, 0.0),
    ];
    let center = Vec3A::ZERO;

    let result = IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
        vertices.iter().copied(),
        center,
        1, // skip_leading
        0, // skip_trailing
        always_valid,
    );

    assert!(result);
}

#[test]
fn test_validator_receives_correct_normals() {
    let vertices: Vec<Vec3A> = vec![
        [1.0, 0.0, 0.0].into(),
        [0.0, 1.0, 0.0].into(),
        [-1.0, 0.0, 0.0].into(),
        [0.0, -1.0, 0.0].into(),
    ];
    let center = Vec3A::ZERO;

    let mut call_count = 0;
    let result = IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
        vertices.into_iter(),
        center,
        1,
        0,
        |n1, len1, n2, len2| {
            call_count += 1;
            // All normals should point in +Z direction (counterclockwise)
            assert!(
                n1.z < 0.0,
                "First normal should point down n1:{:?} n2:{:?}",
                n1,
                n2
            );
            assert!(
                n2.z < 0.0,
                "Second normal should point down n1:{:?} n2:{:?}",
                n1,
                n2
            );
            assert!(len1 > 0.0, "First length should be positive");
            assert!(len2 > 0.0, "Second length should be positive");
            true
        },
    );

    assert!(result);
    assert_eq!(call_count, 2, "call_count:{call_count} expected 2"); // 3 triangles -> 2 pairs
}

#[test]
fn test_valence_4_sub_h1() {
    let vertices: Vec<Vec3A> = vec![
        [1.0, 0.0, 0.0].into(),
        [0.0, 1.0, 0.0].into(),
        [-1.0, 0.0, 0.0].into(),
        [0.0, -1.0, 0.0].into(),
    ];
    let center = Vec3A::ZERO;

    let mut call_count = 0;
    assert!(
        IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
            vertices.into_iter(),
            center,
            1,
            0,
            |_, _, _, _| {
                call_count += 1;
                true
            },
        )
    );

    assert_eq!(call_count, 2, "call_count:{call_count} expected 2"); // 3 valid triangles -> 2 pair
}

#[test]
fn test_valence_4_sub_h1_t1() {
    let vertices: Vec<Vec3A> = vec![
        [1.0, 0.0, 0.0].into(),
        [0.0, 1.0, 0.0].into(),
        [-1.0, 0.0, 0.0].into(),
        [0.0, -1.0, 0.0].into(),
    ];
    let center = Vec3A::ZERO;

    let mut call_count = 0;
    assert!(
        IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
            vertices.into_iter(),
            center,
            1,
            1,
            |_, _, _, _| {
                call_count += 1;
                true
            },
        )
    );

    assert_eq!(call_count, 1, "call_count:{call_count} expected 1"); // 2 valid triangles -> 1 pair
}

#[test]
fn test_valence_4_sub_h2_t1() {
    let vertices: Vec<Vec3A> = vec![
        [1.0, 0.0, 0.0].into(),
        [0.0, 1.0, 0.0].into(),
        [-1.0, 0.0, 0.0].into(),
        [0.0, -1.0, 0.0].into(),
    ];
    let center = Vec3A::ZERO;

    let mut call_count = 0;
    assert!(
        IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
            vertices.into_iter(),
            center,
            2,
            1,
            |_, _, _, _| {
                call_count += 1;
                true
            },
        )
    );

    assert_eq!(call_count, 0, "call_count:{call_count} expected 0"); // 1 valid triangles -> 0 pair
}

#[test]
fn test_valence_3_sub_h1() {
    let vertices: Vec<Vec3A> = vec![
        [1.0, 0.0, 0.0].into(),
        [0.0, 1.0, 0.0].into(),
        [-1.0, 0.0, 0.0].into(),
    ];
    let center = Vec3A::ZERO;

    let mut call_count = 0;
    assert!(
        IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
            vertices.into_iter(),
            center,
            1,
            0,
            |_, _, _, _| {
                call_count += 1;
                true
            },
        )
    );

    assert_eq!(call_count, 1, "call_count:{call_count} expected 1"); // 2 valid triangles -> 1 pair
}

#[test]
fn test_validator_failure_propagates() {
    let vertices = create_fan_vertices(5);
    let center = Vec3A::ZERO;

    let result = IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
        vertices,
        center,
        1,
        0,
        always_invalid,
    );

    assert!(!result);
}

#[test]
fn test_validator_early_exit() {
    let vertices = create_fan_vertices(10); // Many triangles
    let center = Vec3A::ZERO;

    let mut call_count = 0;
    let result = IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
        vertices,
        center,
        1,
        0,
        |_, _, _, _| {
            call_count += 1;
            call_count < 3 // Fail on third call
        },
    );

    assert!(!result);
    assert_eq!(call_count, 3); // Should stop after first failure
}

#[test]
fn test_skip_trailing_boundary() {
    let vertices = create_fan_vertices(6); // 6 triangles
    assert_eq!(vertices.len(), 6);
    let center = Vec3A::ZERO;

    let mut pair_count = 0;
    let _ = IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
        vertices.into_iter(),
        center,
        1, // skip first triangle t0
        3, // Skip last 3 triangles (t5,t4,t3), leaving triangles t1 & t2
        |_, _, _, _| {
            pair_count += 1;
            true
        },
    );

    // Triangles 2 -> only 1 pair
    assert_eq!(pair_count, 1);
}

/// Helper to test skip mechanics with expected pair count
fn test_skip_mechanics(
    vertex_count: usize,
    skip_leading: usize,
    skip_trailing: usize,
    expected_pairs: usize,
) {
    let vertices = create_fan_vertices(vertex_count);
    assert_eq!(vertices.len(), vertex_count);
    let center = Vec3A::ZERO;

    let mut actual_pairs = 0;
    assert!(
        IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
            vertices.into_iter(),
            center,
            skip_leading,
            skip_trailing,
            |_, _, _, _| {
                actual_pairs += 1;
                true
            },
        )
    );

    assert_eq!(
        actual_pairs,
        expected_pairs,
        "vertex_count={vertex_count}, skip_leading={skip_leading}, skip_trailing={skip_trailing}, \
         total_triangles={vertex_count}, checked_triangles={}, expected_pairs={expected_pairs} but instead got {actual_pairs} pairs",
        vertex_count
            .saturating_sub(skip_leading)
            .saturating_sub(skip_trailing),
    );
}

#[test]
fn test_skip_mechanics_permutations() {
    // Format: (vertex_count, skip_leading, skip_trailing, expected_pairs)
    // Formula: checked_triangles = total_triangles - skip_leading - skip_trailing
    //          expected_pairs = checked_triangles - 1 (if checked_triangles >= 2, else 0)

    let test_cases = vec![
        // Basic cases with 4 vertices (4 triangles)
        (4, 1, 0, 2), // Skip t0, check t1,t2,t3 → 3 triangles → 2 pairs
        (4, 1, 1, 1), // Skip t0,t3, check t1,t2 → 2 triangles → 1 pair
        (4, 2, 0, 1), // Skip t0,t1, check t2,t3 → 2 triangles → 1 pair
        (4, 1, 2, 0), // Skip t0,t2,t3, check t1 → 1 triangle → 0 pairs
        (4, 2, 1, 0), // Skip t0,t1,t3, check t2 → 1 triangle → 0 pairs
        // Cases with 5 vertices (5 triangles)
        (5, 1, 0, 3), // Skip t0, check t1,t2,t3,t4 → 4 triangles → 3 pairs
        (5, 1, 1, 2), // Skip t0,t4, check t1,t2,t3 → 3 triangles → 2 pairs
        (5, 2, 1, 1), // Skip t0,t1,t4, check t2,t3 → 2 triangles → 1 pair
        (5, 1, 3, 0), // Skip t0,t2,t3,t4, check t1 → 1 triangle → 0 pairs
        (5, 3, 1, 0), // Skip t0,t1,t2,t4, check t3 → 1 triangle → 0 pairs
        // Cases with 6 vertices (6 triangles)
        (6, 1, 0, 4), // Skip t0, check t1,t2,t3,t4,t5 → 5 triangles → 4 pairs
        (6, 1, 3, 1), // Skip t0,t3,t4,t5, check t1,t2 → 2 triangles → 1 pair
        (6, 2, 2, 1), // Skip t0,t1,t4,t5, check t2,t3 → 2 triangles → 1 pair
        (6, 1, 4, 0), // Skip t0,t2,t3,t4,t5, check t1 → 1 triangle → 0 pairs
        (6, 3, 2, 0), // Skip t0,t1,t2,t4,t5, check t3 → 1 triangle → 0 pairs
        // Cases with 7 vertices (7 triangles)
        (7, 1, 0, 5), // Skip t0, check t1..t6 → 6 triangles → 5 pairs
        (7, 1, 2, 3), // Skip t0,t5,t6, check t1,t2,t3,t4 → 4 triangles → 3 pairs
        (7, 2, 2, 2), // Skip t0,t1,t5,t6, check t2,t3,t4 → 3 triangles → 2 pairs
        (7, 3, 2, 1), // Skip t0,t1,t2,t5,t6, check t3,t4 → 2 triangles → 1 pair
        (7, 1, 5, 0), // Skip t0,t2..t6, check t1 → 1 triangle → 0 pairs
        // Edge cases - skip everything
        (4, 3, 0, 0), // Skip t0,t1,t2, check t3 → 1 triangle → 0 pairs
        (4, 1, 3, 0), // Skip all → 0 triangles → 0 pairs (handled as empty)
        (5, 2, 2, 0), // Skip t0,t1,t3,t4, check t2 → 1 triangle → 0 pairs
        // Minimum fan - 3 vertices (3 triangles)
        (3, 1, 0, 1), // Skip t0, check t1,t2 → 2 triangles → 1 pair
        (3, 1, 1, 0), // Skip t0,t2, check t1 → 1 triangle → 0 pairs
        (3, 2, 0, 0), // Skip t0,t1, check t2 → 1 triangle → 0 pairs
    ];

    for (vertex_count, skip_leading, skip_trailing, expected_pairs) in test_cases {
        println!();
        println!(
            "evaluating vertex_count:{vertex_count}, skip_leading:{skip_leading}, skip_trailing:{skip_trailing} expected:{expected_pairs}):"
        );
        test_skip_mechanics(vertex_count, skip_leading, skip_trailing, expected_pairs);
    }
}

#[test]
fn test_skip_excessive_cases() {
    // Test cases where skipping exceeds available triangles
    let test_cases = vec![
        (4, 4, 0, 0),  // Skip all 4 triangles
        (4, 2, 2, 0),  // Skip 4 total (2+2)
        (4, 3, 1, 0),  // Skip 4 total (3+1)
        (5, 5, 0, 0),  // Skip all 5 triangles
        (5, 3, 3, 0),  // Skip more than available (6 > 5)
        (6, 10, 0, 0), // Excessive skip_leading
        (6, 1, 10, 0), // Excessive skip_trailing
    ];

    for (vertex_count, skip_leading, skip_trailing, expected_pairs) in test_cases {
        test_skip_mechanics(vertex_count, skip_leading, skip_trailing, expected_pairs);
    }
}

#[test]
fn test_skip_symmetry() {
    // Test that skipping from either end produces expected results
    let center = Vec3A::ZERO;

    // Skip 2 from start: check t2..t7 (6 triangles → 5 pairs)
    let mut pairs_skip_start = 0;
    assert!(
        IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
            create_fan_vertices(8),
            center,
            2,
            0,
            |_, _, _, _| {
                pairs_skip_start += 1;
                true
            },
        )
    );
    assert_eq!(pairs_skip_start, 5);

    // Skip 2 from end: check t1..t5 (5 triangles → 4 pairs)
    let mut pairs_skip_end = 0;
    assert!(
        IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
            create_fan_vertices(8),
            center,
            1,
            2,
            |_, _, _, _| {
                pairs_skip_end += 1;
                true
            },
        )
    );
    assert_eq!(pairs_skip_end, 4);

    // Skip 2 from both: check t2..t5 (4 triangles → 3 pairs)
    let mut pairs_skip_both = 0;
    assert!(
        IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
            create_fan_vertices(8),
            center,
            2,
            2,
            |_, _, _, _| {
                pairs_skip_both += 1;
                true
            },
        )
    );
    assert_eq!(pairs_skip_both, 3);
}

#[test]
fn test_normal_magnitude_calculation() {
    // Create a fan with known edge lengths
    let vertices = [
        Vec3A::new(2.0, 0.0, 0.0),  // Edge length 2
        Vec3A::new(0.0, 2.0, 0.0),  // Edge length 2
        Vec3A::new(-2.0, 0.0, 0.0), // Edge length 2
    ];
    let center = Vec3A::ZERO;

    assert!(
        IsotropicRemeshAlgo::<f32, Vec3>::validate_consecutive_triangles(
            vertices.iter().copied(),
            center,
            1,
            0,
            |_n1, len1, _n2, len2| {
                // Cross product of two perpendicular vectors of length 2
                // should give normal with magnitude 4
                assert!((len1.sqrt() - 4.0).abs() < 0.001);
                assert!((len2.sqrt() - 4.0).abs() < 0.001);
                true
            },
        )
    );
}
