use serde::{Deserialize, Serialize};
use std::{
    env,
    io::{BufRead, BufReader, Write},
    net::TcpStream,
    path::{Path, PathBuf},
    process::{Command, ExitCode, Output},
};

#[cfg(target_os = "windows")]
use std::{
    ffi::OsString,
    net::TcpListener,
    sync::mpsc,
    time::Duration,
};

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

#[cfg(target_os = "windows")]
use windows_service::{
    define_windows_service,
    service::{
        ServiceAccess, ServiceControl, ServiceControlAccept, ServiceErrorControl, ServiceExitCode,
        ServiceInfo, ServiceStartType, ServiceState, ServiceStatus, ServiceType,
    },
    service_control_handler::{self, ServiceControlHandlerResult},
    service_dispatcher,
    service_manager::{ServiceManager, ServiceManagerAccess},
};

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

#[cfg(target_os = "windows")]
const SERVICE_NAME: &str = "NexaVPNTunnelService";

#[cfg(target_os = "windows")]
const SERVICE_DISPLAY_NAME: &str = "NexaVPN Tunnel Service";

#[cfg(target_os = "windows")]
const IPC_BIND_ADDR: &str = "127.0.0.1:53189";

#[derive(Debug, Serialize, Deserialize)]
struct TunnelRequest {
    action: String,
    profile: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct TunnelResponse {
    ok: bool,
    error: Option<String>,
    active: Option<bool>,
    rx_bytes: Option<u64>,
    tx_bytes: Option<u64>,
}

#[derive(Debug, Serialize, Deserialize)]
struct TunnelMetrics {
    active: bool,
    rx_bytes: u64,
    tx_bytes: u64,
}

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())?;

    #[cfg(target_os = "windows")]
    {
        return match command.as_str() {
            "service" => run_windows_service_dispatcher(),
            "install-service" => install_windows_service(),
            "install-service-direct" => install_windows_service_direct(),
            "uninstall-service" => uninstall_windows_service(),
            "status" => {
                let profile = parse_profile_arg(args)?;
                let active = windows_client_status(&profile)?;
                println!("{}", if active { "active" } else { "inactive" });
                Ok(())
            }
            "metrics" => {
                let profile = parse_profile_arg(args)?;
                println!(
                    "{}",
                    serde_json::to_string(&windows_client_metrics(&profile)?)
                        .map_err(|err| format!("Unable to encode tunnel metrics: {err}"))?
                );
                Ok(())
            }
            "connect" | "disconnect" => {
                let profile = parse_profile_arg(args)?;
                windows_client_request(command.as_str(), &profile)
            }
            _ => Err("unsupported command".into()),
        };
    }

    #[cfg(not(target_os = "windows"))]
    {
        let profile = parse_profile_arg(args)?;
        match command.as_str() {
            "status" => {
                let active = tunnel_is_active(&profile)?;
                println!("{}", if active { "active" } else { "inactive" });
                Ok(())
            }
            "metrics" => {
                println!(
                    "{}",
                    serde_json::to_string(&tunnel_metrics(&profile)?)
                        .map_err(|err| format!("Unable to encode tunnel metrics: {err}"))?
                );
                Ok(())
            }
            "connect" => connect_direct(&profile),
            "disconnect" => disconnect_direct(&profile),
            _ => Err("unsupported command".into()),
        }
    }
}

fn parse_profile_arg(mut args: impl Iterator<Item = String>) -> Result<PathBuf, String> {
    let flag = args.next().ok_or_else(|| "missing --profile flag".to_string())?;
    if flag != "--profile" {
        return Err("expected --profile flag".into());
    }
    Ok(PathBuf::from(
        args.next().ok_or_else(|| "missing profile path".to_string())?,
    ))
}

#[cfg(target_os = "windows")]
fn run_windows_service_dispatcher() -> Result<(), String> {
    service_dispatcher::start(SERVICE_NAME, ffi_service_main)
        .map_err(|err| format!("Unable to start NexaVPN Windows service dispatcher: {err}"))
}

