use std::fmt;
use crate::{
    request::Request,
    response::{HTTP_NOT_FOUND, Response},
    router::{Route, WebRoute, WsRoute, middleware::call, next::Next}
};

#[derive(Default)]
pub(crate) struct Routes {
    pub(crate) web: Vec<Route<WebRoute>>,
    pub(crate) ws: Vec<Box<Route<WsRoute>>>,
    pub(crate) not_found_callback: Option<Box<WebRoute>>,
}

impl fmt::Debug for Routes {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.debug_struct("Routes")
            .field("web", &self.web)
            .field("ws", &self.ws)
            .field("not_found_callback", &self.not_found_callback.as_ref().map(|_| "Fn(...)"))
            .finish()
    }
}

impl Routes {
    pub fn handle_web_request<'r>(&self, req: &'r mut Request, res: &'r mut Response) {
        for route in &self.web {
            let (is_match, parameters) = route.is_match(req);
            
            if !is_match {
                continue;
            }
            
            req.parameters = parameters;

            return match self.handle_middlewares(req, res, &route.middlewares) {
                true => { (route.handler)(req, res); },
                false => {},
            };
        }

        res.status_code = HTTP_NOT_FOUND;

        if let Some(callback) = &self.not_found_callback {
            callback(req, res);
        }
    }

    pub fn handle_ws_request<'r>(&'r self, req: &'r mut Request, res: &'r mut Response) -> Option<(&'r Route<WsRoute>, &'r mut Request, &'r mut Response)> {
        for route in &self.ws {
            let (is_match, parameters) = route.is_match(req);

            if !is_match {
                continue;
            }

            req.parameters = parameters;

            return match self.handle_middlewares(req, res, &route.middlewares) {
                true => Some((&route, req, res)),
                false => None,
            };
        }

        return None;
    }

    fn handle_middlewares<'g>(&self, req: &'g mut Request, res: &'g mut Response, middlewares: &Vec<String>) -> bool{
        for pointer in  middlewares {
            let mut next = Next::new();

            call(String::from(pointer), req, res, &mut next);

            if !next.is_move {
                return false;
            }
        }

        return true;
    }
}
