use std::sync::atomic::Ordering;
use super::{blkid_uuid, DEMO_MODE, get_fs_type, is_mount_point, part_path, run_cmd, run_chroot, Job, UiSender};

/// initramfs yeniler (GRUB öncesi zorunlu).
/// PisiLinux `dracut` kullanıyorsa `dracut -f --regenerate-all`,
/// mkinitcpio kullanıyorsa `mkinitcpio -P`.
pub struct GenerateInitramfsJob {
    pub mount: String,
    pub encrypt_root: bool,
    pub lvm_active: bool,
}

#[async_trait::async_trait]
impl Job for GenerateInitramfsJob {
    fn name(&self) -> &str { "job_initramfs" }

    async fn run(&self, ui: &UiSender) -> Result<(), String> {
        // LVM için initramfs'e lvm modülü/hook'u ekleyen konfigürasyon
        if self.lvm_active && !self.encrypt_root {
            ui.log("LVM algılandı: initramfs LVM ayarlanıyor…");

            // dracut: add lvm module
            let dracut_conf = format!("{}/etc/dracut.conf.d/99-lvm.conf", self.mount);
            let _ = tokio::fs::create_dir_all(format!("{}/etc/dracut.conf.d", self.mount)).await;
            let _ = tokio::fs::write(&dracut_conf, "add_dracutmodules+=\" lvm \"\n").await;

            // mkinitcpio: add lvm2 hook
            // Hem HOOKS="..." hem HOOKS=(... ) sözdizimi desteklenir.
            let mkinitcpio_conf = format!("{}/etc/mkinitcpio.conf", self.mount);
            if tokio::fs::metadata(&mkinitcpio_conf).await.is_ok() {
                if let Ok(content) = tokio::fs::read_to_string(&mkinitcpio_conf).await {
                    if content.contains("HOOKS=") && !content.contains("lvm2") {
                        let new_content = content
                            .replace("HOOKS=\"", "HOOKS=\"lvm2 ")
                            .replace("HOOKS=(", "HOOKS=(lvm2 ");
                        let _ = tokio::fs::write(&mkinitcpio_conf, new_content).await;
                    }
                }
            }
        }

        // LUKS için initramfs'e cryptsetup ekleyen konfigürasyon
        if self.encrypt_root {
            ui.log("LUKS algılandı: initramfs cryptsetup ayarlanıyor…");

            // dracut: add crypt module
            let dracut_conf = format!("{}/etc/dracut.conf.d/99-luks.conf", self.mount);
            tokio::fs::create_dir_all(
                format!("{}/etc/dracut.conf.d", self.mount)
            ).await.map_err(|e| format!("dracut.conf.d oluşturulamadı: {}", e))?;
            tokio::fs::write(&dracut_conf, "add_dracutmodules+=\" crypt lvm \"\n")
                .await.map_err(|e| format!("dracut luks conf yazılamadı: {}", e))?;

            // mkinitcpio: add encrypt hook
            // Hem HOOKS="..." hem HOOKS=(... ) sözdizimi desteklenir.
            let mkinitcpio_conf = format!("{}/etc/mkinitcpio.conf", self.mount);
            if tokio::fs::metadata(&mkinitcpio_conf).await.is_ok() {
                let content = tokio::fs::read_to_string(&mkinitcpio_conf)
                    .await.unwrap_or_default();
                if content.contains("HOOKS=") && !content.contains("encrypt") {
                    let new_content = content
                        .replace("HOOKS=\"", "HOOKS=\"encrypt ")
                        .replace("HOOKS=(", "HOOKS=(encrypt ");
                    let _ = tokio::fs::write(&mkinitcpio_conf, new_content).await;
                }
            }
        }

        // PisiLinux /sbin/mkinitramfs, dracut, mkinitcpio sırasıyla dene
        let cmds: &[&[&str]] = &[
            &["mkinitramfs", "-f"],
            &["dracut", "-f", "--regenerate-all"],
            &["mkinitcpio", "-P"],
        ];
        for cmd in cmds {
            if run_chroot(&self.mount, cmd).await.is_ok() {
                ui.log(format!("initramfs oluşturuldu ({})", cmd[0]));
                return Ok(());
            }
        }
        Err("initramfs oluşturulamadı: mkinitramfs, dracut, mkinitcpio denendi".to_string())
    }
}



