use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use actix_http::{http::Method, Error};
use actix_service::{Service, ServiceFactory};
use futures_util::future::{ready, FutureExt, LocalBoxFuture};
use crate::extract::FromRequest;
use crate::guard::{self, Guard};
use crate::handler::{Extract, Factory, Handler};
use crate::responder::Responder;
use crate::service::{ServiceRequest, ServiceResponse};
use crate::HttpResponse;
type BoxedRouteService<Req, Res> = Box<
    dyn Service<
        Request = Req,
        Response = Res,
        Error = Error,
        Future = LocalBoxFuture<'static, Result<Res, Error>>,
    >,
>;
type BoxedRouteNewService<Req, Res> = Box<
    dyn ServiceFactory<
        Config = (),
        Request = Req,
        Response = Res,
        Error = Error,
        InitError = (),
        Service = BoxedRouteService<Req, Res>,
        Future = LocalBoxFuture<'static, Result<BoxedRouteService<Req, Res>, ()>>,
    >,
>;
pub struct Route {
    service: BoxedRouteNewService<ServiceRequest, ServiceResponse>,
    guards: Rc<Vec<Box<dyn Guard>>>,
}
impl Route {
    
    #[allow(clippy::new_without_default)]
    pub fn new() -> Route {
        Route {
            service: Box::new(RouteNewService::new(Extract::new(Handler::new(|| {
                ready(HttpResponse::NotFound())
            })))),
            guards: Rc::new(Vec::new()),
        }
    }
    pub(crate) fn take_guards(&mut self) -> Vec<Box<dyn Guard>> {
        std::mem::take(Rc::get_mut(&mut self.guards).unwrap())
    }
}
impl ServiceFactory for Route {
    type Config = ();
    type Request = ServiceRequest;
    type Response = ServiceResponse;
    type Error = Error;
    type InitError = ();
    type Service = RouteService;
    type Future = CreateRouteService;
    fn new_service(&self, _: ()) -> Self::Future {
        CreateRouteService {
            fut: self.service.new_service(()),
            guards: self.guards.clone(),
        }
    }
}
type RouteFuture = LocalBoxFuture<
    'static,
    Result<BoxedRouteService<ServiceRequest, ServiceResponse>, ()>,
>;
#[pin_project::pin_project]
pub struct CreateRouteService {
    #[pin]
    fut: RouteFuture,
    guards: Rc<Vec<Box<dyn Guard>>>,
}
impl Future for CreateRouteService {
    type Output = Result<RouteService, ()>;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.project();
        match this.fut.poll(cx)? {
            Poll::Ready(service) => Poll::Ready(Ok(RouteService {
                service,
                guards: this.guards.clone(),
            })),
            Poll::Pending => Poll::Pending,
        }
    }
}
pub struct RouteService {
    service: BoxedRouteService<ServiceRequest, ServiceResponse>,
    guards: Rc<Vec<Box<dyn Guard>>>,
}
impl RouteService {
    pub fn check(&self, req: &mut ServiceRequest) -> bool {
        for f in self.guards.iter() {
            if !f.check(req.head()) {
                return false;
            }
        }
        true
    }
}
impl Service for RouteService {
    type Request = ServiceRequest;
    type Response = ServiceResponse;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }
    fn call(&mut self, req: ServiceRequest) -> Self::Future {
        self.service.call(req).boxed_local()
    }
}
impl Route {
    
    
    
    
    
    
    
    
    
    
    
    
    
