// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html

#[cfg(all(unix, not(target_os = "macos")))]
pub mod unix;

#[cfg(target_os = "macos")]
pub mod mac;

#[cfg(target_os = "windows")]
pub mod windows;

use std::path::PathBuf;

use anki_process::CommandExt;
use anyhow::Context;
use anyhow::Result;

pub fn get_exe_and_resources_dirs() -> Result<(PathBuf, PathBuf)> {
    let exe_dir = std::env::current_exe()
        .context("Failed to get current executable path")?
        .parent()
        .context("Failed to get executable directory")?
        .to_owned();

    let resources_dir = if cfg!(target_os = "macos") {
        // On macOS, resources are in ../Resources relative to the executable
        exe_dir
            .parent()
            .context("Failed to get parent directory")?
            .join("Resources")
    } else {
        // On other platforms, resources are in the same directory as executable
        exe_dir.clone()
    };

    Ok((exe_dir, resources_dir))
}

pub fn get_uv_binary_name() -> &'static str {
    if cfg!(target_os = "windows") {
        "uv.exe"
    } else if cfg!(target_os = "macos") {
        "uv"
    } else if cfg!(target_arch = "x86_64") {
        "uv.amd64"
    } else {
        "uv.arm64"
    }
}

pub fn respawn_launcher() -> Result<()> {
    use std::process::Stdio;

    let mut launcher_cmd = if cfg!(target_os = "macos") {
        // On macOS, we need to launch the .app bundle, not the executable directly
        let current_exe =
            std::env::current_exe().context("Failed to get current executable path")?;

        // Navigate from Contents/MacOS/launcher to the .app bundle
        let app_bundle = current_exe
            .parent() // MacOS
            .and_then(|p| p.parent()) // Contents
            .and_then(|p| p.parent()) // .app
            .context("Failed to find .app bundle")?;

        let mut cmd = std::process::Command::new("open");
        cmd.arg(app_bundle);
        cmd
    } else {
        let current_exe =
            std::env::current_exe().context("Failed to get current executable path")?;
        std::process::Command::new(current_exe)
    };

    launcher_cmd
        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .stderr(Stdio::null());

    #[cfg(windows)]
    {
        use std::os::windows::process::CommandExt;
        const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
        const DETACHED_PROCESS: u32 = 0x00000008;
        launcher_cmd.creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS);
    }

    #[cfg(all(unix, not(target_os = "macos")))]
    {
        use std::os::unix::process::CommandExt;
        launcher_cmd.process_group(0);
    }

    let child = launcher_cmd.ensure_spawn()?;
    std::mem::forget(child);

    Ok(())
}

pub fn launch_anki_normally(mut cmd: std::process::Command) -> Result<()> {
    #[cfg(windows)]
    {
        crate::platform::windows::prepare_to_launch_normally();
        cmd.ensure_success()?;
    }
    #[cfg(unix)]
    cmd.ensure_exec()?;
    Ok(())
}

#[cfg(windows)]
pub use windows::ensure_terminal_shown;

#[cfg(unix)]
pub fn ensure_terminal_shown() -> Result<()> {
    use std::io::IsTerminal;

    let want_terminal = std::env::var("ANKI_LAUNCHER_WANT_TERMINAL").is_ok();
    let stdout_is_terminal = IsTerminal::is_terminal(&std::io::stdout());
    if want_terminal || !stdout_is_terminal {
        #[cfg(target_os = "macos")]
        mac::relaunch_in_terminal()?;
        #[cfg(not(target_os = "macos"))]
        unix::relaunch_in_terminal()?;
    }

    // Set terminal title to "Anki Launcher"
    print!("\x1b]2;Anki Launcher\x07");
    Ok(())
}

pub fn ensure_os_supported() -> Result<()> {
    #[cfg(all(unix, not(target_os = "macos")))]
    unix::ensure_glibc_supported()?;

    Ok(())
}