/// GRUB bootloader kurulumu (UEFI veya BIOS).
pub struct InstallGrubJob {
    pub disk: String,
    pub mount: String,
    pub is_uefi: bool,
    pub boot_device: String,
    pub timeout: u32,
    pub password: String,
    pub kernel_options: String,
    pub encrypt_root: bool,
}

#[async_trait::async_trait]
impl Job for InstallGrubJob {
    fn name(&self) -> &str { "job_bootloader" }

    async fn run(&self, ui: &UiSender) -> Result<(), String> {
        // Ana dizinleri oluştur (demo modunda rsync çalışmaz)
        let etc_default = format!("{}/etc/default", self.mount);
        let _ = tokio::fs::create_dir_all(&etc_default).await;
        let grub_d = format!("{}/etc/grub.d", self.mount);
        let _ = tokio::fs::create_dir_all(&grub_d).await;

        // GRUB şifresi (varsa) — grub-mkpasswd-pbkdf2 ile hash üretilir,
        // düz metin asla diske yazılmaz.
        if !self.password.is_empty() {
            ui.log("GRUB şifresi PBKDF2 ile hashleniyor…");

            // grub-mkpasswd-pbkdf2 şifreyi stdin'den iki kez okur (şifre + doğrulama)
            use tokio::io::AsyncWriteExt;
            let pw_input = format!("{}\n{}\n", self.password, self.password);
            let mut child = tokio::process::Command::new("grub-mkpasswd-pbkdf2")
                .stdin(std::process::Stdio::piped())
                .stdout(std::process::Stdio::piped())
                .stderr(std::process::Stdio::null())
                .spawn()
                .map_err(|e| format!("grub-mkpasswd-pbkdf2 başlatılamadı: {}", e))?;

            if let Some(mut stdin) = child.stdin.take() {
                stdin.write_all(pw_input.as_bytes()).await
                    .map_err(|e| format!("grub-mkpasswd-pbkdf2 stdin hatası: {}", e))?;
            }

            let output = child.wait_with_output().await
                .map_err(|e| format!("grub-mkpasswd-pbkdf2 bekleme hatası: {}", e))?;

            // Çıktıdan "grub.pbkdf2.sha512...." token'ını ayrıştır
            let stdout = String::from_utf8_lossy(&output.stdout);
            let hash = stdout
                .lines()
                .find_map(|l| {
                    let pos = l.find("grub.pbkdf2")?;
                    Some(l[pos..].trim().to_string())
                })
                .ok_or("grub-mkpasswd-pbkdf2 çıktısında hash bulunamadı")?;

            let custom_grub = format!(
                "#!/bin/sh\nset superusers=\"root\"\npassword_pbkdf2 root {}\n",
                hash
            );
            let pw_path = format!("{}/etc/grub.d/40_custom", self.mount);
            tokio::fs::write(&pw_path, custom_grub)
                .await
                .map_err(|e| format!("GRUB şifre dosyası yazılamadı: {}", e))?;
            run_cmd("chmod", &["+x", &pw_path]).await?;
            ui.log("GRUB şifresi PBKDF2 hash olarak yazıldı.");
        }

        // GRUB timeout ayarı
        ui.log(format!("GRUB timeout ayarlanıyor: {}s", self.timeout));
        let default_path = format!("{}/etc/default/grub", self.mount);
        let mut grub_default = tokio::fs::read_to_string(&default_path)
            .await
            .unwrap_or_default();
        // timeout satırını bul/değiştir
        let timeout_line = format!("GRUB_TIMEOUT={}", self.timeout);
        let found_timeout = grub_default.lines()
            .any(|l| l.trim_start().starts_with("GRUB_TIMEOUT="));
        if found_timeout {
            let mut lines: Vec<String> = grub_default.lines()
                .map(|l| {
                    if l.trim_start().starts_with("GRUB_TIMEOUT=") {
                        timeout_line.clone()
                    } else {
                        l.to_string()
                    }
                })
                .collect();
            lines.push(String::new());
            grub_default = lines.join("\n");
        } else {
            grub_default.push_str(&format!("\n{}\n", timeout_line));
        }
        // kernel cmdline
        let mut cmdline = self.kernel_options.clone();
        if self.encrypt_root {
            // Kök bölüm UUID'sini blkid'den al (LUKS başlık UUID'si)
            let (root_num, _swap_num): (u32, u32) = if self.is_uefi { (3, 2) } else { (2, 1) };
            let root_part = part_path(&self.disk, root_num);
            let root_uuid = blkid_uuid(&root_part).await.unwrap_or_default();
            let crypt_arg = format!("cryptdevice=UUID={}:cryptroot root=/dev/mapper/cryptroot", root_uuid);
            if cmdline.is_empty() {
                cmdline = crypt_arg;
            } else {
                cmdline = format!("{} {}", crypt_arg, cmdline);
            }
        }
        if !cmdline.is_empty() {
            let cmdline_line = format!("GRUB_CMDLINE_LINUX=\"{}\"", cmdline);
            let found_cmdline = grub_default.lines()
                .any(|l| l.trim_start().starts_with("GRUB_CMDLINE_LINUX="));
            if found_cmdline {
                let mut lines: Vec<String> = grub_default.lines()
                    .map(|l| {
                        if l.trim_start().starts_with("GRUB_CMDLINE_LINUX=") {
                            cmdline_line.clone()
                        } else {
                            l.to_string()
                        }
                    })
                    .collect();
                lines.push(String::new());
                grub_default = lines.join("\n");
            } else {
                grub_default.push_str(&format!("{}\n", cmdline_line));
            }
        }
        tokio::fs::write(&default_path, grub_default)
            .await
            .map_err(|e| format!("/etc/default/grub yazılamadı: {}", e))?;

        // Chroot içinde grub2-install çalıştır (UEFI veya BIOS)
        let mut grub_args = vec!["grub2-install"];
        let install_dev = if self.boot_device.is_empty() { self.disk.as_str() } else { self.boot_device.as_str() };

        if self.is_uefi {
            // /boot/efi mevcut değilse veya bağlı değilse bağla
            let host_efi_dir = format!("{}/boot/efi", self.mount);
            let efi_dir = std::path::Path::new(&host_efi_dir);
            if !efi_dir.exists() || !is_mount_point(efi_dir) {
                ui.log("/boot/efi bağlanmamış, ESP taranıyor…");
                tokio::fs::create_dir_all(&host_efi_dir).await
                    .map_err(|e| format!("{}/boot/efi oluşturulamadı: {}", self.mount, e))?;
                // Önyükleyici aygıtında ESP bölümü ara (önce 1, sonra 2)
                for part_num in &[1u32, 2] {
                    let esp_candidate = part_path(install_dev, *part_num);
                    if let Ok(fs_type) = get_fs_type(&esp_candidate).await {
                        if fs_type == "vfat" {
                            ui.log(format!("{} ESP olarak /boot/efi'ye bağlanıyor…", esp_candidate));
                            let _ = run_cmd("mount", &[&esp_candidate, &host_efi_dir]).await;
                            break;
                        }
                    }
                }
            }

            let efi_dir_arg = "--efi-directory=/boot/efi";
            ui.log(format!("GRUB UEFI modu {} aygıtına kuruluyor (chroot içinde)…", install_dev));
            grub_args.extend_from_slice(&[
                "--target=x86_64-efi",
                efi_dir_arg,
                "--bootloader-id=PisiLinux",
                "--recheck",
                install_dev,
            ]);
        } else {
            ui.log(format!("GRUB BIOS modu {} aygıtına kuruluyor (chroot içinde)…", install_dev));
            grub_args.extend_from_slice(&[
                "--target=i386-pc",
                install_dev,
                "--recheck",
            ]);
        }

        run_chroot(&self.mount, &grub_args).await?;

        // device.map: tüm diskleri kapsayan bir mapping oluştur
        ui.log("GRUB device.map oluşturuluyor…");
        let mut device_map = String::new();
        if let Ok(entries) = std::fs::read_dir("/dev") {
            let mut devices: Vec<String> = entries
                .filter_map(|e| e.ok())
                .filter(|e| {
                    let name = e.file_name();
                    let s = name.to_string_lossy();
                    let s = s.as_ref();
                    match s {
                        x if x.starts_with("sd") || x.starts_with("vd") => {
                            // sdX / vdX = disk; sdX1 / vdX1 = partition
                            x.len() <= 3 || !x[3..].chars().any(|c| c.is_ascii_digit())
                        }
                        x if x.starts_with("nvme") => {
                            // nvme0n1 = disk; nvme0n1p1 = partition
                            !x.contains('p')
                        }
                        x if x.starts_with("mmcblk") => {
                            // mmcblk0 = disk; mmcblk0p1 = partition
                            !x.contains('p')
                        }
                        _ => false,
                    }
                })
                .map(|e| format!("/dev/{}", e.file_name().to_string_lossy()))
                .collect();
            devices.sort();
            for (i, dev) in devices.iter().enumerate() {
                device_map.push_str(&format!("(hd{}) {}\n", i, dev));
            }
        }
        if !device_map.is_empty() {
            let dm_path = format!("{}/boot/grub2/device.map", self.mount);
            let _ = tokio::fs::write(&dm_path, device_map).await;
        }

        // /etc/grub.d/30_uefi-firmware: EFI firmware menüsü + Windows EFI taraması
        let uefi_script = r#"#!/bin/sh
set -e

# GRUB2 Helper Script - Pisi Linux / Custom ISO
prefix="/usr"
exec_prefix="/usr"
datarootdir="/usr/share"

export TEXTDOMAIN=grub
export TEXTDOMAINDIR="${datarootdir}/locale"

. "${datarootdir}/grub/grub-mkconfig_lib"

# 1. EFI Firmware (BIOS Setup) Menü Eklentisi
efi_vars_dir=/sys/firmware/efi/efivars
EFI_GLOBAL_VARIABLE=8be4df61-93ca-11d2-aa0d-00e098032b8c
OsIndications="$efi_vars_dir/OsIndicationsSupported-$EFI_GLOBAL_VARIABLE"

if [ -e "$OsIndications" ] && \
   [ "$(( $(printf 0x%x \'"$(cat $OsIndications | cut -b1)") & 1 ))" = 1 ]; then
  LABEL="System setup"

  cat << EOF
if [ "\$grub_platform" = "efi" ]; then
	fwsetup --is-supported
	if [ "\$?" = 0 ]; then
		menuentry '$LABEL' \$menuentry_id_option 'uefi-firmware' {
			fwsetup
		}
	fi
fi
EOF
fi

# 2. Dinamik Windows EFI Taraması (Disk Bağımsız)
if [ -d /sys/firmware/efi ]; then

  TEMP_MOUNT="/tmp/grub_win_detect"
  WIN_UUID=""

  # Sistemdeki vfat (EFI) bölümlerini al
  EFI_PARTS=$(lsblk -list -no NAME,FSTYPE 2>/dev/null | awk '$2=="vfat" {print $1}')

  for part in $EFI_PARTS; do
      DEV="/dev/$part"

      MNT=$(findmnt -n -o TARGET "$DEV" 2>/dev/null || true)
      WAS_MOUNTED=1

      if [ -z "$MNT" ]; then
          WAS_MOUNTED=0
          MNT="$TEMP_MOUNT"
          mkdir -p "$MNT"
          mount -o ro "$DEV" "$MNT" 2>/dev/null || continue
      fi

      if [ -f "$MNT/EFI/Microsoft/Boot/bootmgfw.efi" ]; then
          WIN_UUID=$(blkid -s UUID -o value "$DEV" 2>/dev/null || true)
          if [ "$WAS_MOUNTED" -eq 0 ]; then
              umount "$MNT" 2>/dev/null || true
          fi
          break
      fi

      if [ "$WAS_MOUNTED" -eq 0 ]; then
          umount "$MNT" 2>/dev/null || true
      fi
  done

  rm -rf "$TEMP_MOUNT"

  if [ -n "$WIN_UUID" ]; then
cat << EOF
menuentry "Windows Boot Manager (UEFI)" --class windows --class os {
  insmod part_gpt
  insmod fat
  insmod search_fs_uuid
  insmod chain
  search --fs-uuid --set=root ${WIN_UUID}
  chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}
EOF
  fi

fi
"#;
        let uefi_script_path = format!("{}/etc/grub.d/30_uefi-firmware", self.mount);
        if let Err(e) = tokio::fs::write(&uefi_script_path, uefi_script).await {
            ui.log(format!("/etc/grub.d/30_uefi-firmware yazılamadı: {}", e));
        } else {
            ui.log("30_uefi-firmware güncellendi (EFI firmware + Windows taraması).");
        }

        ui.log("grub2-mkconfig çalıştırılıyor…");
        // grub2-mkconfig'teki set -e'yi geçici olarak set +e yap;
        // böylece grub2-probe hataları script'i durdurmaz, os-prober hatayı atlayıp
        // sonraki aygıtlarla devam edebilir. stderr'deki probe uyarıları bastırılır.
        let _ = run_chroot(&self.mount, &["sh", "-c",
            "cp /usr/sbin/grub2-mkconfig /usr/sbin/grub2-mkconfig.bak 2>/dev/null; \
             sed -i 's/^set -e/set +e/' /usr/sbin/grub2-mkconfig 2>/dev/null; \
             GRUB_DISABLE_OS_PROBER=false grub2-mkconfig -o /boot/grub2/grub.cfg 2>/dev/null; \
             cp /usr/sbin/grub2-mkconfig.bak /usr/sbin/grub2-mkconfig 2>/dev/null; \
             rm -f /usr/sbin/grub2-mkconfig.bak 2>/dev/null",
        ]).await;

        let grub_cfg_path = format!("{}/boot/grub2/grub.cfg", self.mount);
        let grub_cfg_ok = std::path::Path::new(&grub_cfg_path).exists()
            && std::fs::metadata(&grub_cfg_path).map(|m| m.len() > 0).unwrap_or(false);

        if grub_cfg_ok {
            ui.log("GRUB yapılandırması başarıyla oluşturuldu.");
            Ok(())
        } else {
            ui.log("grub2-mkconfig başarısız, minimal yapılandırma oluşturuluyor…");
            generate_minimal_grub_cfg(ui, &self.mount, &self.disk, &self.boot_device, self.is_uefi, &self.kernel_options).await
        }
    }
}

