use crate::core::*;
pub use crate::handlers;
pub use noema_macros::input;

/// Input trait for inputs producing output of type Output
/// # Examples
/// ```ignore
/// #[input(usize)]
/// pub struct MyInput {
///     pub data: String,
/// }
/// ```
pub trait Input {
    fn name(&self) -> String {
        std::any::type_name::<Self>().to_string()
    }
    fn description(&self) -> String {
        format!("Input of type {}", self.name())
    }
    type Output;
}

/// Handler of an Input type T, producing an output of type T::Output
/// Examples
/// ```ignore
/// // First Declare Input type
/// #[input(usize)]
/// pub struct MyInput {
///     pub data: String,
/// }
///
/// // Then implement InputHandler for it
/// #[derive(Injectable)]
/// struct MyInputHandler {}
///
/// #[async_trait]
/// impl InputHandler<MyInput> for MyInputHandler {
///     async fn handle(&self, input: Arc<MyInput>) -> NoemaResult<MyInput as Input>::Output> {
///         Ok(input.data.len()) // Return length of input data
///     }
/// }
/// ```
#[async_trait::async_trait]
pub trait InputHandler<T: Input + Send + Sync> {
    async fn handle(&self, input: Arc<T>) -> NoemaResult<T::Output>;
}

/// Sender for Input type I
#[async_trait::async_trait]
pub trait Sender<I: Input + Send + Sync + 'static> {
    /// Get the input handler for the input type I
    fn get_input_handler() -> arc_dyn!(InputHandler<I>);

    /// Send input and get output
    async fn send(input: I) -> NoemaResult<I::Output> {
        let input = Arc::new(input);
        let handler = Self::get_input_handler();
        handler.handle(input).await
    }
}

/// Macro to define handlers for input types
/// HANDLER MUST IMPLEMENT Injectable TRAIT
///
/// Supports multiple handler registrations:
/// - Input: Handler
///
/// # Examples
/// ```ignore
/// handlers!(
///     MyInput: MyInputHandler,
///     MyOtherInput: MyOtherInputHandler,
///     ThirdInput: ThirdInputHandler
/// );
/// ```
#[macro_export]
macro_rules! handlers {
    // Entry point - process all items
    ($($item:tt)*) => {
        $crate::__handlers_internal!($($item)*);
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __handlers_internal {
    // Base case - empty
    () => {};

    // Input: Handler, more items
    ($tinput:path: $thandler:ty, $($rest:tt)*) => {
        const _: () = {
            use $crate::core::*;
            use $crate::sender::{InputHandler, Sender};

            impl Sender<$tinput> for Container {
                fn get_input_handler() -> arc_dyn!(InputHandler<$tinput>) {
                    let handler = <$thandler>::inject(&Container);
                    Arc::new(handler) as Arc<dyn InputHandler<$tinput> + Send + Sync>
                }
            }
        };
        $crate::__handlers_internal!($($rest)*);
    };

    // Input: Handler, last item
    ($tinput:path: $thandler:ty) => {
        const _: () = {
            use $crate::core::*;
            use $crate::sender::{InputHandler, Sender};

            impl Sender<$tinput> for Container {
                fn get_input_handler() -> arc_dyn!(InputHandler<$tinput>) {
                    let handler = <$thandler>::inject(&Container);
                    Arc::new(handler) as Arc<dyn InputHandler<$tinput> + Send + Sync>
                }
            }
        };
    };
}

/// Send an input and get the output
/// # Examples
/// ```ignore
/// let input = MyInput { data: "Hello".to_string() };
/// let output: usize = send(input).await.unwrap(); // output will be 5
/// ```
pub async fn send<I: Input + Send + Sync + 'static>(input: I) -> Result<I::Output, BoxDynError>
where
    Container: Sender<I>,
{
    <Container as Sender<I>>::send(input).await
}

#[cfg(test)]
mod test {
    use crate::core::*;

    #[test]
    fn mediator_test() {
        use crate::sender::{Input, InputHandler, input, send};
        use async_trait::async_trait;
        use futures::executor::block_on;
        struct Increaser {}
        impl Increaser {
            fn add(&self, value: u32) -> u32 {
                value + 1
            }
        }
        #[input(u32)]
        struct TestInput {
            pub value: u32,
        }
        #[derive(Injectable)]
        struct TestInputHandler {
            increaser: Arc<Increaser>,
        }
        #[async_trait]
        impl InputHandler<TestInput> for TestInputHandler {
            async fn handle(
                &self,
                input: Arc<TestInput>,
            ) -> NoemaResult<<TestInput as Input>::Output> {
                Ok(self.increaser.add(input.value))
            }
        }
        crate::handlers!(TestInput: TestInputHandler); // Register handlers
        impl Resolver<Increaser> for Container {
            fn resolve() -> Arc<Increaser> {
                Arc::new(Increaser {})
            }
        }
        let output = block_on(send(TestInput { value: 41 })).unwrap(); // Send
        assert!(output == 42); // Check output
    }
}
