use std::{fmt::Debug, sync::Arc};

type HandlerLink<T, E> = Arc<Vec<Arc<Box<dyn ChannelHandler_<T, E>>>>>;

pub struct ProceedingJoinPoint<T, E>
where
    E: std::error::Error,
{
    index: usize,
    link: HandlerLink<T, E>,
    pub context: T,
}

impl<T, E> ProceedingJoinPoint<T, E>
where
    E: std::error::Error,
{
    pub fn new(link: HandlerLink<T, E>, context: T) -> Self {
        Self {
            index: 0,
            link,
            context,
        }
    }
    pub fn into_context(self) -> T {
        self.context
    }
    pub async fn proceed(mut self) -> Result<T, E> {
        match self.link.get(self.index) {
            Some(filter) => {
                self.index += 1;
                filter.clone().call(self).await
            }
            None => Ok(self.into_context()),
        }
    }
}

pub type BoxFuture<'a, T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send + 'a>>;

pub trait ChannelHandler_<T, E>: Send + Sync + Debug
where
    E: std::error::Error,
{
    fn call<'a>(&'a self, join_point: ProceedingJoinPoint<T, E>) -> BoxFuture<'a, Result<T, E>>;
}

#[allow(async_fn_in_trait)]
pub trait ChannelHandler<T, E>
where
    E: std::error::Error,
{
    async fn aroud(&self, join_point: ProceedingJoinPoint<T, E>) -> Result<T, E>;
}

#[derive(Debug)]
pub enum HandlerInvoker<T, E> {
    ChannelHandler(Box<dyn ChannelHandler_<T, E>>),
}

#[derive(Debug)]
pub struct Handler<T, E> {
    pub id: String,
    pub handler_invoker: HandlerInvoker<T, E>,
}

pub trait HandlerLoad {
    fn load<T, E>(self) -> Handler<T, E>;
}
