#![deny(clippy::unwrap_used, clippy::dbg_macro, clippy::unimplemented, clippy::todo, clippy::missing_safety_doc)]
#![warn(
    clippy::missing_errors_doc,
    clippy::indexing_slicing,
    clippy::inline_always,
    clippy::fn_params_excessive_bools,
    missing_debug_implementations
)]
pub mod backend_service;
pub mod body;
pub mod extension;
pub mod extractor;
pub mod helper_layers;
pub mod listener;
pub mod service;
pub mod utils;
pub use backend_service::ArcHyperService;
pub use body::SgBody;
use extension::Reflect;
pub use extractor::Extract;
use hyper::{body::Bytes, Request, Response, StatusCode};
use std::{convert::Infallible, fmt};
pub use tokio_util::sync::CancellationToken;
pub use tower_layer::Layer;
use tower_layer::layer_fn;
pub type BoxResult<T> = Result<T, BoxError>;
pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
pub type SgRequest = Request<SgBody>;
pub type SgResponse = Response<SgBody>;
pub trait SgRequestExt {
    fn with_reflect(&mut self);
    fn reflect_mut(&mut self) -> &mut Reflect;
    fn reflect(&self) -> &Reflect;
    #[cfg(feature = "ext-redis")]
    fn get_redis_client_by_gateway_name(&self) -> Option<spacegate_ext_redis::RedisClient>;
    fn extract<M: Extract>(&self) -> M;
    fn defer_call<F>(&mut self, f: F)
    where
        F: FnOnce(SgRequest) -> SgRequest + Send + 'static;
}
impl SgRequestExt for SgRequest {
    fn reflect_mut(&mut self) -> &mut Reflect {
        self.extensions_mut().get_mut::<Reflect>().expect("reflect extension not found")
    }
    fn reflect(&self) -> &Reflect {
        self.extensions().get::<Reflect>().expect("reflect extension not found")
    }
    fn with_reflect(&mut self) {
        if self.extensions().get::<Reflect>().is_none() {
            self.extensions_mut().insert(Reflect::new());
        }
    }
    #[cfg(feature = "ext-redis")]
    fn get_redis_client_by_gateway_name(&self) -> Option<spacegate_ext_redis::RedisClient> {
        self.extensions().get::<extension::GatewayName>().and_then(|gateway_name| spacegate_ext_redis::RedisClientRepo::global().get(gateway_name))
    }
    fn extract<M: Extract>(&self) -> M {
        M::extract(self)
    }
    fn defer_call<F>(&mut self, f: F)
    where
        F: FnOnce(SgRequest) -> SgRequest + Send + 'static,
    {
        let defer = self.extensions_mut().get_or_insert_default::<extension::Defer>();
        defer.push_back(f);
    }
}
pub trait SgResponseExt {
    fn with_code_message(code: StatusCode, message: impl Into<Bytes>) -> Self;
    fn bad_gateway<E: std::error::Error>(e: E) -> Self
    where
        Self: Sized,
    {
        let message = e.to_string();
        let src = e.source();
        let message = if let Some(src) = src { format!("{}:\n {}", message, src) } else { message };
        Self::with_code_message(StatusCode::BAD_GATEWAY, message)
    }
}
impl SgResponseExt for Response<SgBody> {
    fn with_code_message(code: StatusCode, message: impl Into<Bytes>) -> Self {
        let body = SgBody::full(message);
        let mut resp = Response::builder().status(code).body(body).expect("response builder error");
        resp.extensions_mut().insert(Reflect::new());
        resp
    }
}
pub struct BoxLayer {
    boxed: Box<dyn Layer<ArcHyperService, Service = ArcHyperService> + Send + Sync + 'static>,
}
impl BoxLayer {
    pub fn new<L>(inner_layer: L) -> Self
    where
        L: Layer<ArcHyperService> + Send + Sync + 'static,
        L::Service: Clone + hyper::service::Service<Request<SgBody>, Response = Response<SgBody>, Error = Infallible> + Send + Sync + 'static,
        <L::Service as hyper::service::Service<Request<SgBody>>>::Future: Send + 'static,
    {
        let layer = layer_fn(move |inner: ArcHyperService| {
            let out = inner_layer.layer(inner);
            ArcHyperService::new(out)
        });
        Self { boxed: Box::new(layer) }
    }
    #[must_use]
    pub fn layer_shared(&self, inner: ArcHyperService) -> ArcHyperService {
        self.boxed.layer(inner)
    }
}
impl<S> Layer<S> for BoxLayer
where
    S: hyper::service::Service<Request<SgBody>, Response = Response<SgBody>, Error = Infallible> + Send + Sync + 'static,
    <S as hyper::service::Service<hyper::Request<SgBody>>>::Future: std::marker::Send,
{
    type Service = ArcHyperService;
    fn layer(&self, inner: S) -> Self::Service {
        self.boxed.layer(ArcHyperService::new(inner))
    }
}
impl fmt::Debug for BoxLayer {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("BoxLayer").finish()
    }
}