use ntex::web;

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

const BASE_URL: &str = "https://www.tube8.com";

#[derive(Debug, Clone)]
pub struct Tube8Proxy {}

impl Tube8Proxy {
    pub fn new() -> Self {
        Tube8Proxy {}
    }

    fn html_headers() -> Vec<(String, String)> {
        vec![
            (
                "User-Agent".to_string(),
                "Mozilla/5.0 (X11; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0"
                    .to_string(),
            ),
            (
                "Accept".to_string(),
                "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8".to_string(),
            ),
            ("Accept-Language".to_string(), "en-US,en;q=0.5".to_string()),
            ("Referer".to_string(), format!("{BASE_URL}/")),
        ]
    }

    fn api_headers(referer: &str) -> Vec<(String, String)> {
        vec![
            (
                "User-Agent".to_string(),
                "Mozilla/5.0 (X11; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0"
                    .to_string(),
            ),
            ("Accept".to_string(), "application/json, text/javascript, */*; q=0.01".to_string()),
            ("Referer".to_string(), referer.to_string()),
            ("X-Requested-With".to_string(), "XMLHttpRequest".to_string()),
        ]
    }

    // Extract the first /media/hls/?s=... URL from a video page.
    // The page embeds it as: "videoUrl":"https:\/\/www.tube8.com\/media\/hls\/?s=TOKEN"
    fn extract_hls_endpoint(html: &str) -> Option<String> {
        let needle = r#""format":"hls","videoUrl":""#;
        let start = html.find(needle)? + needle.len();
        let rest = &html[start..];
        let end = rest.find('"')?;
        let raw = &rest[..end];
        // JSON-escaped forward slashes → real URL
        Some(raw.replace(r"\/", "/"))
    }

    // Parse the JSON quality array returned by /media/hls/?s=...
    // Returns the highest-quality HLS master playlist URL.
    fn best_hls_url(json: &str) -> Option<String> {
        let parsed: serde_json::Value = serde_json::from_str(json).ok()?;
        let arr = parsed.as_array()?;

        // Prefer highest numeric quality; fall back to defaultQuality
        let mut best_quality: i64 = -1;
        let mut best_url: Option<String> = None;
        let mut default_url: Option<String> = None;

        for entry in arr {
            let url = entry
                .get("videoUrl")
                .and_then(|v| v.as_str())
                .map(|v| v.replace(r"\/", "/"))
                .filter(|v| !v.is_empty())?;

            if entry
                .get("defaultQuality")
                .and_then(|v| v.as_bool())
                .unwrap_or(false)
                && default_url.is_none()
            {
                default_url = Some(url.clone());
            }

            if let Some(q) = entry
                .get("quality")
                .and_then(|v| v.as_str())
                .and_then(|v| v.parse::<i64>().ok())
            {
                if q > best_quality {
                    best_quality = q;
                    best_url = Some(url);
                }
            }
        }

        best_url.or(default_url)
    }

    pub async fn get_video_url(
        &self,
        video_id: String,
        requester: web::types::State<Requester>,
    ) -> String {
        let video_id = video_id.trim_matches('/').trim();
        if video_id.is_empty() {
            return String::new();
        }

        let page_url = format!("{BASE_URL}/porn-video/{video_id}/");
        let mut req = requester.get_ref().clone();

        // Step 1: fetch video page to get the signed /media/hls/ endpoint
        let html = match req
            .get_with_headers(&page_url, Self::html_headers(), None)
            .await
        {
            Ok(v) => v,
            Err(_) => return String::new(),
        };

        let hls_endpoint = match Self::extract_hls_endpoint(&html) {
            Some(url) => url,
            None => return String::new(),
        };

        // Step 2: call the signed endpoint to get quality options
        let json = match req
            .get_with_headers(&hls_endpoint, Self::api_headers(&page_url), None)
            .await
        {
            Ok(v) => v,
            Err(_) => return String::new(),
        };

        Self::best_hls_url(&json).unwrap_or_default()
    }
}

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

    #[test]
    fn extracts_hls_endpoint_from_page() {
        let html = r#"
            mediaDefinition: [{"format":"hls","videoUrl":"https:\/\/www.tube8.com\/media\/hls\/?s=eyJTOKEN","remote":true},
            {"format":"mp4","videoUrl":"https:\/\/www.tube8.com\/media\/mp4\/?s=eyJTOKEN","remote":true}],
        "#;
        let url = Tube8Proxy::extract_hls_endpoint(html).expect("should extract");
        assert_eq!(url, "https://www.tube8.com/media/hls/?s=eyJTOKEN");
    }

    #[test]
    fn picks_best_hls_quality() {
        let json = r#"[
            {"defaultQuality":true,"format":"hls","quality":"480","videoUrl":"https://cdn.example/480/master.m3u8"},
            {"defaultQuality":false,"format":"hls","quality":"720","videoUrl":"https://cdn.example/720/master.m3u8"},
            {"defaultQuality":false,"format":"hls","quality":"1080","videoUrl":"https://cdn.example/1080/master.m3u8"},
            {"defaultQuality":false,"format":"hls","quality":"240","videoUrl":"https://cdn.example/240/master.m3u8"}
        ]"#;
        let url = Tube8Proxy::best_hls_url(json).expect("should parse");
        assert_eq!(url, "https://cdn.example/1080/master.m3u8");
    }

    #[test]
    fn falls_back_to_default_quality_when_no_numeric() {
        let json = r#"[
            {"defaultQuality":true,"format":"hls","videoUrl":"https://cdn.example/default/master.m3u8"},
            {"defaultQuality":false,"format":"hls","videoUrl":"https://cdn.example/other/master.m3u8"}
        ]"#;
        let url = Tube8Proxy::best_hls_url(json).expect("should parse");
        assert_eq!(url, "https://cdn.example/default/master.m3u8");
    }
}
