//! Startup warmup and periodic heartbeat — mimics real webview lifecycle.
//!
//! The real Electron webview calls these methods on startup and then sends
//! Heartbeat every ~30 seconds. Without this, the LS sees a "user" that
//! never initializes and never heartbeats — an obvious bot fingerprint.

use crate::backend::Backend;
use rand::Rng;
use std::sync::Arc;
use std::time::Duration;
use tokio::task::JoinHandle;
use tracing::{debug, info, warn};

/// Run the exact startup sequence the real webview performs on load.
///
/// Called BEFORE accepting any API requests. Each call is fire-and-forget
/// (we don't care if some fail — the LS might not support all methods).
pub async fn warmup_sequence(backend: &Backend, headless: bool) {
    info!("Running webview warmup sequence...");

    // ── CRITICAL: Set detect_and_use_proxy BEFORE any API-triggering call ──
    // The LS creates its HTTP transport lazily on the first API call.
    // If we set ENABLED before that, the LS will honor HTTPS_PROXY env var,
    // routing API traffic through the MITM proxy without iptables/sudo.
    if headless {
        let settings_body = serde_json::json!({
            "userSettings": {
                "detectAndUseProxy": 1  // DETECT_AND_USE_PROXY_ENABLED
            }
        });
        match tokio::time::timeout(
            Duration::from_secs(5),
            backend.call_json("SetUserSettings", &settings_body),
        )
        .await
        {
            Ok(Ok((status, _))) => {
                info!("SetUserSettings (detect_and_use_proxy=ENABLED): {status}")
            }
            Ok(Err(e)) => warn!("SetUserSettings failed: {e}"),
            Err(_) => warn!("SetUserSettings timed out"),
        }
    }

    let calls: &[(&str, serde_json::Value)] = &[
        ("GetStatus", serde_json::json!({})),
        ("Heartbeat", serde_json::json!({})),
        ("GetUserStatus", serde_json::json!({})),
        ("GetCascadeModelConfigs", serde_json::json!({})),
        ("GetCascadeModelConfigData", serde_json::json!({})),
        ("GetWorkspaceInfos", serde_json::json!({})),
        ("GetWorkingDirectories", serde_json::json!({})),
        ("GetAllCascadeTrajectories", serde_json::json!({})),
        ("GetMcpServerStates", serde_json::json!({})),
        ("GetWebDocsOptions", serde_json::json!({})),
        ("GetRepoInfos", serde_json::json!({})),
        ("GetAllSkills", serde_json::json!({})),
        ("InitializeCascadePanelState", serde_json::json!({})),
    ];

    for (method, body) in calls {
        // Timeout per call — in headless mode, the LS can't reach Google's API
        // so these would hang forever without a timeout. Warmup is best-effort.
        match tokio::time::timeout(Duration::from_secs(5), backend.call_json(method, body)).await {
            Ok(Ok((status, _))) => debug!("Warmup {method}: {status}"),
            Ok(Err(e)) => warn!("Warmup {method} failed: {e}"),
            Err(_) => warn!("Warmup {method} timed out"),
        }
        // Small delay between calls — real webview doesn't blast them instantly
        let delay = rand::thread_rng().gen_range(50..200);
        tokio::time::sleep(Duration::from_millis(delay)).await;
    }

    info!("Warmup complete");
}

/// Spawn a background task that sends Heartbeat every ~30s ± jitter.
///
/// Returns a JoinHandle that runs until the task is aborted (on shutdown).
pub fn start_heartbeat(backend: Arc<Backend>) -> JoinHandle<()> {
    tokio::spawn(async move {
        loop {
            // ~30s interval (± 500ms) — matches real setInterval(30000) precision
            let interval_ms = rand::thread_rng().gen_range(29_500..30_500);
            tokio::time::sleep(Duration::from_millis(interval_ms)).await;

            match backend.call_json("Heartbeat", &serde_json::json!({})).await {
                Ok((status, _)) => debug!("Heartbeat: {status}"),
                Err(e) => warn!("Heartbeat failed: {e}"),
            }
        }
    })
}
