/*******************************************************************************
 *
 *    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
#[cfg(test)]
mod tests {
    use std::{
        io,
        panic::{
            AssertUnwindSafe,
            catch_unwind,
        },
        sync::{
            Arc,
            atomic::{
                AtomicBool,
                AtomicUsize,
                Ordering,
            },
        },
    };

    use qubit_dcl::{
        DoubleCheckedLockExecutor,
        double_checked::{
            ExecutionResult,
            ExecutorError,
        },
    };
    use qubit_lock::{
        ArcMutex,
        lock::Lock,
    };

    mod test_executor_lock_builder {
        use super::*;

        fn increment_and_return_task(value: &mut i32) -> Result<i32, io::Error> {
            *value += 1;
            Ok(*value)
        }

        #[test]
        fn test_second_condition_failure_after_prepare_rolls_back() {
            let data = ArcMutex::new(10);
            let checks = Arc::new(AtomicUsize::new(0));
            let rolled_back = Arc::new(AtomicBool::new(false));
            let executor = DoubleCheckedLockExecutor::builder()
                .on(data.clone())
                .when({
                    let checks = checks.clone();
                    move || checks.fetch_add(1, Ordering::AcqRel) == 0
                })
                .log_unmet_condition(log::Level::Info, "condition not met")
                .prepare(|| Ok::<(), io::Error>(()))
                .rollback_prepare({
                    let rolled_back = rolled_back.clone();
                    move || {
                        rolled_back.store(true, Ordering::Release);
                        Ok::<(), io::Error>(())
                    }
                })
                .build();

            let result = executor
                .call_with(increment_and_return_task as fn(&mut i32) -> Result<i32, io::Error>)
                .get_result();

            assert!(matches!(result, ExecutionResult::ConditionNotMet));
            assert!(rolled_back.load(Ordering::Acquire));
            assert_eq!(data.read(|value| *value), 10);
            assert_eq!(checks.load(Ordering::Acquire), 2);
        }

        #[test]
        fn test_prepare_rollback_failure_with_logger_replaces_condition_result() {
            let data = ArcMutex::new(10);
            let checks = Arc::new(AtomicUsize::new(0));
            let executor = DoubleCheckedLockExecutor::builder()
                .on(data)
                .when({
                    let checks = checks.clone();
                    move || checks.fetch_add(1, Ordering::AcqRel) == 0
                })
                .log_unmet_condition(log::Level::Error, "condition not met")
                .prepare(|| Ok::<(), io::Error>(()))
                .rollback_prepare(|| Err::<(), _>(io::Error::other("rollback failed")))
                .build();

            let result = executor
                .call_with(increment_and_return_task as fn(&mut i32) -> Result<i32, io::Error>)
                .get_result();

            assert!(matches!(
                result,
                ExecutionResult::Failed(ExecutorError::PrepareRollbackFailed {
                    original,
                    rollback,
                }) if original.message() == "Condition not met" && rollback.message() == "rollback failed"
            ));
        }

        #[test]
        fn test_prepare_logger_methods_on_lock_builder_are_chainable() {
            let data = ArcMutex::new(1);
            let executor = DoubleCheckedLockExecutor::builder()
                .on(data)
                .log_prepare_failure(log::Level::Warn, "prepare failed")
                .log_prepare_commit_failure(log::Level::Error, "prepare commit failed")
                .log_prepare_rollback_failure(log::Level::Info, "prepare rollback failed")
                .disable_unmet_condition_logging()
                .disable_prepare_failure_logging()
                .disable_prepare_commit_failure_logging()
                .disable_prepare_rollback_failure_logging()
                .when(|| true)
                .build();

            let result = executor
                .call_with(|value: &mut i32| Ok::<i32, io::Error>(*value))
                .get_result();

            assert!(matches!(result, ExecutionResult::Success(1)));
        }

        #[test]
        fn test_lock_builder_set_catch_panics_enables_panic_capture() {
            let data = ArcMutex::new(1);
            let executor = DoubleCheckedLockExecutor::builder()
                .on(data)
                .set_catch_panics(true)
                .when(|| true)
                .build();

            let result = executor
                .execute(|| -> Result<(), io::Error> {
                    panic!("panic from lock builder");
                })
                .get_result();

            assert!(matches!(
                result,
                ExecutionResult::Failed(ExecutorError::Panic(_))
            ));
        }

        #[test]
        fn test_lock_builder_disable_catch_panics_allows_panic() {
            let data = ArcMutex::new(1);
            let executor = DoubleCheckedLockExecutor::builder()
                .on(data)
                .catch_panics()
                .disable_catch_panics()
                .when(|| true)
                .build();

            let caught = catch_unwind(AssertUnwindSafe(|| {
                executor
                    .execute(|| -> Result<(), io::Error> {
                        panic!("panic should propagate");
                    })
                    .get_result();
            }));

            assert!(caught.is_err());
        }
    }
}
