//! ISO ortamı ve donanım uyumluluk kontrolleri.
//!
//! WelcomeStep açılmadan önce çalışır.
//! Her kontrol bir `CheckResult` döner: Pass / Warn / Fail.

use sysinfo::System;
use std::path::Path;
use rust_i18n::t;

// ─────────────────────────────────────────────
// SONUÇ TİPİ
// ─────────────────────────────────────────────

#[derive(Debug, Clone, PartialEq)]
pub enum CheckStatus { Pass, Warn, Fail }

#[derive(Debug, Clone)]
pub struct CheckResult {
    pub name:    String,
    pub status:  CheckStatus,
    pub message: String,
}

impl CheckResult {
    fn pass(name: impl Into<String>, msg: impl Into<String>) -> Self {
        Self { name: name.into(), status: CheckStatus::Pass, message: msg.into() }
    }
    fn warn(name: impl Into<String>, msg: impl Into<String>) -> Self {
        Self { name: name.into(), status: CheckStatus::Warn, message: msg.into() }
    }
    fn fail(name: impl Into<String>, msg: impl Into<String>) -> Self {
        Self { name: name.into(), status: CheckStatus::Fail, message: msg.into() }
    }
}

// ─────────────────────────────────────────────
// TÜM KONTROLLERİ ÇALIŞTIR
// ─────────────────────────────────────────────

pub fn run_all() -> Vec<CheckResult> {
    vec![
        check_root(),
        check_ram(),
        check_disk_space(),
        check_internet(),
        check_boot_mode(),
        check_cpu_arch(),
        check_live_env(),
        check_nvme_support(),
    ]
}

/// Tüm kontroller geçti mi? (Fail yoksa true)
pub fn all_passed(results: &[CheckResult]) -> bool {
    results.iter().all(|r| r.status != CheckStatus::Fail)
}

// ─────────────────────────────────────────────
// BİREYSEL KONTROLLER
// ─────────────────────────────────────────────

/// RAM kontrolü. Minimum 1 GB, önerilen 2 GB.
fn check_ram() -> CheckResult {
    let mut sys = System::new();
    sys.refresh_memory();
    let mb = sys.total_memory() / 1024 / 1024;

    if mb < 1024 {
        CheckResult::fail(
            t!("check_ram"),
            t!("check_ram_fail", mb = mb)
        )
    } else if mb < 2048 {
        CheckResult::warn(
            t!("check_ram"),
            t!("check_ram_warn", mb = mb)
        )
    } else {
        CheckResult::pass(t!("check_ram"), t!("check_ram_pass", mb = mb))
    }
}

/// En az 10 GB boş disk alanı kontrolü.
fn check_disk_space() -> CheckResult {
    // sysinfo yerine doğrudan kernel'in gördüğü blok aygıtı boyutunu alıyoruz.
    let max_gb = get_max_block_device_gb();

    if max_gb < 10 {
        CheckResult::fail(
            t!("check_disk_space"),
            t!("check_disk_fail", size = max_gb),
        )
    } else if max_gb < 20 {
        CheckResult::warn(
            t!("check_disk_space"),
            t!("check_disk_warn", size = max_gb),
        )
    } else {
        CheckResult::pass(
            t!("check_disk_space"), 
            t!("check_disk_pass", size = max_gb)
        )
    }
}

use std::fs;

/// Sistemdeki en büyük fiziksel/sanal blok aygıtının boyutunu GB cinsinden döndürür.
///
/// Önce `lsblk -d` ile dener (yalnızca disk düzeyindeki aygıtlar, bölümler hariç).
/// lsblk yoksa veya başarısız olursa `/sys/block` ile fallback yapar;
/// `/sys/block` yalnızca tam diskleri içerdiğinden bölüm girişleri karışmaz.
fn get_max_block_device_gb() -> u64 {
    let mut max_gb = 0u64;

    // ── 1. Birincil yöntem: lsblk -d (partition.rs ile tutarlı) ──────────────
    let lsblk_out = std::process::Command::new("lsblk")
        .args(["-d", "-o", "NAME,SIZE", "-b", "--noheadings"])
        .output();

    if let Ok(o) = lsblk_out {
        if o.status.success() {
            let text = String::from_utf8_lossy(&o.stdout);
            for line in text.lines() {
                let mut tokens = line.split_whitespace();

                let name = match tokens.next() { Some(n) => n, None => continue };

                // loop, ram, zram, sr (CD-ROM) aygıtlarını atla
                if name.starts_with("loop")
                    || name.starts_with("ram")
                    || name.starts_with("sr")
                    || name.starts_with("zram")
                { continue; }

                let size_str = match tokens.next() { Some(s) => s, None => continue };
                let size_bytes: u64 = size_str.parse().unwrap_or(0);
                if size_bytes == 0 { continue; }

                let size_gb = size_bytes / 1_073_741_824;
                if size_gb > max_gb { max_gb = size_gb; }
            }
            // lsblk başarılıysa ve en az bir disk bulduysa döndür
            if max_gb > 0 { return max_gb; }
        }
    }

    // ── 2. Fallback: /sys/block (yalnızca tam diskler — bölüm yok) ───────────
    // /sys/class/block aksine /sys/block dizini YALNIZCA tam disk girişlerini
    // içerir; sda1, nvme0n1p1 gibi bölüm sembolik bağlantıları burada bulunmaz.
    if let Ok(entries) = fs::read_dir("/sys/block") {
        for entry in entries.flatten() {
            let name = entry.file_name().into_string().unwrap_or_default();

            if name.starts_with("loop")
                || name.starts_with("ram")
                || name.starts_with("zram")
                || name.starts_with("sr")
            { continue; }

            let size_path = entry.path().join("size");
            if let Ok(size_str) = fs::read_to_string(&size_path) {
                // sysfs 'size' dosyası: 512 byte'lık sektör sayısı
                if let Ok(sectors) = size_str.trim().parse::<u64>() {
                    let size_gb = (sectors * 512) / 1_073_741_824;
                    if size_gb > max_gb { max_gb = size_gb; }
                }
            }
        }
    }

    max_gb
}

