//! Answer file yapıları, yükleme ve state'e uygulama.
//!
//! Job kuyruğu ve Job struct'ları `src/jobs/` modülündedir.

pub mod checker;

// ─────────────────────────────────────────────
// ANSWER FILE: VERİ YAPILARI
// ─────────────────────────────────────────────

/// `[install]` bölümündeki alanlar.
#[derive(Debug, serde::Deserialize)]
pub struct InstallConfig {
    pub disk:             String,
    #[serde(default = "default_timezone")]
    pub timezone:         String,
    #[serde(default = "default_locale")]
    pub locale:           String,
    #[serde(default = "default_keyboard")]
    pub keyboard:         String,
    #[serde(default)]
    pub keyboard_variant: String,
    #[serde(default = "default_source")]
    pub source:           String,
    #[serde(default = "default_mount")]
    pub mount:            String,
}

/// `[user]` bölümündeki alanlar.
#[derive(Debug, serde::Deserialize)]
pub struct UserConfig {
    pub username: String,
    pub password: String,
    #[serde(default = "default_hostname")]
    pub hostname: String,
}

/// Answer file'ın tamamı (`--auto-install <dosya.toml>`).
#[derive(Debug, serde::Deserialize)]
pub struct AnswerFile {
    pub install: InstallConfig,
    pub user:    UserConfig,
}

fn default_timezone()  -> String { "Europe/Istanbul".to_string() }
fn default_locale()    -> String { "tr_TR.UTF-8".to_string() }
fn default_keyboard()  -> String { "tr".to_string() }
fn default_source()    -> String { "/run/livecd/squashfs-root/".to_string() }
fn default_mount()     -> String { "/mnt".to_string() }
fn default_hostname()  -> String { "pisilinux".to_string() }

// ─────────────────────────────────────────────
// ANSWER FILE: YÜKLE ve UYGULA
// ─────────────────────────────────────────────

/// TOML answer dosyasını diskten okur ve ayrıştırır.
pub fn load(path: &std::path::Path) -> Result<AnswerFile, String> {
    let content = std::fs::read_to_string(path)
        .map_err(|e| format!("Answer file okunamadı '{}': {}", path.display(), e))?;
    toml::from_str(&content)
        .map_err(|e| format!("Answer file ayrıştırılamadı: {}", e))
}

/// `AnswerFile` içeriğini `GlobalState`'e uygular.
/// Disk boyutunu `lsblk` ile sorgular ve `partition_plan` oluşturur.
pub fn apply_to_state(af: &AnswerFile, state: &mut crate::installer::GlobalState) {
    use crate::installer::{DiskInfo, PartitionPlan, PartitionTableType};

    // --- Giriş doğrulama ---
    // Disk yolu: yalnızca /dev/ ile başlayan, alfanumerik + /._- içeren değerler
    let disk = &af.install.disk;
    if !disk.starts_with("/dev/")
        || disk.contains("..")
        || !disk.chars().all(|c| c.is_alphanumeric() || "/_.-".contains(c))
    {
        eprintln!("autoinstall: geçersiz disk yolu reddedildi: '{}'", disk);
        return;
    }

    // Timezone doğrulama
    let tz = &af.install.timezone;
    if tz.is_empty()
        || tz.contains("..")
        || !tz.chars().all(|c| c.is_alphanumeric() || "/._-+".contains(c))
    {
        eprintln!("autoinstall: geçersiz timezone reddedildi: '{}'", tz);
        return;
    }

    // Locale doğrulama
    let locale = &af.install.locale;
    if locale.is_empty()
        || locale.contains("..")
        || locale.contains('/')
        || !locale.chars().all(|c| c.is_alphanumeric() || "._-@".contains(c))
    {
        eprintln!("autoinstall: geçersiz locale reddedildi: '{}'", locale);
        return;
    }

    // Temel alanlar
    state.timezone         = af.install.timezone.clone();
    state.keyboard_layout  = af.install.keyboard.clone();
    state.keyboard_variant = af.install.keyboard_variant.clone();
    state.username         = af.user.username.clone();
    state.password         = af.user.password.clone();
    state.password_confirm = af.user.password.clone();
    state.hostname         = af.user.hostname.clone();
    state.selected_disk    = Some(af.install.disk.clone());
    state.erase_disk       = true;
    state.language         = if af.install.locale.starts_with("tr") { "tr" } else { "en" }.to_string();

    // Disk boyutunu lsblk ile sorgula
    let disk_info: Option<DiskInfo> = (|| {
        let out = std::process::Command::new("lsblk")
            .args(["-d", "-b", "-o", "NAME,SIZE,MODEL", "--noheadings", &af.install.disk])
            .output()
            .ok()?;
        if !out.status.success() { return None; }
        let text  = String::from_utf8_lossy(&out.stdout);
        let line  = text.lines().next()?;
        let mut parts = line.split_whitespace();
        let name  = parts.next()?.to_string();
        let bytes: u64 = parts.next()?.parse().ok()?;
        let model = parts.collect::<Vec<_>>().join(" ");
        Some(DiskInfo {
            name:       format!("/dev/{}", name),
            vendor:     String::new(),
            model:      if model.is_empty() { "Unknown".to_string() } else { model },
            drive_type: String::new(),
            size_gb:    bytes / 1_073_741_824,
            size_bytes: bytes,
        })
    })();

    // Partition planını hesapla
    if let Some(info) = disk_info {
        let is_uefi  = std::path::Path::new("/sys/firmware/efi").exists();
        let efi_mb   = if is_uefi { 512u64 } else { 0 };
        let total_mb = info.size_bytes / 1_048_576;

        // Swap: RAM * 1.0, min 512 MB, max 2048 MB, ayrıca diskin %10'unu geçmesin
        let mut sys = sysinfo::System::new();
        sys.refresh_memory();
        let ram_mb     = sys.total_memory() / 1_048_576;
        let disk_10pct = total_mb / 10;
        let swap_mb    = ram_mb.clamp(512, 2048).min(disk_10pct);

        let overhead = efi_mb + swap_mb + 512;
        let root_mb  = total_mb.saturating_sub(overhead);

        if root_mb >= 6144 && info.size_gb >= 10 {
            state.partition_plan = Some(PartitionPlan {
                disk:       info.name.clone(),
                table_type: if is_uefi { PartitionTableType::Gpt } else { PartitionTableType::Mbr },
                efi_mb,
                swap_mb,
                root_mb,
                encrypt_root: false,
                luks_password: String::new(),
            });
        }

        state.available_disks.push(info);
    }
}

