use ntex::http::header::{CONTENT_LENGTH, CONTENT_TYPE};
use ntex::{
    http::Response,
    web::{self, HttpRequest, error},
};
use scraper::{Html, Selector};

use crate::util::requester::Requester;

fn normalize_page_url(endpoint: &str) -> String {
    let endpoint = endpoint.trim_start_matches('/');
    if endpoint.starts_with("http://") || endpoint.starts_with("https://") {
        endpoint.to_string()
    } else if endpoint.starts_with("hanime.tv/") {
        format!("https://{endpoint}")
    } else {
        format!("https://hanime.tv/videos/hentai/{endpoint}")
    }
}

async fn fetch_cover_url(page_url: &str, requester: &Requester) -> Option<String> {
    let html = requester
        .clone()
        .get_raw_with_headers(
            page_url,
            vec![("Referer".to_string(), "https://hanime.tv/".to_string())],
        )
        .await
        .ok()?
        .text()
        .await
        .ok()?;

    let doc = Html::parse_document(&html);
    let selector = Selector::parse("div.hvpi-cover-container img.hvpi-cover").ok()?;
    let img = doc.select(&selector).next()?;
    img.value().attr("src").map(str::to_string)
}

pub async fn get_image(
    req: HttpRequest,
    requester: web::types::State<Requester>,
) -> Result<impl web::Responder, web::Error> {
    let endpoint = req.match_info().query("endpoint").to_string();
    let page_url = normalize_page_url(&endpoint);

    let cover_url = match fetch_cover_url(&page_url, requester.get_ref()).await {
        Some(url) => url,
        None => return Ok(web::HttpResponse::NotFound().finish()),
    };

    let upstream = match requester
        .get_ref()
        .clone()
        .get_raw_with_headers(
            &cover_url,
            vec![("Referer".to_string(), "https://hanime.tv/".to_string())],
        )
        .await
    {
        Ok(response) => response,
        Err(_) => return Ok(web::HttpResponse::NotFound().finish()),
    };

    let status = upstream.status();
    let headers = upstream.headers().clone();
    let bytes = upstream.bytes().await.map_err(error::ErrorBadGateway)?;

    let mut resp = Response::build(status);

    if let Some(ct) = headers.get(CONTENT_TYPE) {
        if let Ok(ct_str) = ct.to_str() {
            resp.set_header(CONTENT_TYPE, ct_str);
        }
    }
    if let Some(cl) = headers.get(CONTENT_LENGTH) {
        if let Ok(cl_str) = cl.to_str() {
            resp.set_header(CONTENT_LENGTH, cl_str);
        }
    }

    Ok(resp.body(bytes.to_vec()))
}

#[cfg(test)]
mod tests {
    use super::normalize_page_url;

    #[test]
    fn slug_becomes_full_url() {
        assert_eq!(
            normalize_page_url("reika-wa-karei-na-boku-no-joou-3"),
            "https://hanime.tv/videos/hentai/reika-wa-karei-na-boku-no-joou-3"
        );
    }

    #[test]
    fn full_url_passes_through() {
        assert_eq!(
            normalize_page_url("https://hanime.tv/videos/hentai/reika-wa-karei-na-boku-no-joou-3"),
            "https://hanime.tv/videos/hentai/reika-wa-karei-na-boku-no-joou-3"
        );
    }

    #[test]
    fn hanime_tv_host_gets_scheme() {
        assert_eq!(
            normalize_page_url("hanime.tv/videos/hentai/some-slug"),
            "https://hanime.tv/videos/hentai/some-slug"
        );
    }
}
