use playwright::Playwright;
use playwright::api::Page;
use std::path::Path;
use std::time::Duration;
use tokio::time::{sleep, timeout};

const POLL_INTERVAL: Duration = Duration::from_millis(500);
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);

const CHROME_ARGS: &[&str] = &[
    "--no-sandbox",
    "--disable-dev-shm-usage",
    "--no-first-run",
    "--no-default-browser-check",
    "--disable-background-timer-throttling",
    "--disable-backgrounding-occluded-windows",
    "--disable-renderer-backgrounding",
    "--disable-ipc-flooding-protection",
    "--disable-hang-monitor",
    "--disable-gpu",
    "--disable-software-rasterizer",
    "--disable-accelerated-2d-canvas",
    "--disable-webgl",
    "--disable-3d-apis",
    "--blink-settings=imagesEnabled=false",
    "--mute-audio",
    "--disable-background-networking",
    "--disable-client-side-phishing-detection",
    "--disable-component-update",
    "--disable-domain-reliability",
    "--disable-sync",
    "--metrics-recording-only",
    "--safebrowsing-disable-auto-update",
    "--disable-extensions",
    "--disable-translate",
    "--disable-default-apps",
    "--disable-features=site-per-process",
    "--password-store=basic",
    "--use-mock-keychain",
];

/// Launches a headless Chrome browser via Playwright, navigates to `url`, and
/// polls until the element matched by `xpath` has a non-empty `src` attribute.
pub async fn wait_for_src(url: &str, xpath: &str) -> Option<String> {
    wait_for_attribute(url, xpath, "src", DEFAULT_TIMEOUT).await
}

/// Like [`wait_for_src`] but lets the caller choose the attribute name and timeout.
pub async fn wait_for_attribute(
    url: &str,
    xpath: &str,
    attribute: &str,
    wait_timeout: Duration,
) -> Option<String> {
    // _pw must be kept alive — dropping it kills the driver process.
    let (_pw, page) = open_page(url).await?;

    let xpath_js = serde_json::to_string(xpath).unwrap_or_default();
    let attr_js = serde_json::to_string(attribute).unwrap_or_default();
    let js = format!(
        r#"() => {{
            var r = document.evaluate({xpath_js}, document, null,
                        XPathResult.FIRST_ORDERED_NODE_TYPE, null);
            var el = r.singleNodeValue;
            if (!el) return '';
            return el.getAttribute({attr_js}) || el[{attr_js}] || '';
        }}"#
    );

    let result = timeout(wait_timeout, async {
        loop {
            match page.evaluate::<(), String>(&js, ()).await {
                Ok(s) if !s.is_empty() => return Some(s),
                Ok(_) => {}
                Err(e) => eprintln!("[playwright] evaluate error: {e}"),
            }
            sleep(POLL_INTERVAL).await;
        }
    })
    .await
    .ok()
    .flatten();

    result
}

// ── internals ─────────────────────────────────────────────────────────────────

async fn open_page(url: &str) -> Option<(Playwright, Page)> {
    let pw = Playwright::initialize()
        .await
        .map_err(|e| eprintln!("[playwright] init error: {e}"))
        .ok()?;

    let args: Vec<String> = CHROME_ARGS.iter().map(|s| s.to_string()).collect();

    let browser = pw
        .chromium()
        .launcher()
        .executable(Path::new("/usr/bin/google-chrome"))
        .headless(true)
        .args(&args)
        .launch()
        .await
        .map_err(|e| eprintln!("[playwright] launch error: {e}"))
        .ok()?;

    let context = browser
        .context_builder()
        .build()
        .await
        .map_err(|e| eprintln!("[playwright] context error: {e}"))
        .ok()?;

    let page = context
        .new_page()
        .await
        .map_err(|e| eprintln!("[playwright] new_page error: {e}"))
        .ok()?;

    page.goto_builder(url)
        .goto()
        .await
        .map_err(|e| eprintln!("[playwright] goto error: {e}"))
        .ok()?;

    Some((pw, page))
}
