use std::{
    marker::PhantomData,
    task::{Context, Poll},
};
use tower::Service;
use super::OperationShape;
pub trait OperationService<Op, Exts>: Service<Self::Normalized, Response = Op::Output, Error = Op::Error>
where
    Op: OperationShape,
{
    type Normalized;
    fn normalize(input: Op::Input, exts: Exts) -> Self::Normalized;
}
impl<Op, S> OperationService<Op, ()> for S
where
    Op: OperationShape,
    S: Service<Op::Input, Response = Op::Output, Error = Op::Error>,
{
    type Normalized = Op::Input;
    fn normalize(input: Op::Input, _exts: ()) -> Self::Normalized {
        input
    }
}
impl<Op, Ext0, S> OperationService<Op, (Ext0,)> for S
where
    Op: OperationShape,
    S: Service<(Op::Input, Ext0), Response = Op::Output, Error = Op::Error>,
{
    type Normalized = (Op::Input, Ext0);
    fn normalize(input: Op::Input, exts: (Ext0,)) -> Self::Normalized {
        (input, exts.0)
    }
}
impl<Op, Ext0, Ext1, S> OperationService<Op, (Ext0, Ext1)> for S
where
    Op: OperationShape,
    S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = Op::Error>,
{
    type Normalized = (Op::Input, Ext0, Ext1);
    fn normalize(input: Op::Input, exts: (Ext0, Ext1)) -> Self::Normalized {
        (input, exts.0, exts.1)
    }
}
pub trait OperationServiceExt<Op, Exts>: OperationService<Op, Exts>
where
    Op: OperationShape,
{
    fn normalize(self) -> Normalize<Op, Self>
    where
        Self: Sized,
    {
        Normalize {
            inner: self,
            _operation: PhantomData,
        }
    }
}
impl<F, Op, Exts> OperationServiceExt<Op, Exts> for F
where
    Op: OperationShape,
    F: OperationService<Op, Exts>,
{
}
#[derive(Debug)]
pub struct Normalize<Op, S> {
    pub(crate) inner: S,
    pub(crate) _operation: PhantomData<Op>,
}
impl<Op, S> Clone for Normalize<Op, S>
where
    S: Clone,
{
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
            _operation: PhantomData,
        }
    }
}
impl<Op, S, Exts> Service<(Op::Input, Exts)> for Normalize<Op, S>
where
    Op: OperationShape,
    S: OperationService<Op, Exts>,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = <S as Service<S::Normalized>>::Future;
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.inner.poll_ready(cx)
    }
    fn call(&mut self, (input, exts): (Op::Input, Exts)) -> Self::Future {
        let req = S::normalize(input, exts);
        self.inner.call(req)
    }
}