/*******************************************************************************
 *
 *    Copyright (c) 2025 - 2026 Haixing Hu.
 *
 *    SPDX-License-Identifier: Apache-2.0
 *
 *    Licensed under the Apache License, Version 2.0.
 *
 ******************************************************************************/

//! Unit tests for runnable task types.

use std::{
    cell::Cell,
    io,
    rc::Rc,
    sync::{
        Arc,
        atomic::{
            AtomicUsize,
            Ordering,
        },
    },
};

use qubit_function::{
    ArcRunnable,
    BoxRunnable,
    Callable,
    RcRunnable,
    Runnable,
    SupplierOnce,
};

#[derive(Clone)]
struct ClonedRunnable {
    flag: Rc<Cell<bool>>,
}

impl Runnable<io::Error> for ClonedRunnable {
    fn run(&mut self) -> Result<(), io::Error> {
        self.flag.set(true);
        Ok(())
    }
}

#[test]
fn test_runnable_closure_run_returns_success() {
    let flag = Rc::new(Cell::new(false));
    let captured = Rc::clone(&flag);
    let mut task = move || {
        captured.set(true);
        Ok::<(), io::Error>(())
    };

    task.run().expect("runnable closure should succeed");
    assert!(flag.get());
}

#[test]
fn test_runnable_closure_run_returns_error() {
    let mut task = || Err::<(), _>(io::Error::other("failed"));

    let error = task.run().expect_err("runnable closure should fail");
    assert_eq!(error.kind(), io::ErrorKind::Other);
    assert_eq!(error.to_string(), "failed");
}

#[test]
fn test_runnable_closure_into_box_executes_once() {
    let flag = Rc::new(Cell::new(false));
    let captured = Rc::clone(&flag);
    let task = move || {
        captured.set(true);
        Ok::<(), io::Error>(())
    };

    let mut boxed = Runnable::into_box(task);

    boxed.run().expect("boxed runnable should succeed");
    assert!(flag.get());
}

#[test]
fn test_runnable_closure_into_fn_returns_fn_once() {
    let task = || Ok::<(), io::Error>(());

    let mut function = Runnable::into_fn(task);

    function().expect("runnable function should succeed");
}

#[test]
fn test_runnable_to_box_clones_runnable() {
    let flag = Rc::new(Cell::new(false));
    let mut task = ClonedRunnable {
        flag: Rc::clone(&flag),
    };

    let mut boxed = task.to_box();

    boxed.run().expect("boxed clone should succeed");
    assert!(flag.get());

    flag.set(false);
    task.run().expect("original runnable should remain usable");
    assert!(flag.get());
}

#[test]
fn test_runnable_to_fn_clones_runnable() {
    let flag = Rc::new(Cell::new(false));
    let mut task = ClonedRunnable {
        flag: Rc::clone(&flag),
    };

    let mut function = task.to_fn();

    function().expect("cloned runnable should succeed");
    assert!(flag.get());

    flag.set(false);
    drop(function);
    task.run().expect("original runnable should remain usable");
    assert!(flag.get());
}

#[derive(Clone)]
struct SharedCounterRunnable {
    count: Rc<Cell<u32>>,
}

impl Runnable<io::Error> for SharedCounterRunnable {
    fn run(&mut self) -> Result<(), io::Error> {
        self.count.set(self.count.get() + 1);
        Ok(())
    }
}

#[derive(Clone)]
struct SharedAtomicRunnable {
    count: Arc<AtomicUsize>,
}

impl Runnable<io::Error> for SharedAtomicRunnable {
    fn run(&mut self) -> Result<(), io::Error> {
        self.count.fetch_add(1, Ordering::SeqCst);
        Ok(())
    }
}

#[test]
fn test_runnable_default_into_callable_returns_unit() {
    let flag = Rc::new(Cell::new(false));
    let task = ClonedRunnable {
        flag: Rc::clone(&flag),
    };

    let mut callable = Runnable::into_callable(task);

    callable.call().expect("default callable should succeed");
    assert!(flag.get());
}