    pub fn method(mut self, method: Method) -> Self {
        Rc::get_mut(&mut self.guards)
            .unwrap()
            .push(Box::new(guard::Method(method)));
        self
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    pub fn guard<F: Guard + 'static>(mut self, f: F) -> Self {
        Rc::get_mut(&mut self.guards).unwrap().push(Box::new(f));
        self
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    pub fn to<F, T, R, U>(mut self, handler: F) -> Self
    where
        F: Factory<T, R, U>,
        T: FromRequest + 'static,
        R: Future<Output = U> + 'static,
        U: Responder + 'static,
    {
        self.service =
            Box::new(RouteNewService::new(Extract::new(Handler::new(handler))));
        self
    }
}
struct RouteNewService<T>
where
    T: ServiceFactory<Request = ServiceRequest, Error = (Error, ServiceRequest)>,
{
    service: T,
}
impl<T> RouteNewService<T>
where
    T: ServiceFactory<
        Config = (),
        Request = ServiceRequest,
        Response = ServiceResponse,
        Error = (Error, ServiceRequest),
    >,
    T::Future: 'static,
    T::Service: 'static,
    <T::Service as Service>::Future: 'static,
{
    pub fn new(service: T) -> Self {
        RouteNewService { service }
    }
}
impl<T> ServiceFactory for RouteNewService<T>
where
    T: ServiceFactory<
        Config = (),
        Request = ServiceRequest,
        Response = ServiceResponse,
        Error = (Error, ServiceRequest),
    >,
    T::Future: 'static,
    T::Service: 'static,
    <T::Service as Service>::Future: 'static,
{
    type Config = ();
    type Request = ServiceRequest;
    type Response = ServiceResponse;
    type Error = Error;
    type InitError = ();
    type Service = BoxedRouteService<ServiceRequest, Self::Response>;
    type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
    fn new_service(&self, _: ()) -> Self::Future {
        self.service
            .new_service(())
            .map(|result| match result {
                Ok(service) => {
                    let service: BoxedRouteService<_, _> =
                        Box::new(RouteServiceWrapper { service });
                    Ok(service)
                }
                Err(_) => Err(()),
            })
            .boxed_local()
    }
}
struct RouteServiceWrapper<T: Service> {
    service: T,
}
impl<T> Service for RouteServiceWrapper<T>
where
    T::Future: 'static,
    T: Service<
        Request = ServiceRequest,
        Response = ServiceResponse,
        Error = (Error, ServiceRequest),
    >,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx).map_err(|(e, _)| e)
    }
    fn call(&mut self, req: ServiceRequest) -> Self::Future {
        
        self.service
            .call(req)
            .map(|res| match res {
                Ok(res) => Ok(res),
                Err((err, req)) => Ok(req.error_response(err)),
            })
            .boxed_local()
        
        
        
        
        
        
        
        
    }
}
#[cfg(test)]
mod tests {
    use std::time::Duration;
    use actix_rt::time::delay_for;
    use bytes::Bytes;
    use serde_derive::Serialize;
    use crate::http::{Method, StatusCode};
    use crate::test::{call_service, init_service, read_body, TestRequest};
    use crate::{error, web, App, HttpResponse};
    #[derive(Serialize, PartialEq, Debug)]
    struct MyObject {
        name: String,
    }
    #[actix_rt::test]
    async fn test_route() {
        let mut srv = init_service(
            App::new()
                .service(
                    web::resource("/test")
                        .route(web::get().to(HttpResponse::Ok))
                        .route(web::put().to(|| async {
                            Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
                        }))
                        .route(web::post().to(|| async {
                            delay_for(Duration::from_millis(100)).await;
                            Ok::<_, ()>(HttpResponse::Created())
                        }))
                        .route(web::delete().to(|| async {
                            delay_for(Duration::from_millis(100)).await;
                            Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
                        })),
                )
                .service(web::resource("/json").route(web::get().to(|| async {
                    delay_for(Duration::from_millis(25)).await;
                    web::Json(MyObject {
                        name: "test".to_string(),
                    })
                }))),
        )
        .await;
        let req = TestRequest::with_uri("/test")
            .method(Method::GET)
            .to_request();
        let resp = call_service(&mut srv, req).await;
        assert_eq!(resp.status(), StatusCode::OK);
        let req = TestRequest::with_uri("/test")
            .method(Method::POST)
            .to_request();
        let resp = call_service(&mut srv, req).await;
        assert_eq!(resp.status(), StatusCode::CREATED);
        let req = TestRequest::with_uri("/test")
            .method(Method::PUT)
            .to_request();
        let resp = call_service(&mut srv, req).await;
        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
        let req = TestRequest::with_uri("/test")
            .method(Method::DELETE)
            .to_request();
        let resp = call_service(&mut srv, req).await;
        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
        let req = TestRequest::with_uri("/test")
            .method(Method::HEAD)
            .to_request();
        let resp = call_service(&mut srv, req).await;
        assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
        let req = TestRequest::with_uri("/json").to_request();
        let resp = call_service(&mut srv, req).await;
        assert_eq!(resp.status(), StatusCode::OK);
        let body = read_body(resp).await;
        assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}"));
    }
}