mod method;
mod pattern;
mod protocol;
mod request;
mod response;
mod status_code;

pub use method::*;
pub use protocol::*;
pub use request::*;
pub use response::*;
pub use status_code::*;

use std::{pin::Pin, sync::Arc};

use tokio::net::TcpListener;

use pattern::{MatchResult, Pattern};

type BoxedResponse = Pin<Box<dyn Future<Output = Response> + Send>>;

pub trait AsyncHandler: Send + Sync {
    fn call(&self, req: Request) -> BoxedResponse;
}

impl<F, Fut> AsyncHandler for F
where
    F: Fn(Request) -> Fut + Send + Sync,
    Fut: Future<Output = Response> + Send + 'static,
{
    fn call(&self, req: Request) -> BoxedResponse {
        Box::pin((self)(req))
    }
}

#[derive(Default)]
pub struct Router {
    handlers: Vec<(Pattern, Box<dyn AsyncHandler>)>,
}

impl Router {
    pub async fn serve(self, listener: TcpListener) {
        let handlers = Arc::new(self.handlers);

        loop {
            let handlers = handlers.clone();

            let (stream_read_half, mut stream_write_half) = match listener.accept().await {
                Ok((stream, _)) => tokio::io::split(stream),
                Err(err) => {
                    eprintln!("Unable to accept connection on tcp listener: {err:?}");
                    continue;
                }
            };

            tokio::spawn(async move {
                let res = match Request::try_from_reader(stream_read_half).await {
                    Ok(req) => handle_request(handlers, req).await,
                    Err(err) => {
                        eprintln!("Unable to parse request: {err:?}");
                        Response::new(StatusCode::BadRequest)
                    }
                };

                if let Err(err) = res.write_to_stream(&mut stream_write_half).await {
                    eprintln!("Unable to write response to the stream: {err:?}");
                }
            });
        }
    }

    pub fn route<H>(mut self, method: Method, path: &str, handler: H) -> Self
    where
        H: AsyncHandler + 'static,
    {
        let pattern = Pattern::new(method, path);
        self.handlers.push((pattern, Box::new(handler)));
        self
    }
}

async fn handle_request(
    handlers: Arc<Vec<(Pattern, Box<dyn AsyncHandler>)>>,
    req: Request,
) -> Response {
    for (pattern, handler) in handlers.iter() {
        if let MatchResult::Params(params) =
            pattern.validate_and_extract_params(req.get_method(), &req.get_path())
        {
            let req_with_params = req.params(params);
            return handler.call(req_with_params).await;
        }
    }

    Response::new(StatusCode::NotFound)
}
