/*******************************************************************************
 *
 *    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
//! Tests for FnTransformerOnceOps extension trait

use qubit_function::{
    FnTransformerOnceOps,
    FnTransformerOps,
    Transformer,
    TransformerOnce,
};

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

    #[test]
    fn test_and_then_with_closures() {
        let parse = |s: String| s.parse::<i32>().unwrap_or(0);
        let double = |x: i32| x * 2;

        let composed = FnTransformerOnceOps::and_then(parse, double);
        assert_eq!(composed.apply("21".to_string()), 42);
    }

    #[test]
    fn test_and_then_chain() {
        let parse = |s: String| s.parse::<i32>().unwrap_or(0);
        let add_one = |x: i32| x + 1;
        let double = |x: i32| x * 2;

        let composed =
            FnTransformerOnceOps::and_then(parse, FnTransformerOnceOps::and_then(add_one, double));
        assert_eq!(composed.apply("5".to_string()), 12); // (5 + 1) * 2 = 12
    }

    #[test]
    fn test_compose_with_closures() {
        let double = |x: i32| x * 2;
        let to_string = |x: i32| x.to_string();

        let composed = to_string.compose(double);
        assert_eq!(composed.apply(21), "42");
    }

    #[test]
    fn test_compose_chain() {
        let triple = |x: i32| x * 3;
        let add_two = |x: i32| x + 2;
        let to_string = |x: i32| x.to_string();

        // ((5 + 2) * 3).to_string() = "21"
        let temp = FnTransformerOps::compose(triple, add_two);
        let composed = FnTransformerOps::compose(to_string, temp);
        assert_eq!(composed.apply(5), "21");
    }

    #[test]
    fn test_when_with_closure_predicate() {
        let double = |x: i32| x * 2;
        let conditional = FnTransformerOnceOps::when(double, |x: &i32| *x > 0).or_else(|x: i32| -x);

        assert_eq!(conditional.apply(5), 10);
    }

    #[test]
    fn test_when_with_negative_value() {
        let double = |x: i32| x * 2;
        let conditional = FnTransformerOnceOps::when(double, |x: &i32| *x > 0).or_else(|x: i32| -x);

        assert_eq!(conditional.apply(-5), 5);
    }

    #[test]
    fn test_when_with_identity_else() {
        let double = |x: i32| x * 2;
        let conditional = FnTransformerOnceOps::when(double, |x: &i32| *x > 10).or_else(|x: i32| x);

        assert_eq!(conditional.apply(20), 40);
    }

    #[test]
    fn test_when_with_identity_else_false_condition() {
        let double = |x: i32| x * 2;
        let conditional = FnTransformerOnceOps::when(double, |x: &i32| *x > 10).or_else(|x: i32| x);

        assert_eq!(conditional.apply(5), 5);
    }

    #[test]
    fn test_complex_composition() {
        // Complex composition: parse string, then if > 5 multiply by 2, otherwise multiply by 3, finally convert to string
        let parse = |s: String| s.parse::<i32>().unwrap_or(0);
        let double = |x: i32| x * 2;
        let triple = |x: i32| x * 3;
        let to_string = |x: i32| x.to_string();

        let conditional = FnTransformerOnceOps::when(double, |x: &i32| *x > 5).or_else(triple);
        let conditional_boxed = conditional.into_box();
        let temp = conditional_boxed.and_then(to_string);
        let composed = FnTransformerOnceOps::and_then(parse, temp);

        assert_eq!(composed.apply("10".to_string()), "20"); // 10 > 5, so 10 * 2 = 20
    }

    #[test]
    fn test_complex_composition_else_branch() {
        let parse = |s: String| s.parse::<i32>().unwrap_or(0);
        let double = |x: i32| x * 2;
        let triple = |x: i32| x * 3;
        let to_string = |x: i32| x.to_string();

        let conditional = FnTransformerOnceOps::when(double, |x: &i32| *x > 5).or_else(triple);
        let conditional_boxed = conditional.into_box();
        let temp = conditional_boxed.and_then(to_string);
        let composed = FnTransformerOnceOps::and_then(parse, temp);

        assert_eq!(composed.apply("3".to_string()), "9"); // 3 <= 5, so 3 * 3 = 9
    }

    #[test]
    fn test_function_pointer() {
        fn parse(s: String) -> i32 {
            s.parse().unwrap_or(0)
        }
        fn double(x: i32) -> i32 {
            x * 2
        }

        let composed = FnTransformerOnceOps::and_then(parse, double);
        assert_eq!(composed.apply("21".to_string()), 42);
    }

    #[test]
    fn test_mixed_closure_and_function_pointer() {
        fn parse(s: String) -> i32 {
            s.parse().unwrap_or(0)
        }

        let double = |x: i32| x * 2;
        let composed = FnTransformerOnceOps::and_then(parse, double);
        assert_eq!(composed.apply("21".to_string()), 42);
    }

    #[test]
    fn test_type_transformation() {
        let to_string = |x: i32| x.to_string();
        let get_length = |s: String| s.len();

        let composed = FnTransformerOnceOps::and_then(to_string, get_length);
        assert_eq!(composed.apply(12345), 5);
    }

    #[test]
    fn test_when_with_multiple_conditions() {
        let abs = |x: i32| x.abs();
        let double = |x: i32| x * 2;

        // If negative, take absolute value; otherwise double
        let transformer = FnTransformerOnceOps::when(abs, |x: &i32| *x < 0).or_else(double);

        assert_eq!(transformer.apply(-5), 5);
    }

    #[test]
    fn test_when_with_multiple_conditions_else_branch() {
        let abs = |x: i32| x.abs();
        let double = |x: i32| x * 2;

        let transformer = FnTransformerOnceOps::when(abs, |x: &i32| *x < 0).or_else(double);

        assert_eq!(transformer.apply(5), 10);
    }

    #[test]
    fn test_closure_capturing_environment() {
        let multiplier = 3;
        let multiply = move |x: i32| x * multiplier;
        let add_ten = |x: i32| x + 10;

        let composed = FnTransformerOnceOps::and_then(multiply, add_ten);
        assert_eq!(composed.apply(5), 25); // 5 * 3 + 10
    }

    #[test]
    fn test_consuming_string() {
        let owned = String::from("hello");
        let append = move |s: String| format!("{} {}", s, owned);
        let uppercase = |s: String| s.to_uppercase();

        let composed = FnTransformerOnceOps::and_then(append, uppercase);
        assert_eq!(composed.apply("world".to_string()), "WORLD HELLO");
    }

    #[test]
    fn test_parse_and_validate() {
        let parse = |s: String| s.parse::<i32>().unwrap_or(0);
        let validate = |x: i32| {
            if x > 0 { x } else { 1 }
        };

        let composed = FnTransformerOnceOps::and_then(parse, validate);
        assert_eq!(composed.apply("42".to_string()), 42);
    }

    #[test]
    fn test_parse_and_validate_negative() {
        let parse = |s: String| s.parse::<i32>().unwrap_or(0);
        let validate = |x: i32| {
            if x > 0 { x } else { 1 }
        };

        let composed = FnTransformerOnceOps::and_then(parse, validate);
        assert_eq!(composed.apply("-5".to_string()), 1);
    }
}
