#[cfg(feature = "websocket")]
use crate::ws::{WebSocketReceiver, WebSocketSender};
use crate::{
    context::{Context, HandlerMetadata, RouteId, State},
    controller::{Controller, DynControllerHandler},
    error::Error,
    handler::DynHandler,
    request::Request,
    resolver::{Resolver, ResolverResult},
    responder::{DynResponder, Responder},
    Result,
};
use futures::future::BoxFuture;
use hyper::Method;
use std::{collections::HashMap, sync::Arc};
pub trait Route {
    fn dispatch(
        &'static self,
        resolver_id: u64,
        req: Request,
    ) -> Option<BoxFuture<'static, Box<dyn DynResponder + Send>>>;
    fn add_handler(
        &mut self,
        handler_id: u64,
        method: Method,
        handler: Box<dyn DynHandler + Send + Sync>,
    );
}
pub struct RouterBuilder<'a,T>
where
    T: Route + Send + Sync + Unpin + 'static,
{
    prefix: &'a str,
    resolver: HashMap<String, Resolver>,
    route: T,
}
impl<'a> Default for RouterBuilder<'a,ImplRoute> {
    #[inline]
    fn default() -> Self {
        Self {
            prefix: "",
            resolver: Default::default(),
            route: ImplRoute {
                handlers: Default::default(),
            },
        }
    }
}
macro_rules! method {
    ($($(#[$m:meta])* $v:vis fn $n:ident = $method:expr;)+) => {
        $(
            $(#[$m])* $v fn $n<H>(self,path: &str,handler: H) -> Self
            where
                H: 'static + DynHandler + Send + Sync,
            {
                self.add(path,$method,handler)
            }
        )+
    };
}
impl<'a,T> RouterBuilder<'a,T>
where
    T: 'static + Route + Send + Sync + Unpin,
{
    #[inline]
    fn add<H>(mut self, path: &str, method: Method, handler: H) -> Self
    where
        H: 'static + DynHandler + Send + Sync,
    {
        let path = &format!("{}/{}",self.prefix,path);
        let handler_id = if let Some(resolver) = self.resolver.get_mut(path) {
            let _ = resolver.add_method(method.clone());
            resolver.id()
        } else {
            let resolver =
                Resolver::new(path, method.clone()).expect("Unable to construct endpoint resolver");
            let resolver_id = resolver.id();
            self.resolver.insert(path.to_string(), resolver);
            resolver_id
        };
        self.route
            .add_handler(handler_id, method, Box::new(handler));
        self
    }
    
    #[inline]
    pub fn group(self, prefix: &str) -> RouterBuilder<ImplRoute> {
        RouterBuilder {
            prefix,
            resolver: Default::default(),
            route: ImplRoute {
                handlers: Default::default(),
            },
        }
    }
    method![
        #[inline]
        pub fn get = Method::GET;
        #[inline]
        pub fn post = Method::POST;
        #[inline]
        pub fn options = Method::OPTIONS;
        #[inline]
        pub fn put = Method::PUT;
        #[inline]
        pub fn delete = Method::DELETE;
        #[inline]
        pub fn head = Method::HEAD;
        #[inline]
        pub fn trace = Method::TRACE;
        #[inline]
        pub fn connect = Method::CONNECT;
        #[inline]
        pub fn patch = Method::PATCH;
    ];
    #[cfg(not(feature = "athene_body"))]
    #[cfg(feature = "static_file")]
    #[inline]
    pub fn static_files(self, uri_path: &str, dir_path: impl Into<std::path::PathBuf>) -> Self {
        let prefix = &format!("{}/{}",self.prefix,uri_path);
        self.add(
            uri_path,
            Method::GET,
            crate::static_file::StaticFile::new(prefix, dir_path),
        )
    }
    #[cfg(feature = "websocket")]
    #[inline]
    pub fn ws<H, F>(self, path: &str, handler: H) -> Self
    where
        H: Send + Sync + 'static + Fn(Request, WebSocketSender, WebSocketReceiver) -> F,
        F: std::future::Future<Output = Result<()>> + Send + 'static,
    {
        self.add(path, Method::GET, crate::ws::new_ws(handler))
    }
    #[inline]
    pub fn controller<C>(mut self, controller: C) -> RouterBuilder<'a,RouterChain<C, T>>
    where
        C: Controller + Send + Sync + Unpin,
    {
        let mut handlers = HashMap::new();
        for (name, method, sub_path, handler) in controller.method() {
            let path = format!("{}{}", C::BASE_PATH, sub_path);
            let meta = name.map(|name| HandlerMetadata {
                route_id: Default::default(),
                name: Some(name),
            });
            let handler_id = if let Some(resolver) = self.resolver.get_mut(&path) {
                let _ = resolver.add_method_with_metadata(method.clone(), meta);
                resolver.id()
            } else {
                let resolver = Resolver::new_with_metadata(&path, method.clone(), meta)
                    .expect("Unable to construct endpoint resolver");
                let resolver_id = resolver.id();
                self.resolver.insert(path, resolver);
                resolver_id
            };
            handlers.insert((handler_id, method), handler);
        }
        RouterBuilder {
            prefix: "",
            resolver: self.resolver,
            route: RouterChain {
                controller,
                handlers,
                route: self.route,
            },
        }
    }
    #[inline]
    pub(crate) fn build(self) -> Router {
        let RouterBuilder {
            resolver,
            route: controllers,
            ..
        } = self;
        let mut resolvers = resolver.into_values().collect::<Vec<_>>();
        resolvers.sort_unstable();
        Router {
            inner: Arc::new((resolvers, Box::new(controllers))),
        }
    }
}
#[doc(hidden)]
#[derive(Clone)]
pub struct Router {
    inner: Arc<(Vec<Resolver>, Box<dyn Route + Send + Sync + Unpin>)>,
}
impl<'a> Router {
    #[inline]
    pub fn builder() -> RouterBuilder<'a,ImplRoute> {
        RouterBuilder::default()
    }
    #[inline]
    pub fn resolve(&self, req: &mut Request) -> Result<u64, u16> {
        let mut method_not_allowed = false;
        for resolver in &self.inner.0 {
            match resolver.resolve(req) {
                ResolverResult::InvalidPath => continue,
                ResolverResult::MethodNotAllowed => method_not_allowed = true,
                ResolverResult::Match(_) => return Ok(resolver.id()),
            }
        }
        if method_not_allowed {
            Err(405)
        } else {
            Err(404)
        }
    }
    #[inline]
    pub fn resolve_metadata(&self, req: &mut Request) -> HandlerMetadata {
        let mut method_not_allowed = false;
        for resolver in &self.inner.0 {
            match resolver.resolve(req) {
                ResolverResult::InvalidPath => continue,
                ResolverResult::MethodNotAllowed => method_not_allowed = true,
                ResolverResult::Match(meta) => return meta.clone(),
            }
        }
        if method_not_allowed {
            HandlerMetadata::not_allowed()
        } else {
            HandlerMetadata::not_found()
        }
    }
    #[inline]
    pub async fn dispatch(&self, mut ctx: Context) -> Result<Context, Error> {
        let req = ctx
            .state
            .take_request()
            .ok_or_else(|| Error::Other(String::from("Request moved before handler")))?;
        let static_self = unsafe { std::mem::transmute::<&'_ Self, &'static Self>(self) };
        let builder: crate::response::Builder = crate::response::Builder::new();
        let route_id = match ctx.metadata.route_id {
            RouteId::Id(id) => id,
            RouteId::Error(e) => {
                return e.response(builder, &ctx).build().map(|r| {
                    ctx.state = State::After(Box::new(r));
                    ctx
                });
            }
        };
        let res = if let Some(responder) = static_self.inner.1.dispatch(route_id, req) {
            responder.await.response(builder, &ctx)
        } else {
            404.response(builder, &ctx)
        }
        .build();
        res.map(|r| {
            ctx.state = State::After(Box::new(r));
            ctx
        })
    }
}
#[doc(hidden)]
pub struct ImplRoute {
    handlers: HashMap<(u64, Method), Box<dyn DynHandler + Send + Sync>>,
}
impl Route for ImplRoute {
    #[inline]
    fn dispatch(
        &'static self,
        resolver_id: u64,
        req: Request,
    ) -> Option<BoxFuture<'static, Box<dyn DynResponder + Send>>> {
        if let Some(handler) = self.handlers.get(&(resolver_id, req.method().clone())) {
            Some(handler.handle(req))
        } else {
            None
        }
    }
    #[inline]
    fn add_handler(
        &mut self,
        endpoint_id: u64,
        method: Method,
        handler: Box<dyn DynHandler + Send + Sync>,
    ) {
        self.handlers.insert((endpoint_id, method), handler);
    }
}
#[doc(hidden)]
pub struct RouterChain<C, T: Route> {
    controller: C,
    route: T,
    handlers: HashMap<(u64, Method), Box<dyn DynControllerHandler<C> + Send + Sync>>,
}
impl<C: Sync + Send, Rest: Route + Sync + Send> Route for RouterChain<C, Rest> {
    #[inline]
    fn dispatch(
        &'static self,
        resolver_id: u64,
        req: Request,
    ) -> Option<BoxFuture<'static, Box<dyn DynResponder + Send>>> {
        if let Some(handler) = self.handlers.get(&(resolver_id, req.method().clone())) {
            Some(handler.handle(&self.controller, req))
        } else {
            self.route.dispatch(resolver_id, req)
        }
    }
    #[inline]
    fn add_handler(
        &mut self,
        handler_id: u64,
        method: Method,
        handler: Box<dyn DynHandler + Send + Sync>,
    ) {
        self.route.add_handler(handler_id, method, handler);
    }
}