use std::collections::HashMap;

use anyhow::Result;
use tabled::{Table, Tabled, settings::Style};

use crate::api;
use crate::cli::BidStatus;
use crate::util::{BidStatusExt, calculate_age, sort_bids_by_priority};

#[derive(Tabled)]
struct BidRow {
    #[tabled(rename = "Name")]
    name: String,
    #[tabled(rename = "Instance Type")]
    instance_type: String,
    #[tabled(rename = "Region")]
    region: String,
    #[tabled(rename = "Status")]
    status: String,
    #[tabled(rename = "Instances")]
    instances: String,
    #[tabled(rename = "Created By")]
    created_by: String,
    #[tabled(rename = "Age")]
    age: String,
}

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

    let status_filter = match (&state, show_all) {
        (Some(s), _) => Some(bid_status_to_str(s)),
        (None, true) => None,
        (None, false) => None, // We'll filter client-side for active statuses
    };

    println!("Fetching bids...");
    let (mut bids, instance_types, teammates) = tokio::try_join!(
        client.fetch_bids(None, status_filter, limit),
        client.fetch_instance_types(),
        client.fetch_teammates(),
    )?;

    let user_names: HashMap<String, String> = teammates
        .into_iter()
        .filter_map(|t| t.user_name.map(|name| (t.fid, name)))
        .collect();

    // Filter to active statuses if not --all and no specific state
    if !show_all && state.is_none() {
        bids.retain(|b| b.status.is_active());
    }

    sort_bids_by_priority(&mut bids);

    if json {
        let output = serde_json::json!({
            "bids": bids.iter().map(|b| serde_json::json!({
                "fid": b.fid,
                "name": b.name,
                "project": b.project,
                "instance_type": b.instance_type,
                "region": b.region,
                "status": b.status.as_str(),
                "limit_price": b.limit_price,
                "instance_quantity": b.instance_quantity,
                "instances": b.instances,
                "created_at": b.created_at,
                "created_by": b.created_by,
                "created_by_name": user_names.get(&b.created_by),
            })).collect::<Vec<_>>()
        });
        println!("{}", serde_json::to_string_pretty(&output)?);
        return Ok(());
    }

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

    let rows: Vec<BidRow> = bids
        .iter()
        .map(|bid| {
            let type_name = instance_types
                .get(&bid.instance_type)
                .map(|it| it.name.as_str())
                .unwrap_or("Unknown");
            let creator = user_names
                .get(&bid.created_by)
                .cloned()
                .unwrap_or_else(|| bid.created_by.clone());
            BidRow {
                name: bid.name.clone(),
                instance_type: type_name.to_string(),
                region: bid.region.as_deref().unwrap_or("-").to_string(),
                status: bid.status.colorize(),
                instances: format!("{}/{}", bid.instances.len(), bid.instance_quantity),
                created_by: creator,
                age: calculate_age(&bid.created_at),
            }
        })
        .collect();

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

    Ok(())
}

fn bid_status_to_str(status: &BidStatus) -> &'static str {
    match status {
        BidStatus::Allocated => "Allocated",
        BidStatus::Pending => "pending",
        BidStatus::Open => "Open",
        BidStatus::Starting => "starting",
        BidStatus::Running => "running",
        BidStatus::Paused => "Paused",
        BidStatus::Preempting => "Preempting",
        BidStatus::Completed => "completed",
        BidStatus::Failed => "failed",
        BidStatus::Cancelled => "cancelled",
    }
}
