use crate::colors::get_colors;
use crate::config::get_config;
use crate::options::Options;
use crate::utils::{confirm, get_installed_packages, is_installed, run_command};
use serde_json::to_writer_pretty;
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;

/// Removes installed `SBo` packages using the configured `removepkg` command.
///
/// Packages not found in the installed list are reported as errors.
/// If some packages are valid, the user is prompted before proceeding.
/// When `--with-deps` is set, dependencies installed alongside the package are also removed.
///
/// # Exits
///
/// Exits with code 1 if the `removepkg` command fails.
pub fn remove(packages: &[String], options: &Options) {
    let mut packages = packages.to_vec();
    let mut seen = std::collections::HashSet::new();
    packages.retain(|p| seen.insert(p.clone()));

    let cfg = get_config();
    let color = get_colors(cfg.colors);

    let installed = get_installed_packages();
    let mut to_remove: Vec<String> = Vec::new();
    let mut not_found: Vec<String> = Vec::new();

    type Dependencies = HashMap<String, Vec<String>>;

    let deps_log_file = format!("{}{}", cfg.var_log_path, cfg.deps_log_file);

    let mut deps: Dependencies = match File::open(&deps_log_file) {
        Ok(f) => serde_json::from_reader(BufReader::new(f)).unwrap_or_default(),
        Err(_) => HashMap::new(),
    };

    for pkg in &packages {
        if is_installed(pkg, &installed) {
            to_remove.push(pkg.clone());
            if options.with_deps {
                if let Some(dependencies) = deps.get(pkg) {
                    for dep in dependencies {
                        if is_installed(dep, &installed) && !to_remove.contains(dep) {
                            to_remove.push(dep.clone());
                        }
                    }
                }
            }
        } else {
            not_found.push(pkg.clone());
        }
    }

    if !not_found.is_empty() {
        println!(
            "{}Error:{} package(s) '{}' not installed",
            color.red,
            color.reset,
            not_found.join(", ")
        );
        if !to_remove.is_empty() {
            println!();
        }
    }

    let total = to_remove.len();
    let mut warnings: Vec<String> = Vec::new();

    if !to_remove.is_empty() {
        println!(
            "{}Packages ({}) to remove:{}",
            color.bold, total, color.reset
        );

        for rmv in &to_remove {
            println!("  {}{}{}", color.red, rmv, color.reset);

            let mut used_by: Vec<String> = Vec::new();
            for (k, v) in &deps {
                if v.contains(rmv) && !to_remove.contains(k) {
                    used_by.push(k.to_string());
                }
            }
            if !used_by.is_empty() {
                warnings.push(format!(
                    "'{}' is also a dependency of: {}",
                    rmv,
                    used_by.join(", ")
                ));
            }
        }

        if !warnings.is_empty() {
            println!();
            for w in &warnings {
                println!("  {}Warning:{} {}", color.yellow, color.reset, w);
            }
        }

        confirm("\nContinue? [Y/n]: ", options.no_confirm);
        println!();
    }

    for (i, rmv) in to_remove.iter().enumerate() {
        println!("[{}/{}] Removing {}", i + 1, total, rmv);

        let args = vec!["-c".to_string(), format!("{} {}", cfg.removepkg, rmv)];

        run_command("bash", &args);

        deps.remove(rmv);

        for dependencies in deps.values_mut() {
            dependencies.retain(|x| x != rmv);
        }

        let file_to_write = match File::create(&deps_log_file) {
            Ok(f) => f,
            Err(e) => {
                eprintln!(
                    "{}Error:{} creating file for writing: {}",
                    color.red, color.reset, e
                );
                return;
            }
        };
        match to_writer_pretty(file_to_write, &deps) {
            Ok(()) => (),
            Err(e) => eprintln!(
                "{}Error:{} writing JSON to file: {}",
                color.red, color.reset, e
            ),
        }
    }
}
