use ntex::web;
use regex::Regex;
use std::process::Command;
use url::Url;

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

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

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

    fn normalize_detail_url(endpoint: &str) -> Option<String> {
        let endpoint = endpoint.trim();
        if endpoint.is_empty() {
            return None;
        }

        if endpoint.starts_with("http://") || endpoint.starts_with("https://") {
            Some(endpoint.to_string())
        } else {
            Some(format!("https://{}", endpoint.trim_start_matches('/')))
        }
    }

    fn parse_url(url: &str) -> Option<Url> {
        Url::parse(url).ok()
    }

    fn is_porndish_host(host: &str) -> bool {
        matches!(host, "www.porndish.com" | "porndish.com")
    }

    fn is_myvidplay_host(host: &str) -> bool {
        matches!(host, "myvidplay.com" | "www.myvidplay.com")
    }

    fn is_vidara_host(host: &str) -> bool {
        matches!(host, "vidara.so" | "www.vidara.so")
    }

    fn is_allowed_detail_url(url: &str) -> bool {
        let Some(url) = Self::parse_url(url) else {
            return false;
        };
        if url.scheme() != "https" {
            return false;
        }
        let Some(host) = url.host_str() else {
            return false;
        };
        Self::is_porndish_host(host) && url.path().starts_with("/porn/")
    }

    fn is_allowed_myvidplay_iframe_url(url: &str) -> bool {
        let Some(url) = Self::parse_url(url) else {
            return false;
        };
        if url.scheme() != "https" {
            return false;
        }
        let Some(host) = url.host_str() else {
            return false;
        };
        Self::is_myvidplay_host(host) && url.path().starts_with("/e/")
    }

    fn is_allowed_myvidplay_pass_url(url: &str) -> bool {
        let Some(url) = Self::parse_url(url) else {
            return false;
        };
        if url.scheme() != "https" {
            return false;
        }
        let Some(host) = url.host_str() else {
            return false;
        };
        Self::is_myvidplay_host(host) && url.path().starts_with("/pass_md5/")
    }

    fn is_allowed_vidara_iframe_url(url: &str) -> bool {
        let Some(url) = Self::parse_url(url) else {
            return false;
        };
        if url.scheme() != "https" {
            return false;
        }
        let Some(host) = url.host_str() else {
            return false;
        };
        Self::is_vidara_host(host) && url.path().starts_with("/e/")
    }

    fn vidara_api_url(iframe_url: &str) -> Option<String> {
        let url = Self::parse_url(iframe_url)?;
        if !Self::is_allowed_vidara_iframe_url(iframe_url) {
            return None;
        }
        let filecode = url
            .path_segments()?
            .filter(|segment| !segment.is_empty())
            .next_back()?
            .to_string();
        if filecode.is_empty() {
            return None;
        }
        Some(format!("https://vidara.so/api/stream?filecode={filecode}"))
    }

    fn regex(value: &str) -> Option<Regex> {
        Regex::new(value).ok()
    }

    async fn fetch_with_curl_cffi(url: &str, referer: Option<&str>) -> Option<String> {
        let url = url.to_string();
        let referer = referer.unwrap_or("").to_string();

        let output = tokio::task::spawn_blocking(move || {
            Command::new("python3")
                .arg("-c")
                .arg(
                    r#"
import sys
from curl_cffi import requests

url = sys.argv[1]
referer = sys.argv[2] if len(sys.argv) > 2 else ""
headers = {}
if referer:
    headers["Referer"] = referer

response = requests.get(
    url,
    impersonate="chrome",
    timeout=30,
    allow_redirects=True,
    headers=headers,
)
if response.status_code >= 400:
    sys.exit(1)
sys.stdout.buffer.write(response.content)
"#,
                )
                .arg(url)
                .arg(referer)
                .output()
        })
        .await
        .ok()?
        .ok()?;

        if !output.status.success() {
            return None;
        }

        Some(String::from_utf8_lossy(&output.stdout).to_string())
    }

    async fn resolve_first_redirect(url: &str) -> Option<String> {
        let url = url.to_string();
        let output = tokio::task::spawn_blocking(move || {
            Command::new("python3")
                .arg("-c")
                .arg(
                    r#"
import sys
from curl_cffi import requests

url = sys.argv[1]
response = requests.get(
    url,
    impersonate="chrome",
    timeout=30,
    allow_redirects=False,
)
location = response.headers.get("location", "")
if location:
    sys.stdout.write(location)
"#,
                )
                .arg(url)
                .output()
        })
        .await
        .ok()?
        .ok()?;

        let location = String::from_utf8_lossy(&output.stdout).trim().to_string();
        if location.is_empty() {
            None
        } else {
            Some(location)
        }
    }

    fn extract_iframe_fragments(html: &str) -> Vec<String> {
        let Some(regex) = Self::regex(r#"const\s+[A-Za-z0-9_]+Content\s*=\s*"((?:\\.|[^"\\])*)";"#)
        else {
            return vec![];
        };

        let mut fragments = Vec::new();
        for captures in regex.captures_iter(html) {
            let Some(value) = captures.get(1).map(|value| value.as_str()) else {
                continue;
            };
            let encoded = format!("\"{value}\"");
            let decoded = serde_json::from_str::<String>(&encoded).unwrap_or_default();
            if decoded.contains("<iframe") {
                fragments.push(decoded);
            }
        }
        fragments
    }

    fn parse_embed_source(fragment: &str) -> Option<String> {
        let regex = Self::regex(r#"(?is)<iframe[^>]+src="([^"]+)"[^>]*>"#)?;
        regex
            .captures(fragment)
            .and_then(|captures| captures.get(1))
            .map(|value| value.as_str().to_string())
    }

    async fn resolve_myvidplay_stream(iframe_url: &str) -> Option<String> {
        if !Self::is_allowed_myvidplay_iframe_url(iframe_url) {
            return None;
        }

        let html =
            Self::fetch_with_curl_cffi(iframe_url, Some("https://www.porndish.com/")).await?;
        let pass_regex = Self::regex(r#"\$\.get\(\s*['"](/pass_md5/[^'"]+)['"]"#)?;
        let path = pass_regex
            .captures(&html)
            .and_then(|captures| captures.get(1))
            .map(|value| value.as_str().to_string())?;

        let token = path.trim_end_matches('/').rsplit('/').next()?.to_string();
        if token.is_empty() {
            return None;
        }

        let pass_url = if path.starts_with("http://") || path.starts_with("https://") {
            path
        } else {
            let base = Url::parse(iframe_url).ok()?;
            base.join(&path).ok()?.to_string()
        };
        if !Self::is_allowed_myvidplay_pass_url(&pass_url) {
            return None;
        }

        let base = Self::fetch_with_curl_cffi(&pass_url, Some(iframe_url))
            .await?
            .trim()
            .to_string();
        if base.is_empty() || base == "RELOAD" || !base.starts_with("http") {
            return None;
        }

        let chars = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .ok()?
            .as_millis();
        let suffix = (0..10)
            .map(|index| {
                let pos = ((now + (index as u128 * 17)) % chars.len() as u128) as usize;
                chars[pos] as char
            })
            .collect::<String>();

        let stream_url = format!("{base}{suffix}?token={token}&expiry={now}");
        Some(
            Self::resolve_first_redirect(&stream_url)
                .await
                .unwrap_or(stream_url),
        )
    }

    async fn resolve_vidara_stream(iframe_url: &str) -> Option<String> {
        let api_url = Self::vidara_api_url(iframe_url)?;
        let response = Self::fetch_with_curl_cffi(&api_url, Some(iframe_url)).await?;
        let json: serde_json::Value = serde_json::from_str(&response).ok()?;
        let stream_url = json
            .get("streaming_url")
            .and_then(|value| value.as_str())?
            .trim()
            .to_string();
        if stream_url.is_empty() {
            return None;
        }
        Some(stream_url)
    }

    pub async fn get_video_url(
        &self,
        url: String,
        _requester: web::types::State<Requester>,
    ) -> String {
        let Some(detail_url) = Self::normalize_detail_url(&url) else {
            return String::new();
        };
        if !Self::is_allowed_detail_url(&detail_url) {
            return String::new();
        }

        let Some(html) =
            Self::fetch_with_curl_cffi(&detail_url, Some("https://www.porndish.com/")).await
        else {
            return String::new();
        };

        let mut fallback_iframe: Option<String> = None;

        for fragment in Self::extract_iframe_fragments(&html) {
            let Some(iframe_url) = Self::parse_embed_source(&fragment) else {
                continue;
            };

            let iframe_url =
                if iframe_url.starts_with("http://") || iframe_url.starts_with("https://") {
                    iframe_url
                } else if iframe_url.starts_with("//") {
                    format!("https:{iframe_url}")
                } else {
                    continue;
                };

            if Self::is_allowed_vidara_iframe_url(&iframe_url) {
                if let Some(stream_url) = Self::resolve_vidara_stream(&iframe_url).await {
                    return stream_url;
                }
            }

            if fallback_iframe.is_none() && Self::is_allowed_myvidplay_iframe_url(&iframe_url) {
                fallback_iframe = Some(iframe_url);
            }
        }

        if let Some(iframe_url) = fallback_iframe {
            if let Some(stream_url) = Self::resolve_myvidplay_stream(&iframe_url).await {
                return stream_url;
            }
        }

        String::new()
    }
}

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

    #[test]
    fn allows_only_porndish_detail_urls() {
        assert!(PorndishProxy::is_allowed_detail_url(
            "https://www.porndish.com/porn/example/"
        ));
        assert!(!PorndishProxy::is_allowed_detail_url(
            "https://www.porndish.com/search/example/"
        ));
        assert!(!PorndishProxy::is_allowed_detail_url(
            "https://example.com/porn/example/"
        ));
    }
}