#[cfg(target_os = "windows")]
define_windows_service!(ffi_service_main, service_main);

#[cfg(target_os = "windows")]
fn service_main(_arguments: Vec<OsString>) {
    if let Err(err) = run_service_loop() {
        eprintln!("{err}");
    }
}

#[cfg(target_os = "windows")]
fn run_service_loop() -> Result<(), String> {
    let (shutdown_tx, shutdown_rx) = mpsc::channel();
    let status_handle = service_control_handler::register(SERVICE_NAME, move |control_event| match control_event {
        ServiceControl::Stop => {
            let _ = shutdown_tx.send(());
            ServiceControlHandlerResult::NoError
        }
        ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
        _ => ServiceControlHandlerResult::NotImplemented,
    })
    .map_err(|err| format!("Unable to register NexaVPN service handler: {err}"))?;

    status_handle
        .set_service_status(ServiceStatus {
            service_type: ServiceType::OWN_PROCESS,
            current_state: ServiceState::Running,
            controls_accepted: ServiceControlAccept::STOP,
            exit_code: ServiceExitCode::Win32(0),
            checkpoint: 0,
            wait_hint: Duration::default(),
            process_id: None,
        })
        .map_err(|err| format!("Unable to publish service status: {err}"))?;

    let listener = TcpListener::bind(IPC_BIND_ADDR)
        .map_err(|err| format!("Unable to bind NexaVPN service IPC socket: {err}"))?;
    listener
        .set_nonblocking(true)
        .map_err(|err| format!("Unable to configure IPC socket: {err}"))?;

    loop {
        if shutdown_rx.try_recv().is_ok() {
            break;
        }

        match listener.accept() {
            Ok((stream, _)) => {
                let _ = handle_service_client(stream);
            }
            Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
                std::thread::sleep(Duration::from_millis(200));
            }
            Err(err) => {
                return Err(format!("IPC accept failed: {err}"));
            }
        }
    }

    status_handle
        .set_service_status(ServiceStatus {
            service_type: ServiceType::OWN_PROCESS,
            current_state: ServiceState::Stopped,
            controls_accepted: ServiceControlAccept::empty(),
            exit_code: ServiceExitCode::Win32(0),
            checkpoint: 0,
            wait_hint: Duration::default(),
            process_id: None,
        })
        .map_err(|err| format!("Unable to publish stopped service state: {err}"))?;

    Ok(())
}

#[cfg(target_os = "windows")]
fn handle_service_client(mut stream: TcpStream) -> Result<(), String> {
    let mut reader = BufReader::new(
        stream
            .try_clone()
            .map_err(|err| format!("Unable to clone IPC socket: {err}"))?,
    );
    let mut line = String::new();
    reader
        .read_line(&mut line)
        .map_err(|err| format!("Unable to read IPC request: {err}"))?;

    let request = serde_json::from_str::<TunnelRequest>(&line)
        .map_err(|err| format!("Unable to decode IPC request: {err}"))?;
    let response = match request.action.as_str() {
        "connect" => match connect_direct(Path::new(&request.profile)) {
            Ok(()) => TunnelResponse { ok: true, error: None, active: None, rx_bytes: None, tx_bytes: None },
            Err(err) => TunnelResponse {
                ok: false,
                error: Some(err),
                active: None,
                rx_bytes: None,
                tx_bytes: None,
            },
        },
        "disconnect" => match disconnect_direct(Path::new(&request.profile)) {
            Ok(()) => TunnelResponse { ok: true, error: None, active: None, rx_bytes: None, tx_bytes: None },
            Err(err) => TunnelResponse {
                ok: false,
                error: Some(err),
                active: None,
                rx_bytes: None,
                tx_bytes: None,
            },
        },
        "status" => match tunnel_is_active(Path::new(&request.profile)) {
            Ok(active) => TunnelResponse {
                ok: true,
                error: None,
                active: Some(active),
                rx_bytes: None,
                tx_bytes: None,
            },
            Err(err) => TunnelResponse {
                ok: false,
                error: Some(err),
                active: None,
                rx_bytes: None,
                tx_bytes: None,
            },
        },
        "metrics" => match tunnel_metrics(Path::new(&request.profile)) {
            Ok(metrics) => TunnelResponse {
                ok: true,
                error: None,
                active: Some(metrics.active),
                rx_bytes: Some(metrics.rx_bytes),
                tx_bytes: Some(metrics.tx_bytes),
            },
            Err(err) => TunnelResponse {
                ok: false,
                error: Some(err),
                active: None,
                rx_bytes: None,
                tx_bytes: None,
            },
        },
        _ => TunnelResponse {
            ok: false,
            error: Some("unsupported tunnel action".into()),
            active: None,
            rx_bytes: None,
            tx_bytes: None,
        },
    };

    let payload = serde_json::to_vec(&response).map_err(|err| format!("Unable to encode IPC response: {err}"))?;
    stream
        .write_all(&payload)
        .and_then(|_| stream.write_all(b"\n"))
        .map_err(|err| format!("Unable to write IPC response: {err}"))?;
    Ok(())
}