/// grub2-mkconfig başarısız olursa elle minimal bir grub.cfg oluşturur.
/// Windows ESP tespit edilirse chainloader girişi de ekler.
async fn generate_minimal_grub_cfg(
    ui: &UiSender, mount: &str, disk: &str, boot_device: &str, is_uefi: bool, kernel_options: &str,
) -> Result<(), String> {
    let boot_dir = format!("{}/boot", mount);
    let grub_dir = format!("{}/boot/grub2", mount);
    tokio::fs::create_dir_all(&grub_dir).await
        .map_err(|e| format!("{} oluşturulamadı: {}", grub_dir, e))?;

    let (root_num, _swap_num) = if is_uefi { (3u32, 2u32) } else { (2u32, 1u32) };
    let root_part = part_path(disk, root_num);
    let root_uuid = blkid_uuid(&root_part).await
        .map_err(|e| format!("Kök UUID okunamadı: {}", e))?;

    let mut kernel_file: Option<String> = None;
    let mut initrd_files: Vec<String> = Vec::new();
    let mut ucode_files: Vec<String> = Vec::new();

    if let Ok(mut entries) = tokio::fs::read_dir(&boot_dir).await {
        while let Ok(Some(entry)) = entries.next_entry().await {
            let name = entry.file_name().to_string_lossy().to_string();
            if name.starts_with("kernel-") || name.starts_with("vmlinuz-") {
                kernel_file = Some(name);
            } else if name.starts_with("initramfs-") || name.starts_with("initrd-") {
                initrd_files.push(name);
            } else if name.starts_with("intel-ucode") || name.starts_with("amd-ucode") {
                ucode_files.push(name);
            }
        }
    }

    // Demo modunda gerçek dosyalar kopyalanmadığı için /boot boştur;
    // sentetik bir kernel adıyla akışın tamamı denenebilir.
    let kernel = match kernel_file {
        Some(k) => k,
        None if DEMO_MODE.load(Ordering::Relaxed) => "kernel-demo".to_string(),
        None => return Err(format!("{}/boot/ içinde kernel bulunamadı", mount)),
    };

    let kernel_ver = kernel.strip_prefix("kernel-")
        .or_else(|| kernel.strip_prefix("vmlinuz-"))
        .unwrap_or("");
    let initrd = if kernel_ver.is_empty() {
        initrd_files.first().cloned()
    } else {
        let matching: Vec<String> = initrd_files.iter()
            .filter(|f| f.contains(kernel_ver))
            .cloned()
            .collect();
        matching.first().cloned()
    };

    let mut cfg = String::new();
    cfg.push_str("set default=0\n");
    cfg.push_str("set timeout=5\n\n");

    // PisiLinux girişi
    if is_uefi {
        cfg.push_str("menuentry \"PisiLinux\" {\n");
        cfg.push_str("    insmod part_gpt\n    insmod ext2\n");
        cfg.push_str(&format!("    search --no-floppy --fs-uuid --set=root {}\n", root_uuid));
        cfg.push_str(&format!("    linux /boot/{} root=UUID={} rw quiet", kernel, root_uuid));
    } else {
        cfg.push_str("menuentry \"PisiLinux\" {\n");
        cfg.push_str("    insmod ext2\n    set root=(hd0,msdos2)\n");
        cfg.push_str(&format!("    linux /boot/{} root=UUID={} rw quiet", kernel, root_uuid));
    }

    if !kernel_options.is_empty() {
        cfg.push_str(&format!(" {}", kernel_options));
    }
    cfg.push('\n');

    if !ucode_files.is_empty() || !initrd_files.is_empty() {
        let mut initrd_line = String::from("    initrd");
        for ucode in &ucode_files {
            initrd_line.push_str(&format!(" /boot/{}", ucode));
        }
        if let Some(ref i) = initrd {
            initrd_line.push_str(&format!(" /boot/{}", i));
        } else if !initrd_files.is_empty() {
            for f in &initrd_files {
                initrd_line.push_str(&format!(" /boot/{}", f));
            }
        }
        cfg.push_str(&initrd_line);
        cfg.push('\n');
    }
    cfg.push_str("}\n");

    // Windows chainloader girişi (yalnız UEFI)
    if is_uefi && !boot_device.is_empty() && boot_device != disk {
        let mut win_uuid: Option<String> = None;
        for part_num in &[1u32, 2] {
            let esp_candidate = part_path(boot_device, *part_num);
            if let Ok(fs_type) = get_fs_type(&esp_candidate).await {
                if fs_type == "vfat" {
                    if let Ok(uuid) = blkid_uuid(&esp_candidate).await {
                        win_uuid = Some(uuid);
                        break;
                    }
                }
            }
        }
        if let Some(ref uuid) = win_uuid {
            cfg.push_str(&format!("\nmenuentry \"Windows Boot Manager\" {{\n"));
            cfg.push_str("    insmod part_gpt\n    insmod fat\n");
            cfg.push_str(&format!("    search --no-floppy --fs-uuid --set=root {}\n", uuid));
            cfg.push_str("    chainloader /EFI/Microsoft/Boot/bootmgfw.efi\n");
            cfg.push_str("}\n");
            ui.log("Windows Boot Manager eklendi.");
        }
    }

    tokio::fs::write(&format!("{}/boot/grub2/grub.cfg", mount), cfg).await
        .map_err(|e| format!("grub.cfg yazılamadı: {}", e))?;

    ui.log("Minimal GRUB yapılandırması oluşturuldu.");
    Ok(())
}