#[test]
fn test_runnable_into_rc_runnable_preserves_name_and_state() {
    let count = Rc::new(Cell::new(0));
    let captured = Rc::clone(&count);
    let task = BoxRunnable::new_with_name("shared", move || {
        captured.set(captured.get() + 1);
        Ok::<(), io::Error>(())
    });

    let mut shared = Runnable::into_rc(task);
    let mut clone = shared.clone();

    assert_eq!(shared.name(), Some("shared"));
    shared.run().expect("shared runnable should succeed");
    clone.run().expect("shared clone should succeed");
    assert_eq!(count.get(), 2);
}

#[test]
fn test_runnable_to_rc_clones_source_after_boxed_runnable() {
    let count = Rc::new(Cell::new(0));
    let mut source = SharedCounterRunnable {
        count: Rc::clone(&count),
    };
    let mut shared = source.to_rc();
    let mut shared_clone = shared.clone();

    shared
        .run()
        .expect("to_rc should build shared runnable from cloneable source");
    shared_clone
        .run()
        .expect("shared clone should also execute");
    source
        .run()
        .expect("original cloneable runnable should remain usable");

    assert_eq!(count.get(), 3);
}

#[test]
fn test_runnable_into_arc_runnable_preserves_name_and_state() {
    let count = Arc::new(AtomicUsize::new(0));
    let captured = Arc::clone(&count);
    let task = move || {
        captured.fetch_add(1, Ordering::SeqCst);
        Ok::<(), io::Error>(())
    };

    let mut shared = Runnable::into_arc(task);
    assert_eq!(count.load(Ordering::SeqCst), 0);

    shared.run().expect("shared runnable should succeed");
    shared
        .run()
        .expect("shared runnable should execute repeatedly");
    assert_eq!(count.load(Ordering::SeqCst), 2);
}

#[test]
fn test_runnable_to_arc_clones_source_after_boxed_runnable() {
    let count = Arc::new(AtomicUsize::new(0));
    let mut source = SharedAtomicRunnable {
        count: Arc::clone(&count),
    };
    let mut shared = source.to_arc();
    let mut shared_clone = shared.clone();

    shared
        .run()
        .expect("to_arc should build shared runnable from cloneable source");
    shared_clone
        .run()
        .expect("shared arc clone should also execute");
    source
        .run()
        .expect("original cloneable runnable should remain usable");

    assert_eq!(count.load(Ordering::SeqCst), 3);
}

#[test]
fn test_rc_runnable_from_supplier() {
    let count = Rc::new(Cell::new(0));
    let captured = Rc::clone(&count);
    let mut task = RcRunnable::from_supplier(move || {
        captured.set(captured.get() + 1);
        Ok::<(), io::Error>(())
    });

    task.run().expect("supplier runnable should succeed");
    task.run().expect("supplier runnable should succeed again");
    assert_eq!(count.get(), 2);
}

#[test]
fn test_arc_runnable_from_supplier() {
    let count = Arc::new(AtomicUsize::new(0));
    let captured = Arc::clone(&count);
    let mut task = ArcRunnable::from_supplier(move || {
        captured.fetch_add(1, Ordering::SeqCst);
        Ok::<(), io::Error>(())
    });

    task.run().expect("supplier runnable should succeed");
    task.run().expect("supplier runnable should succeed again");
    assert_eq!(count.load(Ordering::SeqCst), 2);
}

#[test]
fn test_box_runnable_new_and_run() {
    let flag = Rc::new(Cell::new(false));
    let captured = Rc::clone(&flag);
    let mut task = BoxRunnable::new(move || {
        captured.set(true);
        Ok::<(), io::Error>(())
    });

    task.run().expect("box runnable should succeed");
    assert!(flag.get());
}

#[test]
fn test_box_runnable_name_management() {
    let mut task = BoxRunnable::<io::Error>::new_with_name("cleanup", || Ok(()));

    assert_eq!(task.name(), Some("cleanup"));
    assert_eq!(task.to_string(), "BoxRunnable(cleanup)");
    assert!(format!("{task:?}").contains("cleanup"));

    task.set_name("renamed");
    assert_eq!(task.name(), Some("renamed"));

    task.clear_name();
    assert_eq!(task.name(), None);
    assert_eq!(task.to_string(), "BoxRunnable");
}

