use serde::Serialize;
use std::process::{Command, Stdio};

/// Runs a shell command via `sh -c`, capturing stdout/stderr.
/// Returns Ok(stdout) on success (exit code 0) or Err with details on failure.
fn run_command(cmd: &str) -> Result<String, String> {
    let output = Command::new("sh")
        .arg("-c")
        .arg(cmd)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .output()
        .map_err(|e| format!("Failed to execute command: {}", e))?;

    let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
    let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();

    if output.status.success() {
        Ok(stdout)
    } else {
        let code = output.status.code().unwrap_or(-1);
        Err(format!(
            "Command failed (exit code {})\nstdout: {}\nstderr: {}",
            code, stdout, stderr
        ))
    }
}

/// Runs a shell command via `sh -c`, returning (stdout, exit_code).
/// Only errors if the process itself fails to spawn.
fn run_command_with_exit_code(cmd: &str) -> Result<(String, i32), String> {
    let output = Command::new("sh")
        .arg("-c")
        .arg(cmd)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .output()
        .map_err(|e| format!("Failed to execute command: {}", e))?;

    let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
    let code = output.status.code().unwrap_or(-1);

    Ok((stdout, code))
}

// ---------------------------------------------------------------------------
// Distro detection
// ---------------------------------------------------------------------------

#[derive(Serialize, Clone)]
struct DistroInfo {
    /// "fedora", "rhel", "centos", "rocky", "alma", or "unknown"
    id: String,
    /// Human-readable name, e.g. "Fedora Linux 44", "Rocky Linux 9.3"
    name: String,
    /// Major version number as string, e.g. "44", "9"
    version: String,
    /// True for any RHEL-family distro (rhel, centos, rocky, almalinux)
    is_rhel_family: bool,
}

fn detect_distro_inner() -> DistroInfo {
    let os_release = std::fs::read_to_string("/etc/os-release").unwrap_or_default();

    let mut id = String::new();
    let mut name = String::new();
    let mut version_id = String::new();

    for line in os_release.lines() {
        if let Some(val) = line.strip_prefix("ID=") {
            id = val.trim_matches('"').to_lowercase();
        } else if let Some(val) = line.strip_prefix("PRETTY_NAME=") {
            name = val.trim_matches('"').to_string();
        } else if let Some(val) = line.strip_prefix("VERSION_ID=") {
            version_id = val.trim_matches('"').to_string();
        }
    }

    let is_rhel_family = matches!(
        id.as_str(),
        "rhel" | "centos" | "rocky" | "almalinux" | "ol"
    );

    DistroInfo {
        id,
        name,
        version: version_id,
        is_rhel_family,
    }
}

#[tauri::command]
fn detect_distro() -> Result<DistroInfo, String> {
    let info = detect_distro_inner();
    if info.id.is_empty() {
        Err("Could not determine distribution from /etc/os-release".into())
    } else {
        Ok(info)
    }
}

// ---------------------------------------------------------------------------
// Check commands
// ---------------------------------------------------------------------------

#[tauri::command]
fn check_nvidia_gpu() -> Result<String, String> {
    let output = run_command("lspci | grep -iE 'vga|3d'")?;
    if output.to_lowercase().contains("nvidia") {
        Ok(output)
    } else {
        Err(format!("No NVIDIA GPU detected. Found: {}", output))
    }
}

#[tauri::command]
fn check_epel() -> Result<bool, String> {
    let (_, code) = run_command_with_exit_code("rpm -q epel-release")?;
    Ok(code == 0)
}

#[tauri::command]
fn check_rpmfusion_free() -> Result<bool, String> {
    let (_, code) = run_command_with_exit_code("rpm -q rpmfusion-free-release")?;
    Ok(code == 0)
}

#[tauri::command]
fn check_rpmfusion_nonfree() -> Result<bool, String> {
    let (_, code) = run_command_with_exit_code("rpm -q rpmfusion-nonfree-release")?;
    Ok(code == 0)
}

#[tauri::command]
fn check_system_updated() -> Result<bool, String> {
    // The running kernel must match the latest installed kernel so akmod can
    // build modules for it.
    let running = run_command("uname -r")?;

    // Fedora uses kernel-core, RHEL uses kernel — try both
    let latest = run_command(
        "{ rpm -q kernel-core --queryformat '%{VERSION}-%{RELEASE}.%{ARCH}\\n' 2>/dev/null; \
           rpm -q kernel --queryformat '%{VERSION}-%{RELEASE}.%{ARCH}\\n' 2>/dev/null; } \
         | sort -V | tail -1",
    )?;

    Ok(running.trim() == latest.trim())
}

#[tauri::command]
fn check_kernel_devel() -> Result<bool, String> {
    let (_, code) = run_command_with_exit_code("rpm -q kernel-devel-$(uname -r)")?;
    Ok(code == 0)
}

