use std::{
    fs::{self, read_dir},
    io,
    path::Path,
};

use eframe::egui::Ui;

pub const COOL_GRAY: &str = "#8895B3";

use std::collections::HashSet;
use std::fs::File;
use std::io::BufRead;

use std::process::Command;

fn launch_app_from_desktop_file(path: &Path) -> io::Result<()> {
    let file = File::open(path)?;
    let reader = io::BufReader::new(file);
    let mut exec_command = String::new();

    for line in reader.lines() {
        let line = line?;
        if line.starts_with("Exec=") {
            exec_command = line.trim_start_matches("Exec=").to_string();
            break;
        }
    }

    if exec_command.is_empty() {
        return Err(io::Error::new(
            io::ErrorKind::NotFound,
            "Exec command not found in .desktop file",
        ));
    }

    // Remove field codes from Exec command (e.g., %f, %u)
    let exec_parts: Vec<&str> = exec_command
        .split_whitespace()
        .filter(|&part| !part.starts_with('%'))
        .collect();

    if let Some(program) = exec_parts.first() {
        let mut command = Command::new(program);
        command.args(&exec_parts[1..]);
        command.spawn()?;
        Ok(())
    } else {
        Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "Invalid Exec command",
        ))
    }
}

fn get_installed_apps() -> io::Result<Vec<String>> {
    let mut apps = HashSet::new();
    let distribution = detect_distribution()?;
    let command = match distribution.as_str() {
        "debian" | "ubuntu" => "dpkg --list | grep '^ii' | awk '{print $2}'",
        "fedora" | "centos" | "rhel" => "rpm -qa",
        "arch" | "manjaro" | "endeavouros" => "pacman -Q",
        e => {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                format!("Unsupported distribution: {}", e),
            ))
        }
    };

    let output = Command::new("sh").arg("-c").arg(command).output()?;

    for line in output.stdout.lines() {
        if let Ok(app) = line {
            apps.insert(app);
        }
    }

    // let mut apps: Vec<String> = output.stdout.lines().filter_map(|line| line.ok()).collect();

    // Get apps from .desktop files
    let desktop_dirs = vec![
        "/usr/share/applications",
        "/usr/local/share/applications",
        "/home/user/.local/share/applications", // Replace 'user' with actual username
    ];

    for dir in desktop_dirs {
        if let Ok(entries) = read_dir(dir) {
            for entry in entries {
                if let Ok(entry) = entry {
                    if let Some(file_name) = entry.file_name().to_str() {
                        if file_name.ends_with(".desktop") {
                            if let Some(app_name) = get_app_name_from_desktop_file(entry.path()) {
                                apps.insert(app_name);
                            }
                        }
                    }
                }
            }
        }
    }

    // Convert HashSet to Vec and sort
    let mut apps_vec: Vec<String> = apps.into_iter().collect();
    apps_vec.sort_by(|a, b| {
        let a_starts_with_capital = a.chars().next().map_or(false, |c| c.is_uppercase());
        let b_starts_with_capital = b.chars().next().map_or(false, |c| c.is_uppercase());

        match (a_starts_with_capital, b_starts_with_capital) {
            (true, false) => std::cmp::Ordering::Less,
            (false, true) => std::cmp::Ordering::Greater,
            _ => a.to_lowercase().cmp(&b.to_lowercase()),
        }
    });

    Ok(apps_vec)
}

fn get_app_name_from_desktop_file<P: AsRef<Path>>(path: P) -> Option<String> {
    if let Ok(file) = File::open(path) {
        let reader = io::BufReader::new(file);
        for line in reader.lines() {
            if let Ok(line) = line {
                if line.starts_with("Name=") {
                    return Some(line.trim_start_matches("Name=").to_string());
                }
            }
        }
    }
    None
}

fn detect_distribution() -> io::Result<String> {
    let file = File::open("/etc/os-release")?;
    let reader = io::BufReader::new(file);

    for line in reader.lines() {
        let line = line?;
        if line.starts_with("ID=") {
            return Ok(line.trim_start_matches("ID=").trim_matches('"').to_string());
        }
    }

    Err(io::Error::new(
        io::ErrorKind::NotFound,
        "Distribution not detected",
    ))
}

pub fn get_apps() -> io::Result<Vec<String>> {
    match get_installed_apps() {
        Ok(apps) => {
            println!("Installed applications:");
            // for app in apps {
            //     // println!("{}", app);
            // }
            Ok(apps)
        }
        Err(e) => {
            eprintln!("Error: {}", e);
            Err(e)
        }
    }
}

pub fn display_files(ui: &mut Ui, directory: &str, app_name_input: &str) {
    // ui.separator();
    // ui.small("files and folders");
    let path = Path::new(directory);

    if let Ok(entries) = fs::read_dir(path) {
        // ScrollArea::vertical().id_source("app_list").show(ui, |ui| {
        for entry in entries {
            if let Ok(entry) = entry {
                if let Ok(file_name) = entry.file_name().into_string() {
                    if file_name.contains(app_name_input) {
                        ui.label(file_name);
                    }
                }
            }
        }

        // });
    } else {
        ui.label(format!("Error reading directory: {}", directory));
    }
    ui.add_space(50.);
}

pub fn spawn_app(app_name: &str) -> io::Result<()> {
    let desktop_dirs = vec![
        "/usr/share/applications",
        "/usr/local/share/applications",
        "/home/user/.local/share/applications", // Replace 'user' with actual username
    ];

    for dir in desktop_dirs {
        if let Ok(entries) = read_dir(dir) {
            for entry in entries {
                if let Ok(entry) = entry {
                    if let Some(file_name) = entry.file_name().to_str() {
                        if file_name.ends_with(".desktop") {
                            if let Some(found_app_name) =
                                get_app_name_from_desktop_file(&entry.path())
                            {
                                if found_app_name.to_lowercase() == app_name.to_lowercase() {
                                    return launch_app_from_desktop_file(&entry.path());
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    // If no .desktop file found, try to launch directly
    let app_binary = app_name.split(" ").next().unwrap();
    dbg!(&app_binary);
    Command::new(app_binary).spawn()?;
    Ok(())
}
