use ntex::web;
use url::Url;
use wreq::Version;

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

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

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

    fn normalize_detail_request(endpoint: &str) -> Option<(String, String)> {
        let endpoint = endpoint.trim().trim_start_matches('/');
        if endpoint.is_empty() {
            return None;
        }

        let detail_url = if endpoint.starts_with("http://") || endpoint.starts_with("https://") {
            endpoint.to_string()
        } else if endpoint.starts_with("javtiful.com/") || endpoint.starts_with("www.javtiful.com/")
        {
            format!("https://{endpoint}")
        } else {
            format!("https://javtiful.com/{endpoint}")
        };

        let detail_url = if detail_url.starts_with("http://") {
            detail_url.replacen("http://", "https://", 1)
        } else {
            detail_url
        };

        if !Self::is_allowed_detail_url(&detail_url) {
            return None;
        }

        let video_id = Url::parse(&detail_url)
            .ok()
            .and_then(|url| {
                let mut segments = url.path_segments()?;
                if segments.next()? != "video" {
                    return None;
                }
                segments.next().map(ToOwned::to_owned)
            })
            .filter(|value| value.chars().all(|c| c.is_ascii_digit()) && !value.is_empty())?;

        Some((detail_url, video_id))
    }

    fn is_allowed_detail_url(url: &str) -> bool {
        let Some(parsed) = Url::parse(url).ok() else {
            return false;
        };
        if parsed.scheme() != "https" {
            return false;
        }
        let Some(host) = parsed.host_str() else {
            return false;
        };
        (host == "javtiful.com" || host == "www.javtiful.com")
            && parsed.path().starts_with("/video/")
    }

    pub async fn get_video_url(
        &self,
        url: String,
        requester: web::types::State<Requester>,
    ) -> String {
        let mut requester = requester.get_ref().clone();
        let Some((detail_url, _)) = Self::normalize_detail_request(&url) else {
            println!("JavtifulProxy: Invalid detail URL: {url}");
            return String::new();
        };
        let html = requester.get(&detail_url, Some(Version::HTTP_11)).await;
        let Ok(html) = html else {
            return String::new();
        };
        if html.is_empty() {
            return String::new();
        }
        let mut media_url: String = html.split("playerSources\":[{\"src\":\"")
            .nth(1)
            .and_then(|s| s.split('"').next())
            .map(str::trim)
            .map(ToOwned::to_owned).unwrap_or_default().replace("\\u0026", "&");
        media_url = match media_url.starts_with("/"){
            true => format!("https://javtiful.com{media_url}"),
            false => media_url
        };
        return media_url;
    }
}

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

    #[test]
    fn normalizes_detail_request_with_full_url() {
        let (url, video_id) =
            JavtifulProxy::normalize_detail_request("https://javtiful.com/video/106796/fns-176")
                .expect("detail request should parse");
        assert_eq!(url, "https://javtiful.com/video/106796/fns-176");
        assert_eq!(video_id, "106796");
    }

    #[test]
    fn normalizes_detail_request_with_path_only() {
        let (url, video_id) = JavtifulProxy::normalize_detail_request("video/1000/demo")
            .expect("detail request should parse");
        assert_eq!(url, "https://javtiful.com/video/1000/demo");
        assert_eq!(video_id, "1000");
    }
}
