// SPDX-License-Identifier: GPL-3.0-or-later

// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use tauri::Manager;
use tauri_plugin_shell::process::CommandEvent;
use tauri_plugin_shell::ShellExt;

/// Open a URL in the user's default browser.
///
/// We deliberately do NOT use the shell plugin's `open()` for this. Inside an
/// AppImage the runtime injects `LD_LIBRARY_PATH` and a set of GTK/GDK/GIO/Qt module
/// paths pointing at the bundle's own libraries. A browser launched as a child of the
/// app inherits those and typically fails to start (loading incompatible libs) — which
/// is exactly why clicking "buy LTC with card" appeared to do nothing in the bundle.
/// Here we spawn the system opener ourselves with those variables restored to their
/// pre-AppImage values (`<VAR>_ORIG`, if the runtime kept them) or removed, so the
/// browser launches with a clean environment.
#[tauri::command]
fn open_external(url: String) -> Result<(), String> {
    // Only ever open web URLs — never let the frontend open arbitrary local
    // paths or programs through this command.
    if !(url.starts_with("https://") || url.starts_with("http://")) {
        return Err("refusing to open a non-http(s) URL".into());
    }
    open_in_browser(&url)
}

#[cfg(target_os = "linux")]
fn open_in_browser(url: &str) -> Result<(), String> {
    use std::process::{Command, Stdio};

    // Variables the AppImage runtime overrides; a GUI child that inherits them
    // usually crashes on launch. Restore each to its saved pre-AppImage value if the
    // runtime kept one (`<VAR>_ORIG`), otherwise drop it entirely.
    const POISONED: &[&str] = &[
        "LD_LIBRARY_PATH",
        "LD_PRELOAD",
        "GTK_PATH",
        "GTK_EXE_PREFIX",
        "GTK_DATA_PREFIX",
        "GTK_IM_MODULE_FILE",
        "GDK_PIXBUF_MODULE_FILE",
        "GDK_PIXBUF_MODULEDIR",
        "GIO_MODULE_DIR",
        "GIO_EXTRA_MODULES",
        "GSETTINGS_SCHEMA_DIR",
        "QT_PLUGIN_PATH",
        "QT_QPA_PLATFORM_PLUGIN_PATH",
        "FONTCONFIG_FILE",
        "FONTCONFIG_PATH",
        "PYTHONPATH",
        "PYTHONHOME",
        "PERLLIB",
    ];

    let openers: &[&[&str]] = &[&["xdg-open"], &["gio", "open"], &["x-www-browser"]];
    let mut last_err = String::from("no opener available");
    for opener in openers {
        let mut cmd = Command::new(opener[0]);
        cmd.args(&opener[1..]);
        cmd.arg(url);
        for var in POISONED {
            match std::env::var(format!("{var}_ORIG")) {
                Ok(orig) => {
                    cmd.env(var, orig);
                }
                Err(_) => {
                    cmd.env_remove(var);
                }
            }
        }
        cmd.stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null());
        match cmd.spawn() {
            Ok(_) => return Ok(()),
            Err(e) => last_err = format!("{}: {e}", opener[0]),
        }
    }
    Err(last_err)
}

#[cfg(target_os = "macos")]
fn open_in_browser(url: &str) -> Result<(), String> {
    std::process::Command::new("open")
        .arg(url)
        .spawn()
        .map(|_| ())
        .map_err(|e| e.to_string())
}

#[cfg(target_os = "windows")]
fn open_in_browser(url: &str) -> Result<(), String> {
    std::process::Command::new("cmd")
        .args(["/C", "start", "", url])
        .spawn()
        .map(|_| ())
        .map_err(|e| e.to_string())
}

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_plugin_notification::init())
        // The updater plugin is configured in tauri.conf.json to fetch its
        // manifest from the local backend daemon (http://127.0.0.1:62774).
        // The daemon downloads the public manifest through Tor and serves
        // a Tauri-shaped JSON; the desktop bundle download is similarly
        // proxied so all update traffic remains on the Tor circuit.
        .plugin(tauri_plugin_updater::Builder::new().build())
        .invoke_handler(tauri::generate_handler![open_external])
        .setup(|app| {
            // Start the bundled backend daemon (sidecar). It listens on
            // 127.0.0.1:62774, manages Tor + the Restic backups, and serves the
            // local API the UI talks to. Without this the bundled desktop app
            // has no daemon to reach. Failures are logged rather than fatal so
            // the UI still loads and can surface a connection error.
            match app.shell().sidecar("privatebackup-backend") {
                // PRIVATEBACKUP_MANAGED tells the daemon the desktop app owns its
                // lifecycle, so it skips auto-installing a conflicting boot service.
                Ok(cmd) => match cmd.env("PRIVATEBACKUP_MANAGED", "1").spawn() {
                    Ok((mut rx, child)) => {
                        // Keep the child alive for the app's lifetime; Tauri
                        // terminates sidecar processes when the app exits.
                        app.manage(std::sync::Mutex::new(Some(child)));
                        // Drain daemon output so its logs are visible and the
                        // pipe never fills and blocks the child.
                        tauri::async_runtime::spawn(async move {
                            while let Some(event) = rx.recv().await {
                                if let CommandEvent::Stdout(line) | CommandEvent::Stderr(line) =
                                    event
                                {
                                    eprint!("[backend] {}", String::from_utf8_lossy(&line));
                                }
                            }
                        });
                    }
                    Err(e) => eprintln!("[backend] failed to spawn daemon: {e}"),
                },
                Err(e) => eprintln!("[backend] failed to create sidecar command: {e}"),
            }
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
