use anyhow::{Context, Result, bail};
use dialoguer::{Confirm, Select};

use crate::api;
use crate::util::BidStatusExt;

pub async fn run(
    bid_name: Option<String>,
    skip_confirm: bool,
    delete_all: bool,
    name_pattern: Option<String>,
) -> Result<()> {
    let client = api::Client::load()?;

    if delete_all {
        return delete_all_bids(&client, skip_confirm).await;
    }

    if let Some(pattern) = name_pattern {
        return delete_by_pattern(&client, &pattern, skip_confirm).await;
    }

    let bid = if let Some(name) = bid_name {
        find_bid_by_name(&client, &name).await?
    } else {
        select_bid_interactive(&client).await?
    };

    delete_single_bid(&client, &bid, skip_confirm).await
}

async fn delete_single_bid(
    client: &api::Client,
    bid: &mithril_client::models::BidModel,
    skip_confirm: bool,
) -> Result<()> {
    println!();
    println!("Bid to delete:");
    println!("  Name:   {}", bid.name);
    println!("  FID:    {}", bid.fid);
    println!("  Type:   {}", bid.instance_type);
    println!("  Status: {}", bid.status.as_str());
    println!(
        "  Instances: {}/{}",
        bid.instances.len(),
        bid.instance_quantity
    );
    println!();

    if !skip_confirm {
        let confirmed = Confirm::new()
            .with_prompt(format!("Cancel bid '{}'?", bid.name))
            .default(false)
            .interact()
            .context("Failed to get confirmation")?;

        if !confirmed {
            println!("Cancelled.");
            return Ok(());
        }
    }

    println!("Cancelling bid '{}'...", bid.name);
    client.cancel_bid(&bid.fid).await?;
    println!("Bid '{}' cancelled successfully.", bid.name);

    Ok(())
}

async fn delete_all_bids(client: &api::Client, skip_confirm: bool) -> Result<()> {
    println!("Fetching active bids...");
    let bids = client.fetch_bids(None, None, None).await?;

    let active_bids: Vec<_> = bids.into_iter().filter(|b| b.status.is_active()).collect();

    if active_bids.is_empty() {
        println!("No active bids to cancel.");
        return Ok(());
    }

    println!();
    println!("Active bids to cancel:");
    for bid in &active_bids {
        println!(
            "  - {} ({}, {}, {}/{})",
            bid.name,
            bid.instance_type,
            bid.status.as_str(),
            bid.instances.len(),
            bid.instance_quantity
        );
    }
    println!();

    if !skip_confirm {
        let confirmed = Confirm::new()
            .with_prompt(format!("Cancel ALL {} active bid(s)?", active_bids.len()))
            .default(false)
            .interact()
            .context("Failed to get confirmation")?;

        if !confirmed {
            println!("Cancelled.");
            return Ok(());
        }
    }

    for bid in &active_bids {
        println!("Cancelling '{}'...", bid.name);
        match client.cancel_bid(&bid.fid).await {
            Ok(()) => println!("  Cancelled '{}'", bid.name),
            Err(e) => eprintln!("  Failed to cancel '{}': {}", bid.name, e),
        }
    }

    println!();
    println!("Done. Cancelled {} bid(s).", active_bids.len());

    Ok(())
}

