// rmfmailto — Win32 launcher for the OS-level mailto: handler (P115).
//
// Exists for ONE reason: Win11's "Select an app to open this mailto" picker
// reads the EXE's PE FileDescription as the display name. If we register
// `node.exe ... mailx.js --mailto "%1"` the picker shows "Node.js
// JavaScript Runtime" — confusing. With this binary registered instead,
// the picker shows "rmfmail" (set in build.rs's VERSIONINFO).
//
// The runtime job is trivial: receive argv (the mailto URL), find node + the
// installed mailx.js, spawn `node mailx.js --mailto <url>`, exit. Detached
// so we don't hold a handle on a console window.
//
// Lookup order for node + mailx.js:
//   1. NODE_EXE / RMFMAIL_JS env vars (test override / unusual installs).
//   2. %APPDATA%\npm\node_modules\@bobfrankston\rmfmail\bin\mailx.js
//      (standard `npm install -g @bobfrankston/rmfmail` location).
//   3. PATH lookup for `node.exe`.
// Failing any of those, surface a single MessageBox so the user gets a
// readable error instead of a silent dead click.

#![windows_subsystem = "windows"]

use std::env;
use std::path::PathBuf;
use std::process::Command;

#[cfg(windows)]
fn show_error(msg: &str) {
    use std::ffi::OsStr;
    use std::iter::once;
    use std::os::windows::ffi::OsStrExt;
    #[link(name = "user32")]
    extern "system" {
        fn MessageBoxW(hwnd: usize, text: *const u16, caption: *const u16, ty: u32) -> i32;
    }
    let to_wide = |s: &str| -> Vec<u16> {
        OsStr::new(s).encode_wide().chain(once(0)).collect()
    };
    let text = to_wide(msg);
    let caption = to_wide("rmfmail mailto handler");
    unsafe { MessageBoxW(0, text.as_ptr(), caption.as_ptr(), 0x10); }
}

#[cfg(not(windows))]
fn show_error(msg: &str) { eprintln!("{msg}"); }

fn find_node() -> Option<PathBuf> {
    if let Ok(p) = env::var("NODE_EXE") {
        let pb = PathBuf::from(p);
        if pb.is_file() { return Some(pb); }
    }
    // Look up node on PATH. Windows's where.exe finds it but isn't always
    // available; instead walk PATH manually.
    let path = env::var_os("PATH")?;
    for dir in env::split_paths(&path) {
        for name in &["node.exe", "node"] {
            let candidate = dir.join(name);
            if candidate.is_file() { return Some(candidate); }
        }
    }
    None
}

fn find_mailx_js() -> Option<PathBuf> {
    if let Ok(p) = env::var("RMFMAIL_JS") {
        let pb = PathBuf::from(p);
        if pb.is_file() { return Some(pb); }
    }
    // Standard global-install location.
    if let Ok(appdata) = env::var("APPDATA") {
        let pb = PathBuf::from(appdata)
            .join("npm").join("node_modules")
            .join("@bobfrankston").join("rmfmail")
            .join("bin").join("mailx.js");
        if pb.is_file() { return Some(pb); }
    }
    // Fallback: relative to this binary, walking up to find `bin/mailx.js`.
    // Lets a developer-mode build (rmfmailto.exe sitting next to mailx.js
    // in `app/bin/`) work without env-var fiddling.
    if let Ok(exe) = env::current_exe() {
        let mut p = exe.clone();
        for _ in 0..5 {
            if let Some(parent) = p.parent() {
                let candidate = parent.join("mailx.js");
                if candidate.is_file() { return Some(candidate); }
                let candidate = parent.join("bin").join("mailx.js");
                if candidate.is_file() { return Some(candidate); }
                p = parent.to_path_buf();
            } else {
                break;
            }
        }
    }
    None
}

fn main() {
    // argv[0] is the exe path itself; argv[1] should be the mailto URL.
    let mailto_url: Option<String> = env::args().nth(1);
    let mailto_url = match mailto_url {
        Some(u) if !u.is_empty() => u,
        _ => {
            show_error("rmfmailto invoked without a mailto URL.\n\nUsage: rmfmailto.exe <mailto:...>");
            std::process::exit(1);
        }
    };

    let node = match find_node() {
        Some(p) => p,
        None => {
            show_error("Node.js was not found on PATH.\n\nrmfmail requires Node.js to be installed. See https://nodejs.org");
            std::process::exit(2);
        }
    };
    let mailx_js = match find_mailx_js() {
        Some(p) => p,
        None => {
            show_error("rmfmail's mailx.js was not located.\n\nExpected at:\n  %APPDATA%\\npm\\node_modules\\@bobfrankston\\rmfmail\\bin\\mailx.js\n\nSet RMFMAIL_JS to override.");
            std::process::exit(3);
        }
    };

    // Spawn detached so the picker doesn't get a "is this still running?"
    // console window. CREATE_NO_WINDOW (0x08000000) + DETACHED_PROCESS
    // (0x00000008) on Windows; nothing special needed elsewhere.
    let mut cmd = Command::new(&node);
    cmd.arg(&mailx_js).arg("--mailto").arg(&mailto_url);
    #[cfg(windows)]
    {
        use std::os::windows::process::CommandExt;
        cmd.creation_flags(0x08000000 | 0x00000008);
    }
    match cmd.spawn() {
        Ok(_) => {}
        Err(e) => {
            show_error(&format!(
                "Failed to launch rmfmail.\n\nNode: {}\nScript: {}\nError: {e}",
                node.display(), mailx_js.display(),
            ));
            std::process::exit(4);
        }
    }
}