#[tauri::command]
fn check_secure_boot() -> Result<bool, String> {
    let result = run_command_with_exit_code("mokutil --sb-state");
    match result {
        Ok((stdout, _)) => Ok(stdout.to_lowercase().contains("secureboot enabled")),
        Err(_) => Ok(false),
    }
}

#[tauri::command]
fn check_akmod_nvidia() -> Result<bool, String> {
    let (_, code) = run_command_with_exit_code("rpm -q akmod-nvidia")?;
    Ok(code == 0)
}

#[derive(Serialize)]
struct DriverStatus {
    installed: String,
    available: String,
    loaded: String,
    update_available: bool,
}

#[tauri::command]
fn check_driver_status() -> Result<DriverStatus, String> {
    let installed = run_command_with_exit_code(
        "rpm -q akmod-nvidia --queryformat '%{VERSION}-%{RELEASE}'",
    )
    .map(|(out, code)| if code == 0 { out } else { String::new() })
    .unwrap_or_default();

    let available = run_command_with_exit_code(
        "dnf repoquery akmod-nvidia --latest-limit=1 --queryformat '%{VERSION}-%{RELEASE}' 2>/dev/null",
    )
    .map(|(out, code)| if code == 0 { out } else { String::new() })
    .unwrap_or_default();

    let loaded = run_command_with_exit_code("modinfo -F version nvidia")
        .map(|(out, code)| if code == 0 { out } else { String::new() })
        .unwrap_or_default();

    let update_available =
        !installed.is_empty() && !available.is_empty() && installed != available;

    Ok(DriverStatus {
        installed,
        available,
        loaded,
        update_available,
    })
}

// ---------------------------------------------------------------------------
// Install / action commands (all use pkexec for privilege escalation)
// ---------------------------------------------------------------------------

#[tauri::command]
fn install_epel() -> Result<String, String> {
    let distro = detect_distro_inner();
    let ver = &distro.version;
    // Extract major version (e.g. "9.3" -> "9")
    let major = ver.split('.').next().unwrap_or(ver);
    run_command(&format!(
        "pkexec dnf install -y --nogpgcheck \
         https://dl.fedoraproject.org/pub/epel/epel-release-latest-{}.noarch.rpm",
        major
    ))
}

#[tauri::command]
fn install_rpmfusion_free() -> Result<String, String> {
    let distro = detect_distro_inner();
    if distro.is_rhel_family {
        let major = distro.version.split('.').next().unwrap_or(&distro.version).to_string();
        run_command(&format!(
            "pkexec dnf install -y --nogpgcheck \
             https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-{}.noarch.rpm",
            major
        ))
    } else {
        // Fedora
        run_command(
            "pkexec dnf install -y --nogpgcheck \
             https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm",
        )
    }
}

#[tauri::command]
fn install_rpmfusion_nonfree() -> Result<String, String> {
    let distro = detect_distro_inner();
    if distro.is_rhel_family {
        let major = distro.version.split('.').next().unwrap_or(&distro.version).to_string();
        run_command(&format!(
            "pkexec dnf install -y --nogpgcheck \
             https://mirrors.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-{}.noarch.rpm",
            major
        ))
    } else {
        run_command(
            "pkexec dnf install -y --nogpgcheck \
             https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm",
        )
    }
}

#[tauri::command]
fn update_system() -> Result<String, String> {
    run_command("pkexec dnf update -y")
}

#[tauri::command]
fn install_kernel_devel() -> Result<String, String> {
    run_command("pkexec dnf install -y kernel-devel")
}

#[tauri::command]
fn install_akmod_nvidia() -> Result<String, String> {
    run_command("pkexec dnf install -y akmod-nvidia xorg-x11-drv-nvidia-cuda")
}

#[tauri::command]
fn install_secure_boot_tools() -> Result<String, String> {
    run_command("pkexec dnf install -y kmodtool akmods mokutil openssl")
}

#[tauri::command]
fn update_nvidia_driver() -> Result<String, String> {
    run_command("pkexec dnf update -y akmod-nvidia xorg-x11-drv-nvidia-cuda")
}

// ---------------------------------------------------------------------------
// Tauri entry point
// ---------------------------------------------------------------------------

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_opener::init())
        .invoke_handler(tauri::generate_handler![
            detect_distro,
            check_nvidia_gpu,
            check_epel,
            check_rpmfusion_free,
            check_rpmfusion_nonfree,
            check_system_updated,
            check_kernel_devel,
            check_secure_boot,
            check_akmod_nvidia,
            check_driver_status,
            install_epel,
            install_rpmfusion_free,
            install_rpmfusion_nonfree,
            update_system,
            install_kernel_devel,
            install_akmod_nvidia,
            install_secure_boot_tools,
            update_nvidia_driver,
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
