use data::{gacha::global_gacha_config, tables::ItemID};

use crate::ServerState;

use super::ArgSlice;

pub async fn up(
    args: ArgSlice<'_>,
    state: &ServerState,
) -> Result<String, Box<dyn std::error::Error>> {
    const USAGE: &str = "Usage: gacha up [player_uid] (start a gacha UP setting guide (available for Bangboo pool))";

    if args.len() == 0 {
        return Ok(USAGE.to_string());
    }

    let uid = args[0].parse::<u32>()?;

    let Some(player_lock) = state.player_mgr.get_player(uid).await else {
        return Ok(String::from("player not found"));
    };

    let gachaconf = global_gacha_config();
    let pool_list: Vec<(u32, &String)> = gachaconf
        .character_gacha_pool_list
        .iter()
        .filter(|pool| {
            pool.gacha_items.iter().any(|rarity_items| {
                rarity_items
                    .category_guarantee_policy_tags
                    .iter()
                    .map(|tag| gachaconf.category_guarantee_policy_map.get(tag).unwrap())
                    .any(|policy| policy.chooseable)
            })
        })
        .map(|pool| (pool.gacha_schedule_id, &pool.comment))
        .collect::<Vec<_>>();
    if args.len() == 1 {
        return Ok(format!(
            "{}\n{}\n{}",
            "Choose a target gacha pool first:",
            pool_list
                .iter()
                .map(|(id, comment)| format!("- gacha_schedule_id: {id} ({comment})"))
                .collect::<Vec<_>>()
                .join("\n"),
            "Use 'gacha up [player_uid] [gacha_schedule_id]' to continue."
        ));
    }

    let target_pool_id = args[1].parse::<u32>()?;
    let accepted_pool_id_list = pool_list.into_iter().map(|(id, _)| id).collect::<Vec<_>>();
    if !accepted_pool_id_list.contains(&target_pool_id) {
        return Ok(String::from("Gacha Pool not found or not allowed to set UP. Use gacha up [player_uid] to view available pool ids."));
    }
    let target_pool = gachaconf
        .character_gacha_pool_list
        .iter()
        .filter(|pool| pool.gacha_schedule_id == target_pool_id)
        .last()
        .unwrap();

    let chooseable_items: Vec<(ItemID, &String)> = target_pool
        .gacha_items
        .iter()
        .filter(|rarity_items| {
            rarity_items
                .category_guarantee_policy_tags
                .iter()
                .map(|tag| gachaconf.category_guarantee_policy_map.get(tag).unwrap())
                .any(|policy| policy.chooseable)
        })
        .map(|items| {
            items
                .categories
                .iter()
                .map(|(category_tag, category)| {
                    category
                        .item_ids
                        .iter()
                        .map(|id| (ItemID::new_unchecked(id.clone()), category_tag))
                        .collect::<Vec<_>>()
                })
                .collect::<Vec<_>>()
                .concat()
        })
        .collect::<Vec<_>>()
        .concat();
    if args.len() == 2 {
        return Ok(format!(
            "{}\n{}\nUse 'gacha up [player_uid] {target_pool_id} [item_id]' to set your UP.",
            "Choose the UP item you want:",
            chooseable_items
                .iter()
                .map(|(item_id, category_tag)| format!("- ID {item_id} (category: {category_tag})"))
                .collect::<Vec<_>>()
                .join("\n")
        ));
    }

    let accepted_ids = chooseable_items
        .iter()
        .map(|(item_id, _)| item_id.value())
        .collect::<Vec<_>>();
    let item_id = args[2].parse::<u32>()?;
    if !accepted_ids.contains(&item_id) {
        return Ok(format!("Item ID not found or not included in UP list. Use gacha up [player_uid] {target_pool_id} to view available item ids."));
    }

    let mut player = player_lock.lock().await;
    if player
        .gacha_model
        .choose_gacha_up(target_pool, &ItemID::new_unchecked(item_id))
    {
        Ok(format!(
            "successfully set your gacha pool: {} (comment: {}) 100% UP to {item_id}. Close & Open Gacha Page to see result.",
            target_pool.gacha_schedule_id, target_pool.comment
        ))
    } else {
        Ok(format!(
            "failed to set your gacha pool: {} (comment: {}) 100% UP to {item_id} (unexpected maybe bug)",
            target_pool.gacha_schedule_id, target_pool.comment
        ))
    }
}
