use md5::compute as md5_hash;
use std::{env, fs, path::PathBuf, process::Command};

fn run_pnpm(args: &[&str], cwd: &str) {
    let mut cmd = if cfg!(target_os = "windows") {
        let mut c = Command::new("cmd");
        c.args(["/C", "pnpm"]);
        c
    } else {
        Command::new("pnpm")
    };

    let status = cmd
        .args(args)
        .current_dir(cwd)
        .status()
        .expect("failed to run pnpm");

    if !status.success() {
        panic!("pnpm command failed: {:?}", args);
    }
}

fn find_all_file(path: PathBuf) -> Vec<PathBuf> {
    let mut files = vec![];
    for entry in fs::read_dir(path).unwrap() {
        let entry = entry.unwrap();
        let path = entry.path();
        if path.is_file() {
            files.push(path);
        } else if path.is_dir() {
            files.extend(find_all_file(path));
        }
    }
    files
}

fn main() {
    println!("cargo:rerun-if-changed=../../package.json");
    println!("cargo:rerun-if-changed=../../pnpm-lock.yaml");
    println!("cargo:rerun-if-changed=../../tsconfig.json");
    println!("cargo:rerun-if-changed=../../src/");

    let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let project_root = manifest_dir.parent().unwrap().parent().unwrap();

    let node_modules = project_root.join("node_modules");
    let _cache_file = node_modules.join(".cache_hash");

    // Build frontend using pnpm in project root
    println!("cargo:warning=Building frontend...");
    run_pnpm(&["run", "build"], project_root.to_str().unwrap());

    // Embed dist/ into OUT_DIR as blob files + generated .rs
    let dist = project_root.join("dist");
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let blob_dir = out_dir.join("dist_blobs");
    fs::create_dir_all(&blob_dir).unwrap();

    let mut strs = vec![];
    for file in find_all_file(dist.clone()) {
        let key = file.strip_prefix(&dist).unwrap()
            .components()
            .collect::<PathBuf>()
            .to_string_lossy()
            .replace('\\', "/");
        let safe_name = key.replace('/', "_").replace('\\', "_");
        let blob_path = blob_dir.join(&safe_name);
        fs::copy(&file, &blob_path).unwrap();

        // Compute ETag (MD5 hex of content) at build time
        let content = fs::read(&file).unwrap();
        let hash = md5_hash(&content);
        let etag_literal = format!("\"{:x}\"", hash);

        let key_literal = format!("\"{}\"", key.replace('"', "\\\""));
        strs.push(format!(
            "    ({}, include_bytes!(\"dist_blobs/{}\"), {}),",
            key_literal, safe_name, etag_literal
        ));
    }

    let out_file = out_dir.join("frontend.rs");
    let generated = format!(
        "lazy_static::lazy_static! {{\n    pub static ref FRONTEND: Vec<(&'static str, &'static [u8], &'static str)> = vec![\n{}    ];\n}}\n",
        strs.join("\n")
    );
    fs::write(&out_file, generated).unwrap();
    println!("cargo:include={}", out_file.display());
}
