//! Builds the Solid management UI for embedded Release binaries.
//!
//! Debug builds intentionally return before invoking npm: during development
//! Vite serves `web/` independently and proxies its API client to the Rust
//! listener. Release builds use the lockfile, write only into Cargo `OUT_DIR`,
//! and expose a cfg consumed by `src/web.rs`. The source tree therefore never
//! needs a generated `dist/` for `cargo build --release`.
//!
//! Set `WASMELD_BUILD_WEB=1` to exercise the embedded path in another Cargo
//! profile, or `NPM=/absolute/path/to/npm` when npm is not on `PATH`.

use std::{
    env, fs,
    path::PathBuf,
    process::{Command, ExitStatus},
};

const WEB_INPUTS: &[&str] = &[
    "web/src",
    "web/public",
    "web/index.html",
    "web/page.html",
    "web/package.json",
    "web/package-lock.json",
    "web/tsconfig.json",
    "web/vite.config.ts",
];

fn main() {
    println!("cargo:rustc-check-cfg=cfg(wasmeld_embedded_web)");
    println!("cargo:rerun-if-env-changed=WASMELD_BUILD_WEB");
    println!("cargo:rerun-if-env-changed=NPM");
    for input in WEB_INPUTS {
        println!("cargo:rerun-if-changed={input}");
    }

    let force = env::var("WASMELD_BUILD_WEB").is_ok_and(|value| value == "1");
    if env::var("PROFILE").as_deref() != Ok("release") && !force {
        return;
    }

    let crate_dir = PathBuf::from(required_env("CARGO_MANIFEST_DIR"));
    let web_dir = crate_dir.join("web");
    let output_dir = PathBuf::from(required_env("OUT_DIR")).join("web");
    let npm = env::var_os("NPM")
        .map(PathBuf::from)
        .unwrap_or_else(|| PathBuf::from(if cfg!(windows) { "npm.cmd" } else { "npm" }));

    if output_dir.exists() {
        fs::remove_dir_all(&output_dir).unwrap_or_else(|error| {
            panic!(
                "failed to clear embedded Web output {}: {error}",
                output_dir.display()
            )
        });
    }

    // `npm ci` is deliberate here: a release must not silently rewrite the
    // dependency graph represented by package-lock.json.
    run(
        Command::new(&npm).current_dir(&web_dir).args([
            "ci",
            "--ignore-scripts",
            "--no-audit",
            "--no-fund",
        ]),
        "install locked Web dependencies",
    );
    run(
        Command::new(&npm)
            .current_dir(&web_dir)
            .env("WASMELD_WEB_OUT_DIR", &output_dir)
            .args(["run", "build"]),
        "build embedded Web application",
    );

    for required in ["index.html", "page.html", "sw.js", "manifest.webmanifest"] {
        let path = output_dir.join(required);
        assert!(
            path.is_file(),
            "Web build did not produce required asset {}",
            path.display()
        );
    }
    println!("cargo:rustc-cfg=wasmeld_embedded_web");
}

fn run(command: &mut Command, action: &str) {
    let status = command
        .status()
        .unwrap_or_else(|error| panic!("failed to {action}: {error}"));
    assert_success(status, action);
}

fn assert_success(status: ExitStatus, action: &str) {
    assert!(
        status.success(),
        "failed to {action}: process exited with {status}"
    );
}

fn required_env(name: &str) -> String {
    env::var(name).unwrap_or_else(|_| panic!("Cargo did not provide {name}"))
}
