use tower::Layer;
use tracing::Span;
use crate::{enrich_span_fn, make_span_fn, utils::FnWrapper};
#[derive(Clone, Debug)]
pub struct TraceLayer<MakeSpan, OnResponse = (), OnError = ()> {
    make_span: MakeSpan,
    on_response: OnResponse,
    on_error: OnError,
}
impl<F> TraceLayer<FnWrapper<F>> {
    #[must_use]
    pub fn from_fn<T>(f: F) -> TraceLayer<FnWrapper<F>>
    where
        F: Fn(&T) -> Span,
    {
        TraceLayer::new(make_span_fn(f))
    }
}
impl<MakeSpan> TraceLayer<MakeSpan> {
    #[must_use]
    pub fn new(make_span: MakeSpan) -> Self {
        Self {
            make_span,
            on_response: (),
            on_error: (),
        }
    }
}
impl<MakeSpan, OnResponse, OnError> TraceLayer<MakeSpan, OnResponse, OnError> {
    #[must_use]
    pub fn on_response<NewOnResponse>(
        self,
        on_response: NewOnResponse,
    ) -> TraceLayer<MakeSpan, NewOnResponse, OnError> {
        TraceLayer {
            make_span: self.make_span,
            on_response,
            on_error: self.on_error,
        }
    }
    #[must_use]
    pub fn on_response_fn<F, T>(self, f: F) -> TraceLayer<MakeSpan, FnWrapper<F>, OnError>
    where
        F: Fn(&Span, &T),
    {
        self.on_response(enrich_span_fn(f))
    }
    #[must_use]
    pub fn on_error<NewOnError>(
        self,
        on_error: NewOnError,
    ) -> TraceLayer<MakeSpan, OnResponse, NewOnError> {
        TraceLayer {
            make_span: self.make_span,
            on_response: self.on_response,
            on_error,
        }
    }
    pub fn on_error_fn<F, E>(self, f: F) -> TraceLayer<MakeSpan, OnResponse, FnWrapper<F>>
    where
        F: Fn(&Span, &E),
    {
        self.on_error(enrich_span_fn(f))
    }
}
impl<S, MakeSpan, OnResponse, OnError> Layer<S> for TraceLayer<MakeSpan, OnResponse, OnError>
where
    MakeSpan: Clone,
    OnResponse: Clone,
    OnError: Clone,
{
    type Service = super::service::TraceService<S, MakeSpan, OnResponse, OnError>;
    fn layer(&self, inner: S) -> Self::Service {
        super::service::TraceService::new(
            inner,
            self.make_span.clone(),
            self.on_response.clone(),
            self.on_error.clone(),
        )
    }
}