async fn delete_by_pattern(client: &api::Client, pattern: &str, skip_confirm: bool) -> Result<()> {
    println!("Fetching bids matching pattern '{pattern}'...");
    let bids = client.fetch_bids(None, None, None).await?;

    let matching_bids: Vec<_> = bids
        .into_iter()
        .filter(|b| b.status.is_active() && wildcard_match(pattern, &b.name))
        .collect();

    if matching_bids.is_empty() {
        println!("No active bids matching pattern '{pattern}'.");
        return Ok(());
    }

    println!();
    println!("Matching bids:");
    for bid in &matching_bids {
        println!(
            "  - {} ({}, {}, {}/{})",
            bid.name,
            bid.instance_type,
            bid.status.as_str(),
            bid.instances.len(),
            bid.instance_quantity
        );
    }
    println!();

    if !skip_confirm {
        let confirmed = Confirm::new()
            .with_prompt(format!(
                "Cancel {} bid(s) matching '{}'?",
                matching_bids.len(),
                pattern
            ))
            .default(false)
            .interact()
            .context("Failed to get confirmation")?;

        if !confirmed {
            println!("Cancelled.");
            return Ok(());
        }
    }

    for bid in &matching_bids {
        println!("Cancelling '{}'...", bid.name);
        match client.cancel_bid(&bid.fid).await {
            Ok(()) => println!("  Cancelled '{}'", bid.name),
            Err(e) => eprintln!("  Failed to cancel '{}': {}", bid.name, e),
        }
    }

    println!();
    println!("Done. Cancelled {} bid(s).", matching_bids.len());

    Ok(())
}

async fn find_bid_by_name(
    client: &api::Client,
    name: &str,
) -> Result<mithril_client::models::BidModel> {
    println!("Looking for bid '{name}'...");

    let all_matches = client.search_bids_by_name(name).await?;

    if all_matches.is_empty() {
        bail!("Bid '{name}' not found");
    }

    if all_matches.len() == 1 {
        return Ok(all_matches.into_iter().next().unwrap());
    }

    println!("Found {} bids with name '{}':", all_matches.len(), name);
    let selection = Select::new()
        .with_prompt("Select which bid to delete")
        .items(
            &all_matches
                .iter()
                .map(|bid| {
                    format!(
                        "{} (project: {}, region: {}, status: {})",
                        bid.name,
                        bid.project,
                        bid.region.as_deref().unwrap_or("unknown"),
                        bid.status.as_str()
                    )
                })
                .collect::<Vec<_>>(),
        )
        .default(0)
        .interact()
        .context("Failed to get user selection")?;

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

async fn select_bid_interactive(client: &api::Client) -> Result<mithril_client::models::BidModel> {
    println!("Fetching active bids...");

    let bids = client.fetch_bids(None, None, None).await?;

    let active_bids: Vec<_> = bids.into_iter().filter(|b| b.status.is_active()).collect();

    if active_bids.is_empty() {
        bail!("No active bids found");
    }

    let mut sorted_bids = active_bids;
    sorted_bids.sort_by(|a, b| b.created_at.cmp(&a.created_at));

    let selection = Select::new()
        .with_prompt("Select bid to delete")
        .items(
            &sorted_bids
                .iter()
                .map(|bid| {
                    format!(
                        "{} | {} | {} | {} | {}/{}",
                        bid.name,
                        bid.instance_type,
                        bid.region.as_deref().unwrap_or("-"),
                        bid.status.as_str(),
                        bid.instances.len(),
                        bid.instance_quantity
                    )
                })
                .collect::<Vec<_>>(),
        )
        .default(0)
        .interact()
        .context("Failed to get user selection")?;

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

fn wildcard_match(pattern: &str, text: &str) -> bool {
    let pattern_chars: Vec<char> = pattern.chars().collect();
    let text_chars: Vec<char> = text.chars().collect();

    wildcard_match_helper(&pattern_chars, &text_chars)
}

fn wildcard_match_helper(pattern: &[char], text: &[char]) -> bool {
    if pattern.is_empty() {
        return text.is_empty();
    }

    if pattern[0] == '*' {
        // Try matching zero characters or more
        for i in 0..=text.len() {
            if wildcard_match_helper(&pattern[1..], &text[i..]) {
                return true;
            }
        }
        return false;
    }

    if pattern[0] == '?' {
        if text.is_empty() {
            return false;
        }
        return wildcard_match_helper(&pattern[1..], &text[1..]);
    }

    if text.is_empty() || pattern[0] != text[0] {
        return false;
    }

    wildcard_match_helper(&pattern[1..], &text[1..])
}