/// İnternet bağlantısı kontrolü (isteğe bağlı).
fn check_internet() -> CheckResult {
    // DNS çözümlemesi ile basit kontrol
    let connected = std::net::TcpStream::connect_timeout(
        &"8.8.8.8:53".parse().unwrap(),
        std::time::Duration::from_secs(3),
    ).is_ok();

    if connected {
        CheckResult::pass(t!("check_internet"), t!("check_net_pass"))
    } else {
        CheckResult::warn(
            t!("check_internet"),
            t!("check_net_warn"),
        )
    }
}

/// UEFI mi BIOS mu?
fn check_boot_mode() -> CheckResult {
    if Path::new("/sys/firmware/efi").exists() {
        CheckResult::pass(t!("check_boot_mode"), t!("check_uefi_pass"))
    } else {
        CheckResult::pass(t!("check_boot_mode"), t!("check_bios_pass"))
    }
}

/// CPU mimarisi kontrolü (yalnızca x86_64 destekleniyor).
fn check_cpu_arch() -> CheckResult {
    let arch = std::env::consts::ARCH;
    if arch == "x86_64" {
        // CPU özelliklerini de kontrol et
        let cpuinfo = std::fs::read_to_string("/proc/cpuinfo").unwrap_or_default();
        let has_sse2 = cpuinfo.contains("sse2");
        if has_sse2 {
            CheckResult::pass(t!("check_cpu"), t!("check_cpu_pass"))
        } else {
            CheckResult::warn(t!("check_cpu"), t!("check_cpu_warn"))
        }
    } else {
        CheckResult::fail(
            t!("check_cpu"),
            t!("check_cpu_fail", arch = arch)
        )
    }
}

/// Canlı sistem ortamı kontrolü — PyQt yali'deki gibi:
/// /bootmnt varsa kaynak orası (/bootmnt/pisi/pisi.sqfs),
/// yoksa /mnt/cdrom/pisi/pisi.sqfs kontrol edilir.
fn check_live_env() -> CheckResult {
    let sqfs = if Path::new("/bootmnt").exists() {
        "/bootmnt/pisi/pisi.sqfs"
    } else {
        "/mnt/cdrom/pisi/pisi.sqfs"
    };
    if Path::new(sqfs).exists() {
        CheckResult::pass(t!("check_live_sys"), t!("check_live_pass"))
    } else {
        CheckResult::fail(t!("check_live_sys"), t!("check_live_fail"))
    }
}

/// Kök kullanıcı (root) yetkisi kontrolü.
/// mount, swapon, parted gibi komutlar root gerektirdiğinden
/// installer'ın root olarak çalıştırılması zorunludur.
fn check_root() -> CheckResult {
    let is_root = std::process::Command::new("id")
        .arg("-u")
        .output()
        .ok()
        .and_then(|o| {
            if o.status.success() {
                Some(String::from_utf8_lossy(&o.stdout).trim() == "0")
            } else {
                None
            }
        })
        .unwrap_or(false);
    if is_root {
        CheckResult::pass(t!("check_root"), t!("check_root_pass"))
    } else {
        CheckResult::fail(t!("check_root"), t!("check_root_fail"))
    }
}

/// NVMe ve diğer modern disk arayüzlerinin çekirdek desteğini kontrol eder.
fn check_nvme_support() -> CheckResult {
    // /dev/nvme* cihazları var mı?
    let has_nvme_dev = std::fs::read_dir("/dev")
        .map(|dir| dir.flatten().any(|e| e.file_name().to_string_lossy().starts_with("nvme")))
        .unwrap_or(false);

    // Çekirdek modülü yüklü mü?
    let modules = std::fs::read_to_string("/proc/modules").unwrap_or_default();
    let nvme_loaded = modules.contains("nvme ");

    if has_nvme_dev {
        CheckResult::pass(t!("check_nvme"), t!("check_nvme_pass1"))
    } else if nvme_loaded {
        CheckResult::pass(t!("check_nvme"), t!("check_nvme_pass2"))
    } else {
        // NVMe yoksa bu bir sorun değil; SATA/virtio kullanılıyor olabilir
        CheckResult::pass(t!("check_nvme"), t!("check_nvme_pass3"))
    }
}

