use std::{
    env,
    path::{Path, PathBuf},
    process::{Command, ExitCode},
};

#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;

#[cfg(target_os = "windows")]
const CREATE_NO_WINDOW: u32 = 0x08000000;

fn main() -> ExitCode {
    match run() {
        Ok(()) => ExitCode::SUCCESS,
        Err(err) => {
            eprintln!("{err}");
            ExitCode::FAILURE
        }
    }
}

fn run() -> Result<(), String> {
    let mut args = env::args().skip(1);
    let command = args.next().ok_or_else(|| "missing command".to_string())?;
    let flag = args.next().ok_or_else(|| "missing --profile flag".to_string())?;
    if flag != "--profile" {
        return Err("expected --profile flag".into());
    }
    let profile = PathBuf::from(args.next().ok_or_else(|| "missing profile path".to_string())?);

    match command.as_str() {
        "connect" => connect(&profile),
        "disconnect" => disconnect(&profile),
        _ => Err("unsupported command".into()),
    }
}

fn connect(profile: &Path) -> Result<(), String> {
    #[cfg(target_os = "windows")]
    {
        ensure_windows_admin()?;
        let wireguard = find_windows_wireguard()?;
        let output = Command::new(wireguard)
            .arg("/installtunnelservice")
            .arg(profile)
            .creation_flags(CREATE_NO_WINDOW)
            .output()
            .map_err(|err| format!("unable to launch WireGuard runtime: {err}"))?;
        if !output.status.success() {
            return Err(format_windows_runtime_error("connect", &output));
        }
        return Ok(());
    }

    #[cfg(target_os = "macos")]
    {
        let command = format!("wg-quick up '{}'", profile.display());
        let status = Command::new("osascript")
            .arg("-e")
            .arg(format!("do shell script \"{}\" with administrator privileges", command))
            .status()
            .map_err(|err| format!("unable to start tunnel: {err}"))?;
        if !status.success() {
            return Err(format!("macOS tunnel connect failed with status {status}"));
        }
        return Ok(());
    }

    #[allow(unreachable_code)]
    Err("unsupported platform".into())
}

fn disconnect(profile: &Path) -> Result<(), String> {
    #[cfg(target_os = "windows")]
    {
        ensure_windows_admin()?;
        let wireguard = find_windows_wireguard()?;
        let tunnel_name = profile
            .file_stem()
            .and_then(|value| value.to_str())
            .ok_or_else(|| "invalid profile filename".to_string())?;
        let output = Command::new(wireguard)
            .arg("/uninstalltunnelservice")
            .arg(tunnel_name)
            .creation_flags(CREATE_NO_WINDOW)
            .output()
            .map_err(|err| format!("unable to launch WireGuard runtime: {err}"))?;
        if !output.status.success() {
            return Err(format_windows_runtime_error("disconnect", &output));
        }
        return Ok(());
    }

    #[cfg(target_os = "macos")]
    {
        let command = format!("wg-quick down '{}'", profile.display());
        let status = Command::new("osascript")
            .arg("-e")
            .arg(format!("do shell script \"{}\" with administrator privileges", command))
            .status()
            .map_err(|err| format!("unable to stop tunnel: {err}"))?;
        if !status.success() {
            return Err(format!("macOS tunnel disconnect failed with status {status}"));
        }
        return Ok(());
    }

    #[allow(unreachable_code)]
    Err("unsupported platform".into())
}

#[cfg(target_os = "windows")]
fn find_windows_wireguard() -> Result<PathBuf, String> {
    let candidates = [
        PathBuf::from(r"C:\Program Files\WireGuard\wireguard.exe"),
        PathBuf::from(r"C:\Program Files (x86)\WireGuard\wireguard.exe"),
    ];

    candidates
        .into_iter()
        .find(|path| path.exists())
        .ok_or_else(|| "required Windows tunnel runtime is not available".to_string())
}

#[cfg(target_os = "windows")]
fn ensure_windows_admin() -> Result<(), String> {
    let status = Command::new("net")
        .arg("session")
        .creation_flags(CREATE_NO_WINDOW)
        .status()
        .map_err(|err| format!("unable to determine Windows privilege level: {err}"))?;

    if status.success() {
        return Ok(());
    }

    Err("Administrator rights are required to activate the VPN tunnel on Windows. Start NexaVPN as Administrator for now.".into())
}

#[cfg(target_os = "windows")]
fn format_windows_runtime_error(action: &str, output: &std::process::Output) -> String {
    let stderr = String::from_utf8_lossy(&output.stderr);
    let stdout = String::from_utf8_lossy(&output.stdout);
    let details = if !stderr.trim().is_empty() {
        stderr.trim()
    } else {
        stdout.trim()
    };

    if details.is_empty() {
        return format!("WireGuard runtime {} failed with status {}", action, output.status);
    }

    format!("WireGuard runtime {} failed: {}", action, details)
}
