use core::{fmt, sync::atomic::AtomicBool};

use alloc::sync::Arc;
use futures_util::{future::BoxFuture, task::ArcWake, Future, FutureExt};
use libk_util::sync::IrqSafeSpinlock;

use super::executor;

pub trait Termination {
    fn print(&self);
}

pub struct Task {
    pub(super) future: IrqSafeSpinlock<Option<BoxFuture<'static, ()>>>,
    pub(super) enqueued: AtomicBool,
}

impl ArcWake for Task {
    fn wake_by_ref(arc_self: &Arc<Self>) {
        executor::enqueue(arc_self.clone()).unwrap();
    }
}

impl Task {
    pub fn new<T: Termination, F: Future<Output = T> + Send + 'static>(future: F) -> Arc<Self> {
        let future = IrqSafeSpinlock::new(Some(
            async move {
                future.await.print();
            }
            .boxed(),
        ));
        Arc::new(Self {
            future,
            enqueued: AtomicBool::new(false),
        })
    }
}

impl Termination for () {
    fn print(&self) {}
}

impl Termination for ! {
    fn print(&self) {}
}

impl<T, E: fmt::Debug> Termination for Result<T, E> {
    fn print(&self) {
        if let Err(error) = self {
            log::error!("A task finished with an error: {:?}", error);
        }
    }
}