#[cfg(target_os = "windows")]
fn install_windows_service() -> Result<(), String> {
    if is_windows_admin()? {
        return install_windows_service_direct();
    }

    let helper_path = env::current_exe().map_err(|err| format!("Unable to resolve helper path: {err}"))?;
    let status = Command::new("powershell")
        .arg("-NoProfile")
        .arg("-Command")
        .arg(format!(
            "Start-Process -FilePath '{}' -ArgumentList 'install-service-direct' -Verb RunAs -Wait",
            helper_path.display()
        ))
        .creation_flags(CREATE_NO_WINDOW)
        .status()
        .map_err(|err| format!("Unable to request elevated tunnel service install: {err}"))?;

    if !status.success() {
        return Err("NexaVPN tunnel service installation was cancelled or failed.".into());
    }

    std::thread::sleep(Duration::from_secs(2));
    Ok(())
}

#[cfg(target_os = "windows")]
fn install_windows_service_direct() -> Result<(), String> {
    let manager = ServiceManager::local_computer(
        None::<&str>,
        ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE,
    )
    .map_err(|err| format!("Unable to connect to Service Control Manager: {err}"))?;

    let helper_path = env::current_exe().map_err(|err| format!("Unable to resolve helper path: {err}"))?;
    let service_info = ServiceInfo {
        name: OsString::from(SERVICE_NAME),
        display_name: OsString::from(SERVICE_DISPLAY_NAME),
        service_type: ServiceType::OWN_PROCESS,
        start_type: ServiceStartType::AutoStart,
        error_control: ServiceErrorControl::Normal,
        executable_path: helper_path,
        launch_arguments: vec![OsString::from("service")],
        dependencies: vec![],
        account_name: None,
        account_password: None,
    };

    let service = manager
        .create_service(&service_info, ServiceAccess::CHANGE_CONFIG | ServiceAccess::START | ServiceAccess::QUERY_STATUS)
        .or_else(|_| {
            manager.open_service(
                SERVICE_NAME,
                ServiceAccess::CHANGE_CONFIG | ServiceAccess::START | ServiceAccess::QUERY_STATUS,
            )
        })
        .map_err(|err| format!("Unable to install NexaVPN tunnel service: {err}"))?;

    let empty_args: [&str; 0] = [];
    let _ = service.start(&empty_args);
    Ok(())
}

#[cfg(target_os = "windows")]
fn uninstall_windows_service() -> Result<(), String> {
    let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT)
        .map_err(|err| format!("Unable to connect to Service Control Manager: {err}"))?;
    let service = manager
        .open_service(SERVICE_NAME, ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE)
        .map_err(|err| format!("Unable to open NexaVPN tunnel service: {err}"))?;

    let _ = service.stop();
    service
        .delete()
        .map_err(|err| format!("Unable to delete NexaVPN tunnel service: {err}"))
}

