use std::{
    io::{BufRead, BufReader, Write},
    net::TcpStream,
    path::PathBuf,
};

use crate::routing::routes::{Data, Outcome, Request, Response, Status};
use crate::setup::MountPoint;

use super::file_handlers::NamedFile;

pub fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoint>) {
    let buf_reader = BufReader::new(&mut stream);
    let http_request: Vec<String> = buf_reader
        .lines()
        .map(|result| result.unwrap())
        .take_while(|line| !line.is_empty())
        .collect();
    let request_status_line = http_request.get(0);
    if request_status_line == None {
        return;
    }
    let request_status_line = request_status_line.unwrap();
    let request = Request {
        uri: &request_status_line.split(" ").nth(1).unwrap(),
        headers: http_request.clone(),
        method: request_status_line
            .split(" ")
            .next()
            .unwrap()
            .parse()
            .unwrap(),
    };

    let data = Data {
        buffer: vec![],
        is_complete: true,
    };

    let mut handled_response: Option<Outcome<Response, Status, Data>> = None;
    for mountpoint in mountpoints {
        if !request.uri.starts_with(mountpoint.mountpoint) {
            println!("MOUNTPOINT COMPARISON {}", request.uri);
            continue;
        }
        let mounted_request_uri = request.uri.strip_prefix(mountpoint.mountpoint).unwrap();
        for route in mountpoint.routes {
            if route.method != request.method {
                println!("METHOD COMPARE {}", request.method);
                continue;
            }
            if !route.compare_uri(mounted_request_uri) {
                println!("URI COMPARISON {} {}", mounted_request_uri, route.uri);
                continue;
            }
            handled_response = Some((route.handler)(
                Request {
                    uri: mounted_request_uri,
                    ..request.clone()
                },
                data.clone(),
            ));
        }
    }

    let response = match handled_response {
        Some(val) => match val {
            Outcome::Success(success) => (
                format!("HTTP/1.1 {} OK\r\n", success.status.unwrap().code)
                    + &success.headers.join("\r\n")
                    + "\r\n\r\n",
                success.body.get_data(),
            ),
            Outcome::Failure(error) => handler_404(error),
            Outcome::Forward(_) => handler_404(Status { code: 404 }),
        },
        None => handler_404(Status { code: 404 }),
    };

    stream.write_all(response.0.as_bytes()).unwrap();
    stream.write(&response.1).unwrap();
}

fn handler_404(status: Status) -> (String, Vec<u8>) {
    let page_404 = NamedFile::open(PathBuf::from("404.html")).unwrap();
    (
        format!(
            "HTTP/1.1 {} NOT FOUND\r\nContent-Length: {}\r\nContent-Type: {}\r\n\r\n",
            status.code, page_404.content_len, page_404.content_type
        ),
        page_404.content,
    )
}