use anyhow::Result;
use std::collections::{HashMap, HashSet};
use tabled::{Table, Tabled, settings::Style};

use crate::api;
use crate::util::{
    InstanceStatusExt, K8sClusterStatusExt, calculate_age, sort_k8s_clusters_by_priority,
};
use mithril_client::models::kubernetes_cluster_model::Status as K8sClusterStatus;

#[derive(Tabled)]
struct ClusterRow {
    #[tabled(rename = "Name")]
    name: String,
    #[tabled(rename = "Project")]
    project: String,
    #[tabled(rename = "Region")]
    region: String,
    #[tabled(rename = "Status")]
    status: String,
    #[tabled(rename = "Active")]
    active_instances: String,
    #[tabled(rename = "Age")]
    age: String,
}

pub async fn run(show_all: bool, json: bool) -> Result<()> {
    let client = api::Client::load()?;

    println!("Fetching clusters...");
    let projects = client.fetch_projects().await?;

    // Build project name lookup
    let project_names: HashMap<&str, &str> = projects
        .iter()
        .map(|p| (p.fid.as_str(), p.name.as_str()))
        .collect();

    // Fetch clusters from all projects
    let mut clusters = Vec::new();
    let mut projects_with_clusters: HashSet<String> = HashSet::new();
    for project in &projects {
        let project_clusters = client.fetch_k8s_clusters(&project.fid).await?;
        if !project_clusters.is_empty() {
            projects_with_clusters.insert(project.fid.clone());
        }
        clusters.extend(project_clusters);
    }

    // Fetch instances only for projects with clusters
    let mut all_instances = Vec::new();
    for project_fid in &projects_with_clusters {
        let instances = client.fetch_instances_for_project(project_fid).await?;
        all_instances.extend(instances);
    }

    // Build set of active instance FIDs
    let active_instance_fids: HashSet<&str> = all_instances
        .iter()
        .filter(|(inst, _)| inst.status.is_active())
        .map(|(inst, _)| inst.fid.as_str())
        .collect();

    // Filter terminated clusters unless --all
    if !show_all {
        clusters.retain(|c| c.status != K8sClusterStatus::Terminated);
    }

    sort_k8s_clusters_by_priority(&mut clusters);

    if json {
        let output = serde_json::json!({
            "clusters": clusters.iter().map(|c| {
                let active_count = c.instances.iter()
                    .filter(|fid| active_instance_fids.contains(fid.as_str()))
                    .count();
                let project_name = project_names.get(c.project.as_str()).unwrap_or(&"unknown");
                serde_json::json!({
                    "fid": c.fid,
                    "name": c.name,
                    "project_fid": c.project,
                    "project_name": project_name,
                    "region": c.region,
                    "status": c.status.as_str(),
                    "kube_host": c.kube_host,
                    "active_instances": active_count,
                    "total_instances": c.instances.len(),
                    "ssh_keys": c.ssh_keys,
                    "created_at": c.created_at,
                })
            }).collect::<Vec<_>>()
        });
        println!("{}", serde_json::to_string_pretty(&output)?);
        return Ok(());
    }

    if clusters.is_empty() {
        if show_all {
            println!("No clusters found.");
        } else {
            println!("No active clusters found. Use --all to show all clusters.");
        }
        return Ok(());
    }

    let rows: Vec<ClusterRow> = clusters
        .iter()
        .map(|cluster| {
            let active_count = cluster
                .instances
                .iter()
                .filter(|fid| active_instance_fids.contains(fid.as_str()))
                .count();
            let project_name = project_names
                .get(cluster.project.as_str())
                .unwrap_or(&"unknown");
            ClusterRow {
                name: cluster.name.clone(),
                project: project_name.to_string(),
                region: cluster.region.clone(),
                status: cluster.status.colorize(),
                active_instances: active_count.to_string(),
                age: calculate_age(&cluster.created_at),
            }
        })
        .collect();

    let table = Table::new(rows).with(Style::rounded()).to_string();
    println!("{table}");
    println!("\nTotal: {} cluster(s)", clusters.len());

    Ok(())
}
