/*******************************************************************************
 *
 *    Copyright (c) 2025 - 2026 Haixing Hu.
 *
 *    SPDX-License-Identifier: Apache-2.0
 *
 *    Licensed under the Apache License, Version 2.0.
 *
 ******************************************************************************/
// qubit-style: allow explicit-imports
use qubit_function::comparator::{
    ArcComparator,
    BoxComparator,
    Comparator,
    FnComparatorOps,
    RcComparator,
};
use std::cmp::Ordering;

#[test]
fn test_comparator_default_conversions_allow_relaxed_generic_types() {
    #[derive(Clone, Debug)]
    struct BorrowedRc<'a> {
        value: &'a str,
    }

    #[derive(Clone, Debug)]
    struct BorrowedRcComparator;

    impl<'a> Comparator<BorrowedRc<'a>> for BorrowedRcComparator {
        fn compare(&self, first: &BorrowedRc<'a>, second: &BorrowedRc<'a>) -> Ordering {
            first.value.cmp(second.value)
        }
    }

    let left_text = String::from("left");
    let right_text = String::from("right");
    let left = BorrowedRc {
        value: left_text.as_str(),
    };
    let right = BorrowedRc {
        value: right_text.as_str(),
    };
    let comparator = BorrowedRcComparator;

    assert_eq!(
        comparator.clone().into_box().compare(&left, &right),
        Ordering::Less
    );
    assert_eq!(
        comparator.clone().into_rc().compare(&left, &right),
        Ordering::Less
    );
    assert_eq!(comparator.into_arc().compare(&left, &right), Ordering::Less);
}

#[cfg(test)]
mod box_comparator_tests {
    use super::*;

    #[test]
    fn test_new_and_compare() {
        let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        assert_eq!(cmp.compare(&5, &3), Ordering::Greater);
        assert_eq!(cmp.compare(&3, &5), Ordering::Less);
        assert_eq!(cmp.compare(&5, &5), Ordering::Equal);
    }

    #[test]
    fn test_reversed() {
        let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let rev = cmp.reversed();
        assert_eq!(rev.compare(&5, &3), Ordering::Less);
        assert_eq!(rev.compare(&3, &5), Ordering::Greater);
        assert_eq!(rev.compare(&5, &5), Ordering::Equal);
    }

    #[test]
    fn test_then_comparing() {
        let cmp1 = BoxComparator::new(|a: &i32, b: &i32| (a % 2).cmp(&(b % 2)));
        let cmp2 = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let chained = cmp1.then_comparing(cmp2);
        assert_eq!(chained.compare(&4, &2), Ordering::Greater);
        assert_eq!(chained.compare(&3, &1), Ordering::Greater);
        assert_eq!(chained.compare(&2, &4), Ordering::Less);
    }

    #[test]
    fn test_then_comparing_with_equal() {
        let cmp1 = BoxComparator::new(|a: &i32, b: &i32| (a % 2).cmp(&(b % 2)));
        let cmp2 = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let chained = cmp1.then_comparing(cmp2);
        // Both even, so second comparator decides
        assert_eq!(chained.compare(&4, &2), Ordering::Greater);
    }

