use std::path::PathBuf;

use tokio::{io::{AsyncReadExt, BufReader, AsyncBufReadExt, AsyncWriteExt}, net::TcpStream};

use crate::handling::{
    file_handlers::NamedFile,
    request::Request,
    response::{Outcome, Response, ResponseBody, Status},
    routes::Data, methods::Method,
};
use crate::setup::MountPoint;

pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoint<'_>>) {
    let mut buf_reader = BufReader::new(&mut stream);
    let mut http_request: Vec<String> = Vec::with_capacity(100);
    loop {
        let mut buffer = String::new();
        if let Err(_) = buf_reader.read_line(&mut buffer).await {
            eprintln!("\x1b[31mAborting due to invalid UTF-8 in request header\x1b[0m");
            return;
        }
        if buffer == "\r\n" {
            break;
        }
        http_request.push(buffer);
    }

    // println!("{:#?}", http_request);

    let request_status_line = if let Some(status_line) = http_request.get(0).cloned() {
        status_line
    } else {
        eprintln!("\x1b[31mAborting due to missing headers\x1b[0m");
        return;
    };
    let request = Request {
        uri: if let Some(uri) = &request_status_line.split(" ").nth(1) {
            *uri
        } else {
            eprintln!("\x1b[31mAborting due to invalid status line\x1b[0m");
            return;
        },
        headers: http_request,
        method: if let Some(method) = request_status_line
            .split(" ")
            .next() {
            if let Ok(ok) = method.parse() {
                ok
            } else {
                eprintln!("\x1b[31mAborting due to invalid request method\x1b[0m");
                return;
            }
        } else {
            eprintln!("\x1b[31mAborting due to invalid status line\x1b[0m");
            return;
        }
    };

    let mut data = Data {
        is_complete: false,
        buffer: vec![],
    };
    if request.can_have_body() {
        let length = if let Some(len) = request
            .headers
            .iter()
            .filter(|header| header.starts_with("Content-Length: "))
            .map(|header| {
                let header = header.strip_prefix("Content-Length: ").unwrap();
                header.trim().parse::<usize>()
            })
            .next()
        {
            if let Ok(size) = len {
                size
            } else {
                0
            }
        } else {
            0
        };

        let mut buffer: Vec<u8> = vec![];
        buf_reader.read_buf(&mut buffer).await.unwrap();
        if buffer.len() != length {
            let respone = len_not_defined(Status::LengthRequired);
            let mut response = respone.0.as_bytes().to_vec();
            response.extend_from_slice(&respone.1);
            if let Err(e) = stream.write_all(&response).await {
                eprintln!("\x1b[31mError {e} occured when trying to write answer to TCP-Stream for Client, aborting\x1b[0m");
                return;
            }
            return;
        }
        data.is_complete = true;
        data.buffer = buffer;
    }

    let mut handled_response: Option<Outcome<Response, Status, Data>> = None;
    for mountpoint in mountpoints {
        if !request.uri.starts_with(mountpoint.mountpoint) {
            continue;
        }
        let mounted_request_uri = request.uri.strip_prefix(mountpoint.mountpoint).unwrap();
        for route in mountpoint.routes {
            if (route.method != request.method) && 
            ((route.method != Method::Get) && (request.method == Method::Head)) {
                continue;
            }
            if !route.compare_uri(mounted_request_uri) {
                continue;
            }
            handled_response = Some((route.handler)(
                Request {
                    uri: mounted_request_uri,
                    ..request.clone()
                },
                data.clone(),
            ));

            if let Some(Outcome::Forward(_)) = handled_response {
                continue;
            }
            break;
        }
    }

    let response = match handled_response {
        Some(val) => match val {
            Outcome::Success(success) => (
                format!("HTTP/1.1 {}\r\n", success.status.unwrap_or(Status::Ok))
                    + &success.headers.join("\r\n")
                    + "\r\n\r\n",
                if request.method == Method::Head {
                    vec![]
                } else {
                    success.body.get_data()
                }
            ),
            Outcome::Failure(error) => failure_handler(error),
            Outcome::Forward(_) => failure_handler(Status::NotFound),
        },
        None => failure_handler(Status::NotFound),
    };

    let mut resp = response.0.as_bytes().to_vec();
    resp.extend_from_slice(&response.1);
    if let Err(e) = stream.write_all(&resp).await {
        eprintln!("\x1b[31mError {e} occured when trying to write answer to TCP-Stream for Client, aborting\x1b[0m");
        return;
    }
    return;
}

fn failure_handler(status: Status) -> (String, Vec<u8>) {
    let page_404 = NamedFile::open(PathBuf::from("404.html")).unwrap();
    (
        format!(
            "HTTP/1.1 {}\r\nContent-Length: {}\r\nContent-Type: {}\r\n\r\n",
            status,
            page_404.get_len(),
            page_404.get_mime()
        ),
        page_404.get_data(),
    )
}

fn len_not_defined(status: Status) -> (String, Vec<u8>) {
    let page_411 = NamedFile::open(PathBuf::from("411.html")).unwrap();
    (
        format!(
            "HTTP/1.1 {status}\r\nContent-Length: {}\r\nContent-Type: {}\r\n\r\n",
            page_411.get_len(),
            page_411.get_mime()
        ),
        page_411.get_data(),
    )
}