use ntex::web;
use regex::Regex;
use wreq::Version;

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

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

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

    fn request_headers() -> Vec<(String, String)> {
        vec![
            (
                "accept".to_string(),
                "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8"
                    .to_string(),
            ),
            ("accept-language".to_string(), "en-US,en;q=0.6".to_string()),
            ("cache-control".to_string(), "no-cache".to_string()),
            ("pragma".to_string(), "no-cache".to_string()),
            ("priority".to_string(), "u=0, i".to_string()),
            (
                "sec-ch-ua".to_string(),
                r#""Chromium";v="146", "Not-A.Brand";v="24", "Brave";v="146""#.to_string(),
            ),
            ("sec-ch-ua-mobile".to_string(), "?0".to_string()),
            ("sec-ch-ua-platform".to_string(), "\"Linux\"".to_string()),
            ("sec-fetch-dest".to_string(), "document".to_string()),
            ("sec-fetch-mode".to_string(), "navigate".to_string()),
            ("sec-fetch-site".to_string(), "none".to_string()),
            ("sec-fetch-user".to_string(), "?1".to_string()),
            ("sec-gpc".to_string(), "1".to_string()),
            (
                "upgrade-insecure-requests".to_string(),
                "1".to_string(),
            ),
            (
                "user-agent".to_string(),
                "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"
                    .to_string(),
            ),
            ("Referer".to_string(), "https://spankbang.com/".to_string()),
        ]
    }

    fn extract_stream_data(text: &str) -> Option<&str> {
        let marker = "var stream_data = ";
        let start = text.find(marker)? + marker.len();
        let rest = &text[start..];
        let end = rest.find("};")?;
        Some(&rest[..=end])
    }

    fn extract_first_stream_url(stream_data: &str, key: &str) -> Option<String> {
        let pattern = format!(r"'{}'\s*:\s*\[\s*'([^']+)'", regex::escape(key));
        let regex = Regex::new(&pattern).ok()?;
        regex
            .captures(stream_data)
            .and_then(|captures| captures.get(1))
            .map(|value| value.as_str().to_string())
    }

    fn select_best_stream_url(stream_data: &str) -> Option<String> {
        for key in [
            "m3u8", "4k", "1080p", "720p", "480p", "320p", "240p", "main",
        ] {
            if let Some(url) = Self::extract_first_stream_url(stream_data, key) {
                return Some(url);
            }
        }

        None
    }

    pub async fn get_video_url(
        &self,
        url: String,
        requester: web::types::State<Requester>,
    ) -> String {
        let mut requester = requester.get_ref().clone();
        let url = format!("https://spankbang.com/{}", url.trim_start_matches('/'));
        let text = requester
            .get_with_headers(&url, Self::request_headers(), Some(Version::HTTP_2))
            .await
            .unwrap_or_default();
        if text.is_empty() {
            return String::new();
        }

        let Some(stream_data) = Self::extract_stream_data(&text) else {
            return String::new();
        };

        Self::select_best_stream_url(stream_data).unwrap_or_default()
    }
}

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

    #[test]
    fn prefers_m3u8_when_present() {
        assert_eq!(
            SpankbangProxy::request_headers(),
            vec![
                (
                    "accept".to_string(),
                    "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8"
                        .to_string(),
                ),
                ("accept-language".to_string(), "en-US,en;q=0.6".to_string()),
                ("cache-control".to_string(), "no-cache".to_string()),
                ("pragma".to_string(), "no-cache".to_string()),
                ("priority".to_string(), "u=0, i".to_string()),
                (
                    "sec-ch-ua".to_string(),
                    r#""Chromium";v="146", "Not-A.Brand";v="24", "Brave";v="146""#.to_string(),
                ),
                ("sec-ch-ua-mobile".to_string(), "?0".to_string()),
                ("sec-ch-ua-platform".to_string(), "\"Linux\"".to_string()),
                ("sec-fetch-dest".to_string(), "document".to_string()),
                ("sec-fetch-mode".to_string(), "navigate".to_string()),
                ("sec-fetch-site".to_string(), "none".to_string()),
                ("sec-fetch-user".to_string(), "?1".to_string()),
                ("sec-gpc".to_string(), "1".to_string()),
                (
                    "upgrade-insecure-requests".to_string(),
                    "1".to_string(),
                ),
                (
                    "user-agent".to_string(),
                    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"
                        .to_string(),
                ),
                ("Referer".to_string(), "https://spankbang.com/".to_string()),
            ]
        );

        let data = r#"
            var stream_data = {'240p': ['https://cdn.example/240.mp4'], '720p': ['https://cdn.example/720.mp4'], 'm3u8': ['https://cdn.example/master.m3u8'], 'main': ['https://cdn.example/720.mp4']};
        "#;

        let stream_data = SpankbangProxy::extract_stream_data(data).unwrap();
        assert_eq!(
            SpankbangProxy::select_best_stream_url(stream_data).as_deref(),
            Some("https://cdn.example/master.m3u8")
        );
    }

    #[test]
    fn falls_back_to_highest_quality_mp4() {
        let data = r#"
            var stream_data = {'240p': ['https://cdn.example/240.mp4'], '480p': ['https://cdn.example/480.mp4'], '720p': ['https://cdn.example/720.mp4'], '1080p': [], '4k': [], 'm3u8': [], 'main': ['https://cdn.example/480.mp4']};
        "#;

        let stream_data = SpankbangProxy::extract_stream_data(data).unwrap();
        assert_eq!(
            SpankbangProxy::select_best_stream_url(stream_data).as_deref(),
            Some("https://cdn.example/720.mp4")
        );
    }
}
