//! HTTP-site daemon integration. Unlike `smoke_tests.rs` (MockDriver), this runs
//! the REAL `TailFinSession` driver to prove the HTTP launch branch builds no
//! Chrome and that a business command reaches the descriptor `Executor`.
//!
//! Hermetic: alphora's launch builds no browser and no network is touched until
//! a real `alphora.*` wire is dispatched. We dispatch an UNKNOWN wire so the
//! executor returns its typed error without any HTTP call. A live test that
//! issues a real wire is `#[ignore]` (see `alphora_real_wire_live`).

use std::time::Duration;

use night_fury_daemon_core::cli::make_req;
use night_fury_daemon_core::client;
use serde_json::json;
use tempfile::tempdir;

async fn spawn_real_daemon(sock_dir: &std::path::Path) -> (String, tokio::task::JoinHandle<()>) {
    // Register the real site registry so HTTP sites are present.
    tail_fin_daemon::driver::register_sites(tail_fin_daemon::registry::all_site_entries().to_vec());

    let sock = sock_dir.join("tfd-http.sock").to_string_lossy().to_string();
    let sock_bg = sock.clone();
    let handle = tokio::spawn(async move {
        let _ = tail_fin_daemon::server::serve(&sock_bg, 300, 900, 3).await;
    });
    for _ in 0..50 {
        if tokio::net::UnixStream::connect(&sock).await.is_ok() {
            break;
        }
        tokio::time::sleep(Duration::from_millis(100)).await;
    }
    (sock, handle)
}

#[tokio::test]
async fn http_site_acquire_builds_no_browser() {
    let dir = tempdir().unwrap();
    let (sock, daemon) = spawn_real_daemon(dir.path()).await;

    // Acquire alphora. The signal is that this returns OK with NO reachable CDP
    // host and no Chrome on the box — only the HTTP launch branch (which builds no
    // browser) can succeed here. host is irrelevant for HTTP entries. The 10s
    // wrap below is a hang-backstop, not the proof.
    let acquire = make_req(
        "session.acquire",
        None,
        json!({"site": "alphora", "host": "auto", "mode": "in_use"}),
    );
    let resp = tokio::time::timeout(Duration::from_secs(10), client::send(&sock, &acquire))
        .await
        .expect("acquire must not hang on a browser launch")
        .unwrap();
    assert!(
        resp.ok,
        "alphora acquire (HTTP, no browser) should succeed: {:?}",
        resp.error
    );
    let sid = resp.data.unwrap()["session_id"]
        .as_str()
        .unwrap()
        .to_string();

    // Dispatch an UNKNOWN wire — reaches AlphoraExecutor (descriptor), which
    // returns its typed error WITHOUT any network call. Proves the handler is the
    // real ExecutorHandler and not a browser path.
    let biz = client::send(
        &sock,
        &make_req("alphora.not-a-real-wire", Some(&sid), json!({})),
    )
    .await
    .unwrap();
    assert!(!biz.ok, "unknown wire should error");
    assert!(
        biz.error
            .as_deref()
            .unwrap_or("")
            .contains("unknown alphora wire"),
        "error should come from AlphoraExecutor, got {:?}",
        biz.error
    );

    let _ = client::send(&sock, &make_req("daemon.shutdown", None, json!({}))).await;
    let _ = tokio::time::timeout(Duration::from_secs(3), daemon).await;
}

#[tokio::test]
async fn http_site_acquire_reuses_idle_session() {
    let dir = tempdir().unwrap();
    let (sock, daemon) = spawn_real_daemon(dir.path()).await;

    let r1 = client::send(
        &sock,
        &make_req(
            "session.acquire",
            None,
            json!({"site": "arkham", "host": "auto", "mode": "in_use"}),
        ),
    )
    .await
    .unwrap();
    assert!(r1.ok, "arkham acquire failed: {:?}", r1.error);
    let sid1 = r1.data.unwrap()["session_id"].as_str().unwrap().to_string();

    let _ = client::send(&sock, &make_req("session.release", Some(&sid1), json!({}))).await;

    let r2 = client::send(
        &sock,
        &make_req(
            "session.acquire",
            None,
            json!({"site": "arkham", "host": "auto", "mode": "in_use"}),
        ),
    )
    .await
    .unwrap();
    let data = r2.data.unwrap();
    assert_eq!(data["session_id"].as_str().unwrap(), &sid1);
    assert_eq!(data["reused"].as_bool(), Some(true));

    let _ = client::send(&sock, &make_req("daemon.shutdown", None, json!({}))).await;
    let _ = tokio::time::timeout(Duration::from_secs(3), daemon).await;
}

/// Live: issues a REAL alphora wire over the daemon (network). Run serially with
/// `--ignored --test-threads=1`. Proves the full HTTP exec path end-to-end.
#[tokio::test]
#[ignore = "live: hits insights.alphoragroup.com over the network"]
async fn alphora_real_wire_live() {
    let dir = tempdir().unwrap();
    let (sock, daemon) = spawn_real_daemon(dir.path()).await;

    let r = client::send(
        &sock,
        &make_req(
            "session.acquire",
            None,
            json!({"site": "alphora", "host": "auto", "mode": "in_use"}),
        ),
    )
    .await
    .unwrap();
    let sid = r.data.unwrap()["session_id"].as_str().unwrap().to_string();

    let biz = client::send(
        &sock,
        &make_req("alphora.asset-universe", Some(&sid), json!({})),
    )
    .await
    .unwrap();
    assert!(
        biz.ok,
        "live alphora.asset-universe failed: {:?}",
        biz.error
    );

    let _ = client::send(&sock, &make_req("daemon.shutdown", None, json!({}))).await;
    let _ = tokio::time::timeout(Duration::from_secs(3), daemon).await;
}

/// HTTP sites build no browser, so the requested --host is meaningless. The
/// pool must key them host-agnostically: acquiring an HTTP site under one host
/// then re-acquiring under a DIFFERENT host must reuse the same session, not
/// fragment the pool.
#[tokio::test]
async fn http_site_pool_reuse_is_host_agnostic() {
    let dir = tempdir().unwrap();
    let (sock, daemon) = spawn_real_daemon(dir.path()).await;

    let r1 = client::send(
        &sock,
        &make_req(
            "session.acquire",
            None,
            json!({"site": "alphora", "host": "127.0.0.1:1111", "mode": "in_use"}),
        ),
    )
    .await
    .unwrap();
    assert!(r1.ok, "first acquire failed: {:?}", r1.error);
    let sid1 = r1.data.unwrap()["session_id"].as_str().unwrap().to_string();
    let _ = client::send(&sock, &make_req("session.release", Some(&sid1), json!({}))).await;

    // Re-acquire with a totally different host — should reuse the SAME session.
    let r2 = client::send(
        &sock,
        &make_req(
            "session.acquire",
            None,
            json!({"site": "alphora", "host": "9.9.9.9:9999", "mode": "in_use"}),
        ),
    )
    .await
    .unwrap();
    assert!(r2.ok, "second acquire failed: {:?}", r2.error);
    let d2 = r2.data.unwrap();
    assert_eq!(
        d2["session_id"].as_str().unwrap(),
        sid1,
        "HTTP pool reuse must ignore the requested host"
    );
    assert_eq!(d2["reused"].as_bool(), Some(true));

    let _ = client::send(&sock, &make_req("daemon.shutdown", None, json!({}))).await;
    let _ = tokio::time::timeout(Duration::from_secs(3), daemon).await;
}
