Documentation
// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

use crate::{
    all_the_tuples,
    extract::{Exclusive, FromContext, FromContextOnce},
    BoxError, BoxFuture,
};
use std::{future::Future, marker::PhantomData, pin::Pin};

pub type HandlerResult = Result<(), BoxError>;

/// Result for generic handlers.
///
/// This Result type enables several return types for filters and configurations.
pub trait IntoHandlerResult {
    fn into_handler_result(self) -> HandlerResult;
}

impl IntoHandlerResult for () {
    fn into_handler_result(self) -> HandlerResult {
        Ok(())
    }
}

impl<E> IntoHandlerResult for Result<(), E>
where
    E: Into<BoxError>,
{
    fn into_handler_result(self) -> HandlerResult {
        match self {
            Ok(()) => Ok(()),
            Err(err) => Err(err.into()),
        }
    }
}

#[derive(thiserror::Error, Debug)]
#[error("Extraction error: {0}")]
pub struct ExtractionError(pub Box<dyn std::error::Error>);

pub trait Handler<C> {
    type Output;

    type Future<'h>: Future<Output = Result<Self::Output, ExtractionError>>
    where
        Self: 'h,
        C: 'h;

    #[must_use]
    fn call<'h>(&'h self, context: C) -> Self::Future<'h>
    where
        C: 'h;
}

pub struct HandlerFunction<F, I> {
    function: F,
    _arguments: PhantomData<I>,
}

impl<F: Clone, I> Clone for HandlerFunction<F, I> {
    fn clone(&self) -> Self {
        Self {
            function: self.function.clone(),
            _arguments: PhantomData,
        }
    }
}

impl<F, I> HandlerFunction<F, I> {
    fn new(function: F) -> Self {
        Self {
            function,
            _arguments: PhantomData,
        }
    }
}

impl<C, F, Fut> Handler<C> for HandlerFunction<F, ()>
where
    F: Fn() -> Fut,
    Fut: Future,
{
    type Output = Fut::Output;

    type Future<'h>
        = BoxFuture<'h, Result<Self::Output, ExtractionError>>
    where
        Self: 'h,
        C: 'h;

    fn call<'h>(&'h self, _: C) -> Self::Future<'h>
    where
        C: 'h,
    {
        // Ideally this should be resolved with async traits when Rust supports them.
        Box::pin(async move { Ok((self.function)().await) })
    }
}

macro_rules! impl_handler_function {
    (
        $first:ident $(,$ty:ident)*
    ) => {

        #[allow(non_snake_case)]
        impl<C, F, Fut, M, $first, $($ty,)* > Handler<C> for
            HandlerFunction<F, (M, $first, $($ty,)*)>
        where
            Fut: Future,
            F: Fn($first, $($ty,)* ) -> Fut,
            $first: FromContextOnce<C, M>,
            $($ty: FromContext<C>,)*
        {
            type Output = <Fut as Future>::Output;

            type Future<'h> = Pin<Box<dyn Future<Output = Result<Self::Output, ExtractionError>> + 'h>>
            where Self: 'h, C: 'h;

            fn call<'h>(&'h self, context: C) -> Self::Future<'h>
            where
                C: 'h,
            {
                #[allow(unused_mut)]
                async fn extract<'c, C, M, $first, $($ty,)*>(context: &C) ->
                    Result<($first, $($ty,)*), ExtractionError>
                where
                    $first: FromContextOnce<C, M>,
                    $($ty: FromContext<C>),*
                {
                    let exclusive_context = Exclusive::new(context);
                    Ok((
                        $first::from_context_once(exclusive_context)
                            .await
                            .map_err(|e| ExtractionError(e.into()))?,
                        $($ty::from_context(context).map_err(|e| ExtractionError(e.into()))?,)*
                    ))
                }

                // Ideally this should be implemented with async traits when Rust support them.
                Box::pin(async move {
                    let ($first, $($ty,)*) = extract::<C, M, $first, $($ty,)*>(&context).await?;
                    Ok((self.function)($first, $($ty,)*).await)
                })
            }
        }
    }
}

all_the_tuples!(impl_handler_function);

mod private {
    pub enum HandlerIntoHandlerArgs {}
}

pub trait IntoHandler<C, I> {
    type Output;

    type Handler: Handler<C, Output = Self::Output>;

    fn into_handler(self) -> Self::Handler;
}

impl<C, H> IntoHandler<C, private::HandlerIntoHandlerArgs> for H
where
    H: Handler<C>,
{
    type Output = H::Output;

    type Handler = Self;

    fn into_handler(self) -> Self::Handler {
        self
    }
}

impl<C, F, Fut> IntoHandler<C, ()> for F
where
    F: Fn() -> Fut,
    Fut: Future,
{
    type Output = Fut::Output;

    type Handler = HandlerFunction<F, ()>;

    fn into_handler(self) -> Self::Handler {
        HandlerFunction::new(self)
    }
}

macro_rules! impl_into_handler {
    (
        $first:ident $(,$ty:ident)*
    ) => {
        #[allow(non_snake_case)]
        impl<C, F, Fut, M, $first, $($ty,)*> IntoHandler<C, (M, $first, $($ty,)*)> for F
        where
            Fut: Future,
            F: Fn($first, $($ty,)* ) -> Fut,
            $first: FromContextOnce<C, M>,
            $($ty: FromContext<C>,)*
        {
            type Output = Fut::Output;

            type Handler = HandlerFunction<F, (M, $first, $($ty,)*)>;

            fn into_handler(self) -> Self::Handler {
                HandlerFunction::new(self)
            }
        }
    }
}

all_the_tuples!(impl_into_handler);