#[cfg(target_os = "windows")]
fn windows_client_request(action: &str, profile: &Path) -> Result<(), String> {
    let mut stream = connect_to_service()?;

    let payload = serde_json::to_string(&TunnelRequest {
        action: action.to_string(),
        profile: profile.display().to_string(),
    })
    .map_err(|err| format!("Unable to encode tunnel request: {err}"))?;

    stream
        .write_all(payload.as_bytes())
        .and_then(|_| stream.write_all(b"\n"))
        .map_err(|err| format!("Unable to send tunnel request: {err}"))?;

    let mut reader = BufReader::new(stream);
    let mut line = String::new();
    reader
        .read_line(&mut line)
        .map_err(|err| format!("Unable to read tunnel service response: {err}"))?;

    let response = serde_json::from_str::<TunnelResponse>(&line)
        .map_err(|err| format!("Unable to decode tunnel service response: {err}"))?;
    if response.ok {
        return Ok(());
    }

    Err(response
        .error
        .unwrap_or_else(|| "NexaVPN tunnel service reported an unknown error".into()))
}

#[cfg(target_os = "windows")]
fn windows_client_status(profile: &Path) -> Result<bool, String> {
    Ok(windows_client_metrics(profile)?.active)
}

#[cfg(target_os = "windows")]
fn windows_client_metrics(profile: &Path) -> Result<TunnelMetrics, String> {
    let mut stream = connect_to_service()?;

    let payload = serde_json::to_string(&TunnelRequest {
        action: "metrics".to_string(),
        profile: profile.display().to_string(),
    })
    .map_err(|err| format!("Unable to encode tunnel metrics request: {err}"))?;

    stream
        .write_all(payload.as_bytes())
        .and_then(|_| stream.write_all(b"\n"))
        .map_err(|err| format!("Unable to send tunnel metrics request: {err}"))?;

    let mut reader = BufReader::new(stream);
    let mut line = String::new();
    reader
        .read_line(&mut line)
        .map_err(|err| format!("Unable to read tunnel metrics response: {err}"))?;

    let response = serde_json::from_str::<TunnelResponse>(&line)
        .map_err(|err| format!("Unable to decode tunnel metrics response: {err}"))?;
    if !response.ok {
        return Err(response
            .error
            .unwrap_or_else(|| "NexaVPN tunnel service metrics check failed".into()));
    }

    Ok(TunnelMetrics {
        active: response.active.unwrap_or(false),
        rx_bytes: response.rx_bytes.unwrap_or(0),
        tx_bytes: response.tx_bytes.unwrap_or(0),
    })
}

#[cfg(target_os = "windows")]
fn connect_to_service() -> Result<TcpStream, String> {
    if let Ok(stream) = TcpStream::connect(IPC_BIND_ADDR) {
        return Ok(stream);
    }

    let _ = start_windows_service();
    std::thread::sleep(Duration::from_millis(700));
    if let Ok(stream) = TcpStream::connect(IPC_BIND_ADDR) {
        return Ok(stream);
    }

    install_windows_service()?;
    std::thread::sleep(Duration::from_secs(2));
    if let Ok(stream) = TcpStream::connect(IPC_BIND_ADDR) {
        return Ok(stream);
    }

    Err("NexaVPN background service is not available. Reinstall NexaVPN or install the tunnel service once as administrator.".into())
}

#[cfg(target_os = "windows")]
fn start_windows_service() -> Result<(), String> {
    let status = Command::new("sc.exe")
        .arg("start")
        .arg(SERVICE_NAME)
        .creation_flags(CREATE_NO_WINDOW)
        .status()
        .map_err(|err| format!("Unable to start NexaVPN tunnel service: {err}"))?;

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

    Err("Unable to start NexaVPN tunnel service.".into())
}

#[cfg(target_os = "windows")]
fn is_windows_admin() -> Result<bool, 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}"))?;
    Ok(status.success())
}