#[test]
fn test_box_runnable_set_name_handles_empty_and_same_name() {
    let mut task = BoxRunnable::<io::Error>::new(|| Ok(()));

    assert_eq!(task.name(), None);
    task.set_name("cleanup");
    assert_eq!(task.name(), Some("cleanup"));
    task.set_name("cleanup");
    assert_eq!(task.name(), Some("cleanup"));
}

#[test]
fn test_box_runnable_into_box_returns_self() {
    let task = BoxRunnable::new(|| Ok::<(), io::Error>(()));

    let mut boxed = Runnable::into_box(task);

    boxed.run().expect("boxed runnable should succeed");
}

#[test]
fn test_box_runnable_into_fn_extracts_function() {
    let task = BoxRunnable::new(|| Ok::<(), io::Error>(()));

    let mut function = Runnable::into_fn(task);

    function().expect("runnable function should succeed");
}

#[test]
fn test_box_runnable_from_supplier() {
    let supplier = || Ok::<(), io::Error>(());

    let mut task = BoxRunnable::from_supplier(supplier);

    task.run().expect("supplier-backed runnable should succeed");
}

#[test]
fn test_box_runnable_implements_supplier_once() {
    let task = BoxRunnable::new(|| Ok::<(), io::Error>(()));

    let result = SupplierOnce::get(task);

    result.expect("supplier runnable should succeed");
}

#[test]
fn test_box_runnable_and_then_runs_next_on_success() {
    let events = Rc::new(Cell::new(0));
    let first_events = Rc::clone(&events);
    let second_events = Rc::clone(&events);
    let first = BoxRunnable::new(move || {
        first_events.set(1);
        Ok::<(), io::Error>(())
    });
    let second = move || {
        second_events.set(2);
        Ok::<(), io::Error>(())
    };

    let mut chained = first.and_then(second);

    chained.run().expect("chained runnable should succeed");
    assert_eq!(events.get(), 2);
}

#[test]
fn test_box_runnable_and_then_skips_next_on_error() {
    let events = Rc::new(Cell::new(0));
    let second_events = Rc::clone(&events);
    let first = BoxRunnable::new(|| Err::<(), _>(io::Error::other("stop")));
    let second = move || {
        second_events.set(2);
        Ok::<(), io::Error>(())
    };

    let mut chained = first.and_then(second);

    assert_eq!(
        chained
            .run()
            .expect_err("chained runnable should preserve error")
            .to_string(),
        "stop",
    );
    assert_eq!(events.get(), 0);
}

#[test]
fn test_box_runnable_then_callable_runs_callable_on_success() {
    let task = BoxRunnable::new_with_name("prepare", || Ok::<(), io::Error>(()));
    let callable = || Ok::<i32, io::Error>(42);

    let mut chained = task.then_callable(callable);

    assert_eq!(chained.name(), Some("prepare"));
    assert_eq!(chained.call().expect("callable should succeed"), 42);
}

#[test]
fn test_box_runnable_then_callable_skips_callable_on_error() {
    let callable_ran = Rc::new(Cell::new(false));
    let callable_ran_capture = Rc::clone(&callable_ran);
    let task = BoxRunnable::<io::Error>::new(|| Err(io::Error::other("prepare failed")));
    let callable = move || {
        callable_ran_capture.set(true);
        Ok::<i32, io::Error>(42)
    };

    let mut chained = task.then_callable(callable);
    let error = chained
        .call()
        .expect_err("then_callable should preserve runnable error");

    assert_eq!(error.to_string(), "prepare failed");
    assert!(!callable_ran.get());
}

#[test]
fn test_runnable_into_callable_returns_unit_callable() {
    let task = BoxRunnable::new_with_name("cleanup", || Ok::<(), io::Error>(()));

    let mut callable = task.into_callable();

    assert_eq!(callable.name(), Some("cleanup"));
    callable.call().expect("unit callable should succeed");
}

#[test]
fn test_runnable_into_callable_preserves_error() {
    let task = BoxRunnable::<io::Error>::new_with_name("cleanup", || {
        Err(io::Error::other("cleanup failed"))
    });

    let mut callable = task.into_callable();
    let error = callable
        .call()
        .expect_err("unit callable should preserve runnable error");

    assert_eq!(callable.name(), Some("cleanup"));
    assert_eq!(error.to_string(), "cleanup failed");
}
