use std::collections::BTreeSet;

use anyhow::{Context, Result};

use crate::config::Config;
use crate::project;
use crate::state::{MachineState, ProjectIntent};
use crate::ui::{self, style};

pub fn run() -> Result<()> {
    let cwd = std::env::current_dir().context("getting current directory")?;
    let global = Config::load()?;
    let machine = MachineState::load()?;
    let project_intent = match project::discover_root(&cwd) {
        Some(root) => Some((root.clone(), ProjectIntent::load(&root)?)),
        None => None,
    };

    let mut printed_anything = false;

    if !machine.tools.is_empty() {
        println!("{}", style::bold("Tools"));
        let mut rows: Vec<Vec<String>> = Vec::new();
        for (name, install) in &machine.tools {
            rows.push(vec![
                name.clone(),
                install.version.clone().unwrap_or_else(|| "-".into()),
                install.method.clone(),
            ]);
        }
        ui::print_table_styled(
            &["Tool", "Version", "Method"],
            &rows,
            None,
            Some(&|col, cell| match col {
                0 => style::cyan(cell),
                1 | 2 => style::dim(cell),
                _ => cell.to_string(),
            }),
        );
        printed_anything = true;
    }

    let mut pkg_names: BTreeSet<String> = BTreeSet::new();
    pkg_names.extend(global.install.packages.iter().cloned());
    if let Some((_, intent)) = &project_intent {
        pkg_names.extend(intent.packages.iter().cloned());
    }
    pkg_names.extend(machine.packages.keys().cloned());

    if !pkg_names.is_empty() {
        if printed_anything {
            println!();
        }
        println!("{}", style::bold("Packages"));
        let mut rows: Vec<Vec<String>> = Vec::new();
        for name in &pkg_names {
            let mut sources: Vec<&str> = Vec::new();
            if global.install.packages.iter().any(|p| p == name) {
                sources.push("global");
            }
            if let Some((_, intent)) = &project_intent {
                if intent.packages.contains(name) {
                    sources.push("project");
                }
            }
            let source = if sources.is_empty() {
                "-".to_string()
            } else {
                sources.join(",")
            };
            let (version, method) = match machine.packages.get(name) {
                Some(m) => (
                    m.version.clone().unwrap_or_else(|| "-".into()),
                    m.method.clone(),
                ),
                None => ("-".into(), "-".into()),
            };
            rows.push(vec![name.clone(), version, method, source]);
        }
        ui::print_table_styled(
            &["Package", "Version", "Method", "Source"],
            &rows,
            None,
            Some(&|col, cell| match col {
                0 => style::cyan(cell),
                1 | 2 => style::dim(cell),
                3 => color_sources(cell),
                _ => cell.to_string(),
            }),
        );
        printed_anything = true;
    }

    if !printed_anything {
        println!("nothing declared or installed");
    }
    Ok(())
}

fn color_sources(cell: &str) -> String {
    let parts: Vec<String> = cell
        .split(',')
        .map(|p| match p.trim() {
            "global" => style::yellow("global"),
            "project" => style::magenta("project"),
            other => other.to_string(),
        })
        .collect();
    parts.join(",")
}
