#![allow(clippy::type_complexity)]
use std::{fmt, future::Future, marker::PhantomData};
use crate::and_then::{AndThen, AndThenFactory};
use crate::apply::{Apply, ApplyFactory};
use crate::ctx::ServiceCtx;
use crate::map::{Map, MapFactory};
use crate::map_err::{MapErr, MapErrFactory};
use crate::map_init_err::MapInitErr;
use crate::middleware::{ApplyMiddleware, Middleware};
use crate::then::{Then, ThenFactory};
use crate::{IntoService, IntoServiceFactory, Pipeline, Service, ServiceFactory};
pub fn chain<Svc, Req, F>(service: F) -> ServiceChain<Svc, Req>
where
    Svc: Service<Req>,
    F: IntoService<Svc, Req>,
{
    ServiceChain {
        service: service.into_service(),
        _t: PhantomData,
    }
}
pub fn chain_factory<T, R, C, F>(factory: F) -> ServiceChainFactory<T, R, C>
where
    T: ServiceFactory<R, C>,
    F: IntoServiceFactory<T, R, C>,
{
    ServiceChainFactory {
        factory: factory.into_factory(),
        _t: PhantomData,
    }
}
pub struct ServiceChain<Svc, Req> {
    service: Svc,
    _t: PhantomData<Req>,
}
impl<Svc: Service<Req>, Req> ServiceChain<Svc, Req> {
    pub fn and_then<Next, F>(self, service: F) -> ServiceChain<AndThen<Svc, Next>, Req>
    where
        Self: Sized,
        F: IntoService<Next, Svc::Response>,
        Next: Service<Svc::Response, Error = Svc::Error>,
    {
        ServiceChain {
            service: AndThen::new(self.service, service.into_service()),
            _t: PhantomData,
        }
    }
    pub fn then<Next, F>(self, service: F) -> ServiceChain<Then<Svc, Next>, Req>
    where
        Self: Sized,
        F: IntoService<Next, Result<Svc::Response, Svc::Error>>,
        Next: Service<Result<Svc::Response, Svc::Error>, Error = Svc::Error>,
    {
        ServiceChain {
            service: Then::new(self.service, service.into_service()),
            _t: PhantomData,
        }
    }
    pub fn map<F, Res>(self, f: F) -> ServiceChain<Map<Svc, F, Req, Res>, Req>
    where
        Self: Sized,
        F: Fn(Svc::Response) -> Res,
    {
        ServiceChain {
            service: Map::new(self.service, f),
            _t: PhantomData,
        }
    }
    pub fn map_err<F, Err>(self, f: F) -> ServiceChain<MapErr<Svc, F, Err>, Req>
    where
        Self: Sized,
        F: Fn(Svc::Error) -> Err,
    {
        ServiceChain {
            service: MapErr::new(self.service, f),
            _t: PhantomData,
        }
    }
    pub fn apply_fn<F, R, In, Out, Err>(
        self,
        f: F,
    ) -> ServiceChain<Apply<Svc, Req, F, R, In, Out, Err>, In>
    where
        F: Fn(In, Pipeline<Svc>) -> R,
        R: Future<Output = Result<Out, Err>>,
        Svc: Service<Req>,
        Err: From<Svc::Error>,
    {
        ServiceChain {
            service: Apply::new(self.service, f),
            _t: PhantomData,
        }
    }
    pub fn into_pipeline(self) -> Pipeline<Svc> {
        Pipeline::new(self.service)
    }
}
impl<Svc, Req> Clone for ServiceChain<Svc, Req>
where
    Svc: Clone,
{
    fn clone(&self) -> Self {
        ServiceChain {
            service: self.service.clone(),
            _t: PhantomData,
        }
    }
}
impl<Svc, Req> fmt::Debug for ServiceChain<Svc, Req>
where
    Svc: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ServiceChain")
            .field("service", &self.service)
            .finish()
    }
}
impl<Svc: Service<Req>, Req> Service<Req> for ServiceChain<Svc, Req> {
    type Response = Svc::Response;
    type Error = Svc::Error;
    crate::forward_poll_ready!(service);
    crate::forward_poll_shutdown!(service);
    #[inline]
    async fn call(
        &self,
        req: Req,
        ctx: ServiceCtx<'_, Self>,
    ) -> Result<Self::Response, Self::Error> {
        ctx.call(&self.service, req).await
    }
}
pub struct ServiceChainFactory<T, Req, C = ()> {
    factory: T,
    _t: PhantomData<(Req, C)>,
}
impl<T: ServiceFactory<Req, C>, Req, C> ServiceChainFactory<T, Req, C> {
    pub fn and_then<F, U>(
        self,
        factory: F,
    ) -> ServiceChainFactory<AndThenFactory<T, U>, Req, C>
    where
        Self: Sized,
        F: IntoServiceFactory<U, T::Response, C>,
        U: ServiceFactory<T::Response, C, Error = T::Error, InitError = T::InitError>,
    {
        ServiceChainFactory {
            factory: AndThenFactory::new(self.factory, factory.into_factory()),
            _t: PhantomData,
        }
    }
    pub fn apply<U>(self, tr: U) -> ServiceChainFactory<ApplyMiddleware<U, T, C>, Req, C>
    where
        U: Middleware<T::Service>,
    {
        ServiceChainFactory {
            factory: ApplyMiddleware::new(tr, self.factory),
            _t: PhantomData,
        }
    }
    pub fn apply_fn<F, R, In, Out, Err>(
        self,
        f: F,
    ) -> ServiceChainFactory<ApplyFactory<T, Req, C, F, R, In, Out, Err>, In, C>
    where
        F: Fn(In, Pipeline<T::Service>) -> R + Clone,
        R: Future<Output = Result<Out, Err>>,
        T: ServiceFactory<Req, C>,
        Err: From<T::Error>,
    {
        ServiceChainFactory {
            factory: ApplyFactory::new(self.factory, f),
            _t: PhantomData,
        }
    }
    pub fn then<F, U>(self, factory: F) -> ServiceChainFactory<ThenFactory<T, U>, Req, C>
    where
        Self: Sized,
        C: Clone,
        F: IntoServiceFactory<U, Result<T::Response, T::Error>, C>,
        U: ServiceFactory<
            Result<T::Response, T::Error>,
            C,
            Error = T::Error,
            InitError = T::InitError,
        >,
    {
        ServiceChainFactory {
            factory: ThenFactory::new(self.factory, factory.into_factory()),
            _t: PhantomData,
        }
    }
    pub fn map<F, Res>(
        self,
        f: F,
    ) -> ServiceChainFactory<MapFactory<T, F, Req, Res, C>, Req, C>
    where
        Self: Sized,
        F: Fn(T::Response) -> Res + Clone,
    {
        ServiceChainFactory {
            factory: MapFactory::new(self.factory, f),
            _t: PhantomData,
        }
    }
    pub fn map_err<F, E>(
        self,
        f: F,
    ) -> ServiceChainFactory<MapErrFactory<T, Req, C, F, E>, Req, C>
    where
        Self: Sized,
        F: Fn(T::Error) -> E + Clone,
    {
        ServiceChainFactory {
            factory: MapErrFactory::new(self.factory, f),
            _t: PhantomData,
        }
    }
    pub fn map_init_err<F, E>(
        self,
        f: F,
    ) -> ServiceChainFactory<MapInitErr<T, Req, C, F, E>, Req, C>
    where
        Self: Sized,
        F: Fn(T::InitError) -> E + Clone,
    {
        ServiceChainFactory {
            factory: MapInitErr::new(self.factory, f),
            _t: PhantomData,
        }
    }
    pub async fn pipeline(&self, cfg: C) -> Result<Pipeline<T::Service>, T::InitError>
    where
        Self: Sized,
    {
        Ok(Pipeline::new(self.factory.create(cfg).await?))
    }
}
impl<T, R, C> Clone for ServiceChainFactory<T, R, C>
where
    T: Clone,
{
    fn clone(&self) -> Self {
        ServiceChainFactory {
            factory: self.factory.clone(),
            _t: PhantomData,
        }
    }
}
impl<T, R, C> fmt::Debug for ServiceChainFactory<T, R, C>
where
    T: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ServiceChainFactory")
            .field("factory", &self.factory)
            .finish()
    }
}
impl<T: ServiceFactory<R, C>, R, C> ServiceFactory<R, C> for ServiceChainFactory<T, R, C> {
    type Response = T::Response;
    type Error = T::Error;
    type Service = T::Service;
    type InitError = T::InitError;
    #[inline]
    async fn create(&self, cfg: C) -> Result<Self::Service, Self::InitError> {
        self.factory.create(cfg).await
    }
}