//! End-to-end test for `adom-video-post publish`.
//!
//! It generates a tiny real mp4, runs the actual `publish` subcommand against a
//! live wiki page, then curls the URL the command returns to confirm the file
//! serves as a valid `ftyp` mp4 over HTTP.
//!
//! It needs network + a writable wiki page, so it is gated behind an env var and
//! is a no-op (passes) under a plain `cargo test`:
//!
//!   VIDEO_POST_TEST_PAGE=adom/adom-video-post cargo test --test publish_serves -- --nocapture
//!
//! Requires `ffmpeg`, `adom-wiki`, and `curl` on PATH.

use std::process::Command;

const BIN: &str = env!("CARGO_BIN_EXE_adom-video-post");
const TEST_FILE: &str = "_video-post-selftest.mp4";

#[test]
fn publish_pushes_and_serves_a_valid_mp4() {
    let page = match std::env::var("VIDEO_POST_TEST_PAGE") {
        Ok(p) if !p.trim().is_empty() => p,
        _ => {
            eprintln!(
                "skipping: set VIDEO_POST_TEST_PAGE=<owner>/<slug> to run the live publish test"
            );
            return;
        }
    };

    // 1. Generate a tiny but real mp4 named so the served URL is predictable.
    let mp4 = std::env::temp_dir().join(TEST_FILE);
    let _ = std::fs::remove_file(&mp4);
    let ff = Command::new("ffmpeg")
        .args(["-y", "-f", "lavfi", "-i", "color=c=teal:s=128x128:d=1", "-r", "8"])
        .arg(&mp4)
        .output()
        .expect("spawn ffmpeg");
    assert!(
        ff.status.success(),
        "ffmpeg failed: {}",
        String::from_utf8_lossy(&ff.stderr)
    );
    assert!(mp4.exists(), "ffmpeg produced no file at {}", mp4.display());

    // 2. Run the real publish subcommand.
    let out = Command::new(BIN)
        .args(["publish", "--input"])
        .arg(&mp4)
        .args(["--page", &page, "--caption", "video-post publish self-test"])
        .output()
        .expect("spawn adom-video-post");
    let stdout = String::from_utf8_lossy(&out.stdout);
    let stderr = String::from_utf8_lossy(&out.stderr);
    assert!(
        out.status.success(),
        "publish failed:\nstdout: {stdout}\nstderr: {stderr}"
    );

    // 3. Extract the served URL the command printed (strip ANSI color codes).
    let plain = strip_ansi(&stdout);
    let url = plain
        .split_whitespace()
        .find(|t| t.starts_with("https://") && t.ends_with(".mp4"))
        .unwrap_or_else(|| panic!("no served mp4 URL in output:\n{plain}"))
        .to_string();
    eprintln!("served URL: {url}");

    // 4. Curl the URL and confirm it serves a valid ftyp mp4.
    let head = Command::new("curl")
        .args(["-s", "--max-time", "60", &url])
        .output()
        .expect("spawn curl");
    assert!(head.status.success(), "curl failed for {url}");
    let body = head.stdout;
    assert!(body.len() > 64, "served body too small ({} bytes)", body.len());
    // ISO base-media files carry the `ftyp` box marker at bytes 4..8.
    let marker = &body[4..8.min(body.len())];
    assert_eq!(
        marker, b"ftyp",
        "served bytes are not a valid mp4 (expected ftyp at offset 4, got {:?})",
        String::from_utf8_lossy(marker)
    );

    // 5. Best-effort cleanup: remove the test file from the page repo.
    let _ = Command::new("adom-wiki")
        .args(["repo", "rm", &page, TEST_FILE])
        .output();
    let _ = std::fs::remove_file(&mp4);
}

fn strip_ansi(s: &str) -> String {
    let mut out = String::with_capacity(s.len());
    let bytes = s.as_bytes();
    let mut i = 0;
    while i < bytes.len() {
        if bytes[i] == 0x1b {
            // Skip CSI sequence: ESC '[' ... final byte in 0x40..=0x7e
            i += 1;
            if i < bytes.len() && bytes[i] == b'[' {
                i += 1;
                while i < bytes.len() && !(0x40..=0x7e).contains(&bytes[i]) {
                    i += 1;
                }
                i += 1; // consume final byte
            }
        } else {
            out.push(bytes[i] as char);
            i += 1;
        }
    }
    out
}