use anyhow::{Context, Result};
use dialoguer::Select;
use std::collections::HashMap;
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::KubernetesClusterModel;

#[derive(Tabled)]
struct InstanceRow {
    #[tabled(rename = "#")]
    index: usize,
    #[tabled(rename = "Name")]
    name: String,
    #[tabled(rename = "Status")]
    status: String,
    #[tabled(rename = "SSH")]
    ssh: String,
    #[tabled(rename = "Private IP")]
    private_ip: String,
}

pub async fn run(cluster_name: Option<String>, json: bool) -> Result<()> {
    let client = api::Client::load()?;
    let project = client.resolve_project(Some(&client.project_id)).await?;

    let cluster = if let Some(name_or_fid) = cluster_name {
        client
            .resolve_k8s_cluster(&project.fid, &name_or_fid)
            .await?
    } else {
        select_cluster_interactive(&client, &project.fid).await?
    };

    // Fetch SSH key names and instances for this cluster
    let (ssh_keys, instances) = tokio::try_join!(
        client.fetch_ssh_keys(&project.fid),
        client.fetch_instances_for_project(&project.fid),
    )?;

    let ssh_key_names: HashMap<&str, &str> = ssh_keys
        .iter()
        .map(|k| (k.fid.as_str(), k.name.as_str()))
        .collect();

    let cluster_instances: Vec<_> = instances
        .into_iter()
        .filter(|(inst, _)| cluster.instances.contains(&inst.fid))
        .collect();

    let active_count = cluster_instances
        .iter()
        .filter(|(inst, _)| inst.status.is_active())
        .count();

    if json {
        let output = serde_json::json!({
            "cluster": {
                "fid": cluster.fid,
                "name": cluster.name,
                "project": cluster.project,
                "region": cluster.region,
                "status": cluster.status.as_str(),
                "kube_host": cluster.kube_host,
                "ssh_keys": cluster.ssh_keys.iter().map(|fid| {
                    serde_json::json!({
                        "fid": fid,
                        "name": ssh_key_names.get(fid.as_str()).unwrap_or(&"unknown")
                    })
                }).collect::<Vec<_>>(),
                "active_instances": active_count,
                "total_instances": cluster.instances.len(),
                "join_command": cluster.join_command,
                "created_at": cluster.created_at,
                "deleted_at": cluster.deleted_at,
            },
            "instances": cluster_instances.iter().map(|(inst, _)| serde_json::json!({
                "fid": inst.fid,
                "name": inst.name,
                "status": format!("{:?}", inst.status),
                "ssh_destination": inst.ssh_destination,
                "private_ip": inst.private_ip,
                "region": inst.region,
                "created_at": inst.created_at,
            })).collect::<Vec<_>>()
        });
        println!("{}", serde_json::to_string_pretty(&output)?);
        return Ok(());
    }

    // Display cluster details
    println!();
    println!("Cluster: {}", cluster.name);
    println!("{}", "=".repeat(50));
    println!("FID:              {}", cluster.fid);
    println!("Project:          {}", cluster.project);
    println!("Status:           {}", cluster.status.colorize());
    println!("Region:           {}", cluster.region);
    println!(
        "Kube Host:        {}",
        cluster.kube_host.as_deref().unwrap_or("-")
    );
    println!(
        "Active Instances: {}/{}",
        active_count,
        cluster.instances.len()
    );
    println!(
        "Created:          {} ({})",
        cluster.created_at,
        calculate_age(&cluster.created_at)
    );

    // SSH Keys
    let key_names: Vec<_> = cluster
        .ssh_keys
        .iter()
        .map(|fid| {
            ssh_key_names
                .get(fid.as_str())
                .copied()
                .unwrap_or("unknown")
        })
        .collect();
    println!("SSH Keys:         {}", key_names.join(", "));

    if let Some(ref deleted_at) = cluster.deleted_at {
        println!("Deleted At:       {deleted_at}");
    }

    // Display instances
    if !cluster_instances.is_empty() {
        let rows: Vec<InstanceRow> = cluster_instances
            .iter()
            .enumerate()
            .map(|(i, (inst, _))| InstanceRow {
                index: i,
                name: inst.name.clone(),
                status: format!("{:?}", inst.status),
                ssh: inst
                    .ssh_destination
                    .as_ref()
                    .map(|s| format!("ubuntu@{s}"))
                    .unwrap_or_else(|| "-".to_string()),
                private_ip: inst.private_ip.clone().unwrap_or_else(|| "-".to_string()),
            })
            .collect();

        println!();
        println!("Instances:");
        let table = Table::new(rows).with(Style::rounded()).to_string();
        println!("{table}");
    }

    // Show join command if available
    if let Some(ref join_cmd) = cluster.join_command
        && !join_cmd.is_empty()
    {
        println!();
        println!("Join Command:");
        println!("  {join_cmd}");
    }

    println!();

    Ok(())
}

async fn select_cluster_interactive(
    client: &api::Client,
    project_fid: &str,
) -> Result<KubernetesClusterModel> {
    println!("Fetching clusters...");

    let clusters = client.fetch_k8s_clusters(project_fid).await?;

    if clusters.is_empty() {
        anyhow::bail!("No clusters found");
    }

    let mut sorted_clusters: Vec<_> = clusters.into_iter().collect();
    sort_k8s_clusters_by_priority(&mut sorted_clusters);

    let selection = Select::new()
        .with_prompt("Select cluster to view")
        .items(
            &sorted_clusters
                .iter()
                .map(|c| {
                    format!(
                        "{} | {} | {} | {} instances",
                        c.name,
                        c.region,
                        c.status.as_str(),
                        c.instances.len()
                    )
                })
                .collect::<Vec<_>>(),
        )
        .default(0)
        .interact()
        .context("Failed to get user selection")?;

    Ok(sorted_clusters.into_iter().nth(selection).unwrap())
}
