//! `cargo-supermachine` — cargo subcommand plugin that wraps
//! `cargo build` with `supermachine codesign`, so embedders can
//! drop in a one-step replacement for `cargo build`:
//!
//! ```text
//! cargo install supermachine            # gets you cargo-supermachine too
//! cargo add supermachine                 # in your app
//! cargo supermachine build --release    # builds + codesigns target/release/<bin>
//! ```
//!
//! Cargo dispatches `cargo supermachine X` to a binary named
//! `cargo-supermachine` on PATH and invokes it with argv:
//!
//! ```text
//! cargo-supermachine supermachine X [flags...]
//! ```
//!
//! We strip the leading `supermachine` arg (the cargo dispatch
//! marker), pass the rest through to `cargo`, and post-process by
//! shelling out to the `supermachine` CLI's `codesign` subcommand
//! on each binary built.

use std::process::{Command, ExitCode};

fn main() -> ExitCode {
    let raw: Vec<String> = std::env::args().collect();
    // argv[0] is the binary name (cargo-supermachine), argv[1] is
    // the cargo dispatch marker ("supermachine"). Strip both.
    let mut args = raw.into_iter().skip(1).peekable();
    if matches!(args.peek().map(|s| s.as_str()), Some("supermachine")) {
        let _ = args.next();
    }

    let sub = match args.next() {
        Some(s) => s,
        None => {
            print_usage();
            return ExitCode::from(1);
        }
    };

    let rest: Vec<String> = args.collect();
    match sub.as_str() {
        "build" | "run" | "test" | "check" => run_cargo_then_sign(&sub, &rest),
        "-h" | "--help" | "help" => {
            print_usage();
            ExitCode::SUCCESS
        }
        other => {
            eprintln!("cargo-supermachine: unknown subcommand: {other}");
            print_usage();
            ExitCode::from(2)
        }
    }
}

fn print_usage() {
    eprintln!(
        "cargo-supermachine — wraps cargo build/run/test/check with codesign\n\
         \n\
         USAGE:\n  \
           cargo supermachine build [cargo args...]    # cargo build, then codesign\n  \
           cargo supermachine run   [cargo args...]    # cargo build + codesign + run\n  \
           cargo supermachine test  [cargo args...]    # cargo test (codesigns test bins)\n  \
           cargo supermachine check [cargo args...]    # cargo check (no codesign needed)\n\
         \n\
         What it does:\n  \
           1. Invokes `cargo <sub> [flags...]`\n  \
           2. On macOS, runs `supermachine codesign <binary>` on every\n     \
              built artifact in target/{{debug,release}}/.\n  \
           3. For `run`, executes the codesigned binary with the trailing args.\n\
         \n\
         Requires `supermachine` on PATH (`cargo install supermachine`)."
    );
}

fn run_cargo_then_sign(sub: &str, rest: &[String]) -> ExitCode {
    // 1. cargo <sub> [flags...]
    let cargo_status = Command::new("cargo").arg(sub).args(rest).status();
    let cargo_status = match cargo_status {
        Ok(s) => s,
        Err(e) => {
            eprintln!("cargo-supermachine: spawn cargo: {e}");
            return ExitCode::from(127);
        }
    };
    if !cargo_status.success() {
        return cargo_status
            .code()
            .map(|c| ExitCode::from(c as u8))
            .unwrap_or(ExitCode::from(1));
    }

    // 2. On non-Darwin, codesign is a no-op — done.
    if !cfg!(target_os = "macos") {
        if sub == "run" {
            // Cargo already ran; nothing more to do.
        }
        return ExitCode::SUCCESS;
    }

    // Find the supermachine binary on PATH for the codesign step.
    let sm = which_supermachine();
    let sm = match sm {
        Some(p) => p,
        None => {
            eprintln!("cargo-supermachine: `supermachine` not on PATH (run `cargo install supermachine`).");
            eprintln!("                    skipping codesign step; binary will fail with HV_DENIED at runtime.");
            return ExitCode::SUCCESS;
        }
    };

    // 3. Sweep target/{debug,release}/ for fresh artifacts.
    //    Conservative: sign every binary in the matching profile dir;
    //    codesign is idempotent so repeated signing is safe.
    let profile = if rest.iter().any(|a| a == "--release") {
        "release"
    } else {
        "debug"
    };
    let target_dir = std::env::var("CARGO_TARGET_DIR")
        .map(std::path::PathBuf::from)
        .unwrap_or_else(|_| std::path::PathBuf::from("target"));
    let prof_dir = target_dir.join(profile);

    let mut signed = 0;
    if let Ok(read) = std::fs::read_dir(&prof_dir) {
        for entry in read.flatten() {
            let path = entry.path();
            let Ok(meta) = entry.metadata() else { continue };
            if !meta.is_file() {
                continue;
            }
            // Skip non-executables and dotfiles.
            #[cfg(unix)]
            {
                use std::os::unix::fs::PermissionsExt;
                if meta.permissions().mode() & 0o111 == 0 {
                    continue;
                }
            }
            // Heuristic: only sign Mach-O (no extension or bundle suffix).
            let name = path.file_name().and_then(|s| s.to_str()).unwrap_or("");
            if name.starts_with('.')
                || name.contains('.')   // skip *.d, *.dSYM, etc.
                || name.ends_with("-d")
            {
                continue;
            }

            let sign = Command::new(&sm).arg("codesign").arg(&path).status();
            match sign {
                Ok(s) if s.success() => {
                    signed += 1;
                }
                Ok(s) => {
                    eprintln!(
                        "cargo-supermachine: codesign {} failed (exit {:?})",
                        path.display(),
                        s.code()
                    );
                    return ExitCode::from(1);
                }
                Err(e) => {
                    eprintln!("cargo-supermachine: spawn supermachine codesign: {e}");
                    return ExitCode::from(127);
                }
            }
        }
    }
    if signed == 0 && sub != "check" {
        eprintln!(
            "cargo-supermachine: no Mach-O artifacts found in {} — nothing to sign.",
            prof_dir.display()
        );
    }
    ExitCode::SUCCESS
}

fn which_supermachine() -> Option<std::path::PathBuf> {
    let path = std::env::var_os("PATH")?;
    for dir in std::env::split_paths(&path) {
        let candidate = dir.join("supermachine");
        if candidate.is_file() {
            return Some(candidate);
        }
    }
    None
}
