use base64::{engine::general_purpose, Engine};
use ntex::web;
use crate::util::requester::Requester;
use crate::videos::VideoFormat;
use crate::providers::report_provider_error_background;

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

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

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

    fn decode_data_enc(encoded_data: &str) -> Option<Vec<VideoFormat>> {
        let cleaned_data = encoded_data.replace("-", "+").replace("_", "/");
        let padding = (4 - cleaned_data.len() % 4) % 4;
        let padded_data = format!("{}{}", cleaned_data, "=".repeat(padding));

        let decoded_bytes = match general_purpose::STANDARD.decode(&padded_data) {
            Ok(bytes) => bytes,
            Err(e) => {
                report_provider_error_background(CHANNEL_ID, "proxy.decode_data_enc.base64", &format!("error={e}"));
                return None;
            }
        };

        if decoded_bytes.is_empty() {
            report_provider_error_background(CHANNEL_ID, "proxy.decode_data_enc.empty", "decoded bytes are empty");
            return None;
        }

        let key = decoded_bytes[0];
        let decrypted_bytes: Vec<u8> = decoded_bytes[1..]
            .iter()
            .map(|&b| b ^ key)
            .collect();

        let json_str = match String::from_utf8(decrypted_bytes) {
            Ok(s) => s,
            Err(e) => {
                report_provider_error_background(CHANNEL_ID, "proxy.decode_data_enc.utf8", &format!("error={e}"));
                return None;
            }
        };

        let formats_json: serde_json::Value = match serde_json::from_str(&json_str) {
            Ok(value) => value,
            Err(e) => {
                report_provider_error_background(CHANNEL_ID, "proxy.decode_data_enc.json_parse", &format!("error={e}"));
                return None;
            }
        };

        let Some(formats_array) = formats_json.as_array() else {
            report_provider_error_background(CHANNEL_ID, "proxy.decode_data_enc.not_array", "JSON is not an array");
            return None;
        };

        let mut formats = Vec::new();
        for format_val in formats_array {
            let Some(u) = format_val.get("u").and_then(|v| v.as_str()) else { continue; };
            let Some(q) = format_val.get("q").and_then(|v| v.as_str()) else { continue; };
            
            let mut format = VideoFormat::new(
                u.to_string(),
                q.to_string(),
                "application/x-mpegURL".to_string(),
            );
            // Add referer to the format
            format.add_http_header("Referer".to_string(), BASE_URL.to_string());
            formats.push(format);
        }

        Some(formats)
    }

    pub async fn get_video_url(
        &self,
        url_path: String, // Expects something like videos/2019/thai-student-have-sex-at-taxi-1658/
        requester: web::types::State<Requester>,
    ) -> String {
        let mut requester = requester.get_ref().clone();
        let video_detail_url = format!("{}/{}", BASE_URL, url_path.trim_start_matches('/'));

        let html = match requester.get(&video_detail_url, None).await {
            Ok(text) => text,
            Err(e) => {
                report_provider_error_background(CHANNEL_ID, "proxy.get_video_url.fetch_html", &format!("url={}; error={}", video_detail_url, e));
                return String::new();
            }
        };

        let document = scraper::Html::parse_document(&html);
        let video_element_selector = scraper::Selector::parse("video[data-enc]").unwrap();

        if let Some(video_element) = document.select(&video_element_selector).next() {
            if let Some(data_enc) = video_element.value().attr("data-enc") {
                if let Some(formats) = Self::decode_data_enc(data_enc) {
                    // Return the URL of the best quality format, or the first one if quality is not a factor.
                    // For m3u8, we typically just return the m3u8 playlist URL.
                    if let Some(format) = formats.first() {
                        return format.url.clone();
                    }
                }
            }
        }
        String::new()
    }
}