fn connect_direct(profile: &Path) -> Result<(), String> {
    #[cfg(target_os = "windows")]
    {
        if tunnel_service_is_active(profile)? {
            return Ok(());
        }

        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 is_already_running_error(&output) {
            return Ok(());
        }
        if !output.status.success() {
            return Err(format_windows_runtime_error("connect", &output));
        }
        wait_for_windows_tunnel_running(profile)?;
        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_direct(profile: &Path) -> Result<(), String> {
    #[cfg(target_os = "windows")]
    {
        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())
}

fn tunnel_is_active(profile: &Path) -> Result<bool, String> {
    Ok(tunnel_metrics(profile)?.active)
}

fn tunnel_metrics(profile: &Path) -> Result<TunnelMetrics, String> {
    let active = tunnel_service_is_active(profile)?;
    if !active {
        return Ok(TunnelMetrics {
            active: false,
            rx_bytes: 0,
            tx_bytes: 0,
        });
    }

    let (rx_bytes, tx_bytes) = read_transfer_totals(profile).unwrap_or((0, 0));
    Ok(TunnelMetrics {
        active: true,
        rx_bytes,
        tx_bytes,
    })
}

fn tunnel_service_is_active(profile: &Path) -> Result<bool, String> {
    #[cfg(target_os = "windows")]
    {
        let tunnel_name = tunnel_name(profile)?;
        let service_name = format!("WireGuardTunnel${}", tunnel_name);
        let output = Command::new("sc.exe")
            .arg("query")
            .arg(&service_name)
            .creation_flags(CREATE_NO_WINDOW)
            .output()
            .map_err(|err| format!("Unable to query tunnel status: {err}"))?;

        if !output.status.success() {
            return Ok(false);
        }

        let stdout = String::from_utf8_lossy(&output.stdout);
        return Ok(stdout.contains("RUNNING"));
    }

    #[cfg(target_os = "macos")]
    {
        let tunnel_name = tunnel_name(profile)?;
        let status = Command::new("wg")
            .arg("show")
            .arg(tunnel_name)
            .status()
            .map_err(|err| format!("Unable to query tunnel status: {err}"))?;
        return Ok(status.success());
    }

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

#[cfg(target_os = "windows")]
fn wait_for_windows_tunnel_running(profile: &Path) -> Result<(), String> {
    for _ in 0..12 {
        if tunnel_service_is_active(profile)? {
            return Ok(());
        }
        std::thread::sleep(Duration::from_millis(500));
    }

    Err(describe_windows_tunnel_state(profile))
}

#[cfg(target_os = "windows")]
fn describe_windows_tunnel_state(profile: &Path) -> String {
    let tunnel_name = match tunnel_name(profile) {
        Ok(value) => value,
        Err(err) => return err,
    };
    let service_name = format!("WireGuardTunnel${}", tunnel_name);
    let output = Command::new("sc.exe")
        .arg("query")
        .arg(&service_name)
        .creation_flags(CREATE_NO_WINDOW)
        .output();

    match output {
        Ok(output) if output.status.success() => {
            let stdout = String::from_utf8_lossy(&output.stdout);
            let summary = stdout
                .lines()
                .find(|line| line.contains("STATE"))
                .map(str::trim)
                .unwrap_or("state unavailable");
            format!("WireGuard tunnel service did not reach RUNNING state yet ({summary}).")
        }
        Ok(output) => {
            let stdout = String::from_utf8_lossy(&output.stdout);
            let stderr = String::from_utf8_lossy(&output.stderr);
            let details = if !stderr.trim().is_empty() {
                stderr.trim()
            } else {
                stdout.trim()
            };
            if details.is_empty() {
                "WireGuard tunnel service was created but could not be queried.".into()
            } else {
                format!("WireGuard tunnel service could not be queried: {details}")
            }
        }
        Err(err) => format!("WireGuard tunnel service did not reach RUNNING state: {err}"),
    }
}

fn tunnel_name(profile: &Path) -> Result<&str, String> {
    profile
        .file_stem()
        .and_then(|value| value.to_str())
        .ok_or_else(|| "invalid profile filename".to_string())
}

fn read_transfer_totals(profile: &Path) -> Result<(u64, u64), String> {
    let tunnel_name = tunnel_name(profile)?;
    let mut command = Command::new(find_wg_cli()?);
    command.arg("show").arg(tunnel_name).arg("dump");
    #[cfg(target_os = "windows")]
    command.creation_flags(CREATE_NO_WINDOW);

    let output = command
        .output()
        .map_err(|err| format!("Unable to query tunnel transfer counters: {err}"))?;

    if !output.status.success() {
        return read_transfer_totals_from_show(tunnel_name);
    }

    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut rx_bytes = 0_u64;
    let mut tx_bytes = 0_u64;

    for line in stdout.lines().skip(1) {
        let fields: Vec<&str> = line.split('\t').collect();
        if fields.len() < 7 {
            continue;
        }

        rx_bytes = rx_bytes.saturating_add(fields[5].parse::<u64>().unwrap_or(0));
        tx_bytes = tx_bytes.saturating_add(fields[6].parse::<u64>().unwrap_or(0));
    }

    Ok((rx_bytes, tx_bytes))
}

fn read_transfer_totals_from_show(tunnel_name: &str) -> Result<(u64, u64), String> {
    let mut command = Command::new(find_wg_cli()?);
    command.arg("show").arg(tunnel_name);
    #[cfg(target_os = "windows")]
    command.creation_flags(CREATE_NO_WINDOW);

    let output = command
        .output()
        .map_err(|err| format!("Unable to query tunnel transfer text: {err}"))?;

    if !output.status.success() {
        return Err("Unable to read WireGuard transfer counters.".into());
    }

    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut rx_bytes = 0_u64;
    let mut tx_bytes = 0_u64;

    for line in stdout.lines() {
        let trimmed = line.trim();
        if let Some(rest) = trimmed.strip_prefix("transfer:") {
            let parts: Vec<&str> = rest.split(',').collect();
            if let Some(received) = parts.first() {
                rx_bytes = rx_bytes.saturating_add(parse_human_wireguard_bytes(received));
            }
            if let Some(sent) = parts.get(1) {
                tx_bytes = tx_bytes.saturating_add(parse_human_wireguard_bytes(sent));
            }
        }
    }

    Ok((rx_bytes, tx_bytes))
}

fn parse_human_wireguard_bytes(value: &str) -> u64 {
    let cleaned = value
        .replace("received", "")
        .replace("sent", "")
        .trim()
        .to_string();
    let mut parts = cleaned.split_whitespace();
    let amount = parts
        .next()
        .map(|raw| raw.replace(',', "."))
        .and_then(|raw| raw.parse::<f64>().ok())
        .unwrap_or(0.0);
    let unit = parts.next().unwrap_or("B");

    let multiplier = match unit {
        "B" => 1.0,
        "KiB" => 1024.0,
        "MiB" => 1024.0 * 1024.0,
        "GiB" => 1024.0 * 1024.0 * 1024.0,
        "TiB" => 1024.0 * 1024.0 * 1024.0 * 1024.0,
        _ => 1.0,
    };

    (amount * multiplier) as u64
}

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

    if let Some(path) = candidates.into_iter().find(|path| path.exists()) {
        return Ok(path);
    }

    Ok(PathBuf::from("wg.exe"))
}

#[cfg(target_os = "macos")]
fn find_wg_cli() -> Result<PathBuf, String> {
    Ok(PathBuf::from("wg"))
}

#[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 format_windows_runtime_error(action: &str, output: &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)
}

#[cfg(target_os = "windows")]
fn is_already_running_error(output: &Output) -> bool {
    let stderr = String::from_utf8_lossy(&output.stderr);
    let stdout = String::from_utf8_lossy(&output.stdout);
    let combined = format!("{}\n{}", stdout, stderr).to_ascii_lowercase();
    combined.contains("already installed and running")
}