    #[test]
    fn test_then_comparing_with_non_equal_greater() {
        // Test the case where the first comparator returns Greater
        let cmp1 = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let cmp2 = BoxComparator::new(|_a: &i32, _b: &i32| {
            panic!("Second comparator should not be called")
        });
        let chained = cmp1.then_comparing(cmp2);
        // 5 > 3, so first comparator returns Greater, second not called
        assert_eq!(chained.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_then_comparing_with_non_equal_less() {
        // Test the case where the first comparator returns Less
        let cmp1 = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let cmp2 = BoxComparator::new(|_a: &i32, _b: &i32| {
            panic!("Second comparator should not be called")
        });
        let chained = cmp1.then_comparing(cmp2);
        // 3 < 5, so first comparator returns Less, second not called
        assert_eq!(chained.compare(&3, &5), Ordering::Less);
    }

    #[test]
    fn test_comparing() {
        #[derive(Debug)]
        struct Person {
            #[allow(dead_code)]
            name: String,
            age: i32,
        }

        let by_age = BoxComparator::comparing(|p: &Person| &p.age);
        let p1 = Person {
            name: "Alice".to_string(),
            age: 30,
        };
        let p2 = Person {
            name: "Bob".to_string(),
            age: 25,
        };
        assert_eq!(by_age.compare(&p1, &p2), Ordering::Greater);
    }

    #[test]
    fn test_into_fn() {
        let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let func = cmp.into_fn();
        assert_eq!(func(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_into_box() {
        let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let boxed = cmp.into_box();
        assert_eq!(boxed.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_into_rc() {
        let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let rc = cmp.into_rc();
        assert_eq!(rc.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_with_strings() {
        let cmp = BoxComparator::new(|a: &String, b: &String| a.cmp(b));
        assert_eq!(
            cmp.compare(&"hello".to_string(), &"world".to_string()),
            Ordering::Less
        );
    }
}

#[cfg(test)]
mod arc_comparator_tests {
    use super::*;

    #[test]
    fn test_new_and_compare() {
        let cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        assert_eq!(cmp.compare(&5, &3), Ordering::Greater);
        assert_eq!(cmp.compare(&3, &5), Ordering::Less);
        assert_eq!(cmp.compare(&5, &5), Ordering::Equal);
    }

    #[test]
    fn test_clone() {
        let cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let cloned = cmp.clone();
        assert_eq!(cmp.compare(&5, &3), Ordering::Greater);
        assert_eq!(cloned.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_reversed() {
        let cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let rev = cmp.reversed();
        assert_eq!(rev.compare(&5, &3), Ordering::Less);
        // Original still works
        assert_eq!(cmp.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_then_comparing() {
        let cmp1 = ArcComparator::new(|a: &i32, b: &i32| (a % 2).cmp(&(b % 2)));
        let cmp2 = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let chained = cmp1.then_comparing(&cmp2);
        assert_eq!(chained.compare(&4, &2), Ordering::Greater);
        // Originals still work
        assert_eq!(cmp1.compare(&4, &2), Ordering::Equal);
        assert_eq!(cmp2.compare(&4, &2), Ordering::Greater);
    }

    #[test]
    fn test_then_comparing_with_non_equal_greater() {
        // Test the case where the first comparator returns Greater
        let cmp1 = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let cmp2 = ArcComparator::new(|_a: &i32, _b: &i32| {
            panic!("Second comparator should not be called")
        });
        let chained = cmp1.then_comparing(&cmp2);
        // 5 > 3, so first comparator returns Greater, second not called
        assert_eq!(chained.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_then_comparing_with_non_equal_less() {
        // Test the case where the first comparator returns Less
        let cmp1 = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let cmp2 = ArcComparator::new(|_a: &i32, _b: &i32| {
            panic!("Second comparator should not be called")
        });
        let chained = cmp1.then_comparing(&cmp2);
        // 3 < 5, so first comparator returns Less, second not called
        assert_eq!(chained.compare(&3, &5), Ordering::Less);
    }

    #[test]
    fn test_comparing() {
        #[derive(Debug)]
        struct Person {
            #[allow(dead_code)]
            name: String,
            age: i32,
        }

        let by_age = ArcComparator::comparing(|p: &Person| &p.age);
        let p1 = Person {
            name: "Alice".to_string(),
            age: 30,
        };
        let p2 = Person {
            name: "Bob".to_string(),
            age: 25,
        };
        assert_eq!(by_age.compare(&p1, &p2), Ordering::Greater);
    }

    #[test]
    fn test_into_fn() {
        let cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let func = cmp.into_fn();
        assert_eq!(func(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_into_box() {
        let cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let boxed = cmp.into_box();
        assert_eq!(boxed.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_into_rc() {
        let cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let rc = cmp.into_rc();
        assert_eq!(rc.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_into_arc() {
        let cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let arc = cmp.into_arc();
        assert_eq!(arc.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_thread_safety() {
        let cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let handles: Vec<_> = (0..10)
            .map(|i| {
                let cmp_clone = cmp.clone();
                std::thread::spawn(move || {
                    assert_eq!(cmp_clone.compare(&(i + 1), &i), Ordering::Greater);
                })
            })
            .collect();

        for handle in handles {
            handle.join().unwrap();
        }
    }
}

#[cfg(test)]
mod rc_comparator_tests {
    use super::*;

    #[test]
    fn test_new_and_compare() {
        let cmp = RcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        assert_eq!(cmp.compare(&5, &3), Ordering::Greater);
        assert_eq!(cmp.compare(&3, &5), Ordering::Less);
        assert_eq!(cmp.compare(&5, &5), Ordering::Equal);
    }

    #[test]
    fn test_clone() {
        let cmp = RcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let cloned = cmp.clone();
        assert_eq!(cmp.compare(&5, &3), Ordering::Greater);
        assert_eq!(cloned.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_reversed() {
        let cmp = RcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let rev = cmp.reversed();
        assert_eq!(rev.compare(&5, &3), Ordering::Less);
        // Original still works
        assert_eq!(cmp.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_then_comparing() {
        let cmp1 = RcComparator::new(|a: &i32, b: &i32| (a % 2).cmp(&(b % 2)));
        let cmp2 = RcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let chained = cmp1.then_comparing(&cmp2);
        assert_eq!(chained.compare(&4, &2), Ordering::Greater);
        // Originals still work
        assert_eq!(cmp1.compare(&4, &2), Ordering::Equal);
        assert_eq!(cmp2.compare(&4, &2), Ordering::Greater);
    }

    #[test]
    fn test_then_comparing_with_non_equal_greater() {
        // Test the case where the first comparator returns Greater
        let cmp1 = RcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let cmp2 = RcComparator::new(|_a: &i32, _b: &i32| {
            panic!("Second comparator should not be called")
        });
        let chained = cmp1.then_comparing(&cmp2);
        // 5 > 3, so first comparator returns Greater, second not called
        assert_eq!(chained.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_then_comparing_with_non_equal_less() {
        // Test the case where the first comparator returns Less
        let cmp1 = RcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let cmp2 = RcComparator::new(|_a: &i32, _b: &i32| {
            panic!("Second comparator should not be called")
        });
        let chained = cmp1.then_comparing(&cmp2);
        // 3 < 5, so first comparator returns Less, second not called
        assert_eq!(chained.compare(&3, &5), Ordering::Less);
    }

    #[test]
    fn test_comparing() {
        #[derive(Debug)]
        struct Person {
            #[allow(dead_code)]
            name: String,
            age: i32,
        }

        let by_age = RcComparator::comparing(|p: &Person| &p.age);
        let p1 = Person {
            name: "Alice".to_string(),
            age: 30,
        };
        let p2 = Person {
            name: "Bob".to_string(),
            age: 25,
        };
        assert_eq!(by_age.compare(&p1, &p2), Ordering::Greater);
    }

    #[test]
    fn test_into_fn() {
        let cmp = RcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let func = cmp.into_fn();
        assert_eq!(func(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_into_box() {
        let cmp = RcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let boxed = cmp.into_box();
        assert_eq!(boxed.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_into_rc() {
        let cmp = RcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let rc = cmp.into_rc();
        assert_eq!(rc.compare(&5, &3), Ordering::Greater);
    }
}

#[cfg(test)]
mod closure_tests {
    use super::*;

    #[test]
    fn test_closure_as_comparator() {
        let cmp = |a: &i32, b: &i32| a.cmp(b);
        assert_eq!(cmp.compare(&5, &3), Ordering::Greater);
        assert_eq!(cmp.compare(&3, &5), Ordering::Less);
        assert_eq!(cmp.compare(&5, &5), Ordering::Equal);
    }

    #[test]
    fn test_closure_into_box() {
        let cmp = |a: &i32, b: &i32| a.cmp(b);
        let boxed = cmp.into_box();
        assert_eq!(boxed.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_closure_into_rc() {
        let cmp = |a: &i32, b: &i32| a.cmp(b);
        let rc = cmp.into_rc();
        assert_eq!(rc.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_closure_into_arc() {
        let cmp = |a: &i32, b: &i32| a.cmp(b);
        let arc = cmp.into_arc();
        assert_eq!(arc.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_closure_into_fn() {
        let cmp = |a: &i32, b: &i32| a.cmp(b);
        let func = cmp.into_fn();
        assert_eq!(func(&5, &3), Ordering::Greater);
    }
}

#[cfg(test)]
mod fn_ops_tests {
    use super::*;

    #[test]
    fn test_reversed() {
        let rev = (|a: &i32, b: &i32| a.cmp(b)).reversed();
        assert_eq!(rev.compare(&5, &3), Ordering::Less);
    }

    #[test]
    fn test_then_comparing() {
        let cmp = (|a: &i32, b: &i32| (a % 2).cmp(&(b % 2)))
            .then_comparing(BoxComparator::new(|a: &i32, b: &i32| a.cmp(b)));
        assert_eq!(cmp.compare(&4, &2), Ordering::Greater);
    }

    #[test]
    fn test_chained_operations() {
        let cmp = (|a: &i32, b: &i32| a.cmp(b))
            .reversed()
            .then_comparing(BoxComparator::new(|a: &i32, b: &i32| b.cmp(a)));
        assert_eq!(cmp.compare(&5, &3), Ordering::Less);
    }
}

#[cfg(test)]
mod conversion_tests {
    use super::*;

    #[test]
    fn test_box_to_rc() {
        let box_cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let rc_cmp = box_cmp.into_rc();
        assert_eq!(rc_cmp.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_arc_to_box() {
        let arc_cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let box_cmp = arc_cmp.into_box();
        assert_eq!(box_cmp.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_arc_to_rc() {
        let arc_cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let rc_cmp = arc_cmp.into_rc();
        assert_eq!(rc_cmp.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_rc_to_box() {
        let rc_cmp = RcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let box_cmp = rc_cmp.into_box();
        assert_eq!(box_cmp.compare(&5, &3), Ordering::Greater);
    }
}

#[cfg(test)]
mod generic_tests {
    use super::*;

    fn sort_with_comparator<C: Comparator<i32>>(cmp: &C, mut vec: Vec<i32>) -> Vec<i32> {
        vec.sort_by(|a, b| cmp.compare(a, b));
        vec
    }

    #[test]
    fn test_with_box_comparator() {
        let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let sorted = sort_with_comparator(&cmp, vec![3, 1, 4, 1, 5]);
        assert_eq!(sorted, vec![1, 1, 3, 4, 5]);
    }

    #[test]
    fn test_with_arc_comparator() {
        let cmp = ArcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let sorted = sort_with_comparator(&cmp, vec![3, 1, 4, 1, 5]);
        assert_eq!(sorted, vec![1, 1, 3, 4, 5]);
    }

    #[test]
    fn test_with_rc_comparator() {
        let cmp = RcComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let sorted = sort_with_comparator(&cmp, vec![3, 1, 4, 1, 5]);
        assert_eq!(sorted, vec![1, 1, 3, 4, 5]);
    }

    #[test]
    fn test_with_closure() {
        let cmp = |a: &i32, b: &i32| a.cmp(b);
        let sorted = sort_with_comparator(&cmp, vec![3, 1, 4, 1, 5]);
        assert_eq!(sorted, vec![1, 1, 3, 4, 5]);
    }
}

#[cfg(test)]
mod edge_cases {
    use super::*;

    #[test]
    fn test_with_empty_values() {
        let cmp = BoxComparator::new(|a: &String, b: &String| a.cmp(b));
        assert_eq!(
            cmp.compare(&String::new(), &"hello".to_string()),
            Ordering::Less
        );
    }

    #[test]
    fn test_with_negative_numbers() {
        let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        assert_eq!(cmp.compare(&-5, &-3), Ordering::Less);
        assert_eq!(cmp.compare(&-3, &-5), Ordering::Greater);
    }

    #[test]
    fn test_multiple_reversals() {
        let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
        let rev1 = cmp.reversed();
        let rev2 = rev1.reversed();
        // Double reversal should be same as original
        assert_eq!(rev2.compare(&5, &3), Ordering::Greater);
    }

    #[test]
    fn test_long_chain() {
        let cmp1 = BoxComparator::new(|a: &i32, b: &i32| (a / 10).cmp(&(b / 10)));
        let cmp2 = BoxComparator::new(|a: &i32, b: &i32| (a % 10).cmp(&(b % 10)));
        let chained = cmp1.then_comparing(cmp2);
        assert_eq!(chained.compare(&15, &12), Ordering::Greater);
        assert_eq!(chained.compare(&12, &15), Ordering::Less);
    }
}
