use data::gacha::gacha_config::*;
use data::gacha::global_gacha_config;
use data::tables::ItemID;

use chrono::{DateTime, Local};
use proto::{Gacha, GachaData, GachaPool, NeedItemInfo};
use std::{
    cmp::min,
    collections::{
        hash_map::Entry::{Occupied, Vacant},
        HashSet,
    },
};

use super::GachaModel;

impl GachaModel {
    pub fn to_client(&self, now: &DateTime<Local>) -> GachaData {
        let gachaconf = global_gacha_config();
        let mut gacha_list: Vec<Gacha> = vec![];
        for target_pool in gachaconf.character_gacha_pool_list.iter() {
            if target_pool.is_still_open(now) {
                gacha_list.push(
                    self.generate_gacha_info_from_pool(target_pool, &gachaconf.common_properties),
                );
            }
        }

        // tracing::info!("gacha_list: {:?}", gacha_list);
        GachaData {
            random_number: 6167,
            gacha_pool: Some(GachaPool { gacha_list }),
            ..GachaData::default()
        }
    }

    fn generate_gacha_info_from_pool(
        &self,
        target_pool: &CharacterGachaPool,
        common_properties: &GachaCommonProperties,
    ) -> Gacha {
        let gachaconf = data::gacha::global_gacha_config();
        let sharing_guarantee_category_tag = &target_pool.sharing_guarantee_info_category;
        let status_bin = self
            .gacha_status_map
            .get(sharing_guarantee_category_tag)
            .unwrap();
        let pity_s = status_bin
            .rarity_status_map
            .get(&common_properties.s_item_rarity)
            .unwrap()
            .pity;
        let pity_a = status_bin
            .rarity_status_map
            .get(&common_properties.a_item_rarity)
            .unwrap()
            .pity;

        let mut discount_ten_roll_prize: u32 = 0;
        let mut discount_avaliable_num: u32 = 0;
        let mut advanced_s_guarantee: u32 = 0;
        let mut free_select_progress: u32 = 0;
        let mut free_select_required_pull: u32 = 0;
        let mut free_select_policy: Option<&FreeSelectItem> = None;
        for discount_policy_tag in target_pool.discount_policy_tags.iter() {
            if common_properties.newcomer_advanced_s_tag == *discount_policy_tag {
                let policy = gachaconf
                    .discount_policies
                    .advanced_guarantee_map
                    .get(discount_policy_tag)
                    .unwrap();
                if status_bin
                    .discount_usage_map
                    .get(discount_policy_tag)
                    .unwrap()
                    < &policy.use_limit
                {
                    advanced_s_guarantee = policy.guarantee_pity - pity_s + 1;
                }
            } else if common_properties.ten_pull_discount_tag == *discount_policy_tag {
                let policy = gachaconf
                    .discount_policies
                    .ten_pull_discount_map
                    .get(discount_policy_tag)
                    .unwrap();
                let discount_usage = status_bin
                    .discount_usage_map
                    .get(discount_policy_tag)
                    .unwrap();
                if discount_usage < &policy.use_limit {
                    discount_ten_roll_prize = policy.discounted_prize;
                    discount_avaliable_num = policy.use_limit - discount_usage;
                }
            } else if gachaconf
                .discount_policies
                .free_select_map
                .contains_key(discount_policy_tag)
            {
                let policy = gachaconf
                    .discount_policies
                    .free_select_map
                    .get(discount_policy_tag)
                    .unwrap();
                let free_select_demand_idx = usize::try_from(
                    *(status_bin
                        .discount_usage_map
                        .get(&policy.free_select_usage_record_tag)
                        .unwrap()),
                )
                .unwrap();
                if policy.milestones.len() <= free_select_demand_idx {
                    continue;
                }

                let free_select_actual_progress = status_bin
                    .discount_usage_map
                    .get(&policy.free_select_progress_record_tag)
                    .unwrap();
                free_select_policy = Some(policy);
                free_select_required_pull = policy
                    .milestones
                    .get(free_select_demand_idx)
                    .unwrap()
                    .to_owned();
                free_select_progress = min(free_select_required_pull, *free_select_actual_progress);
            }
        }

        let mut up_s_item_list: Vec<u32> = vec![];
        let mut up_a_item_list: Vec<u32> = vec![];
        let mut free_select_item_list: Vec<u32> = vec![];
        let mut chooseable_up_list: Vec<u32> = vec![];
        let mut chosen_up_item: u32 = 0;
        let mut s_guarantee: u32 = 0;
        let mut a_guarantee: u32 = 0;
        for rarity_items in target_pool.gacha_items.iter() {
            let mut chooseable_up_included_category_tags: Option<&HashSet<String>> = None;
            let mut chooseable_policy_tag: Option<&String> = None;
            for guarantee_policy_tag in rarity_items.category_guarantee_policy_tags.iter() {
                let category_guarantee_policy = gachaconf
                    .category_guarantee_policy_map
                    .get(guarantee_policy_tag)
                    .unwrap();
                if !category_guarantee_policy.chooseable {
                    continue;
                }
                chooseable_policy_tag = Some(guarantee_policy_tag);
                chooseable_up_included_category_tags =
                    Some(&category_guarantee_policy.included_category_tags);
                if let Some(item) = status_bin
                    .rarity_status_map
                    .get(&rarity_items.rarity)
                    .unwrap()
                    .categories_chosen_guarantee_item_map
                    .get(guarantee_policy_tag)
                {
                    chosen_up_item = item.clone();
                }
            }

            for (category_tag, category) in rarity_items.categories.iter() {
                let probability_model = gachaconf
                    .probability_model_map
                    .get(&rarity_items.probability_model_tag)
                    .unwrap();
                let maximum_pity = &probability_model.maximum_guarantee_pity;
                if rarity_items.rarity == common_properties.s_item_rarity {
                    if category.is_promotional_items {
                        up_s_item_list = category.item_ids.clone();
                    }
                    // tracing::info!("pity_s: {pity_s}");
                    // thread 'tokio-runtime-worker' panicked at nap_gameserver\src\handlers\gacha.rs:369:31:
                    // attempt to subtract with overflow
                    s_guarantee = maximum_pity - min(pity_s, maximum_pity.clone()) + 1;
                }
                if rarity_items.rarity == common_properties.a_item_rarity {
                    if category.is_promotional_items {
                        up_a_item_list = category.item_ids.clone();
                    }
                    // tracing::info!("pity_a: {pity_a}");
                    a_guarantee = maximum_pity - min(pity_a, maximum_pity.clone()) + 1;
                }

                if let Some(val) = free_select_policy {
                    if val.rarity == rarity_items.rarity && val.category_tags.contains(category_tag)
                    {
                        free_select_item_list.append(&mut category.item_ids.clone());
                    }
                }

                if let Some(tags) = chooseable_up_included_category_tags {
                    if tags.contains(category_tag) {
                        chooseable_up_list.append(&mut category.item_ids.clone());
                    }
                }
            }

            if let Some(_priority_policy_tag) = chooseable_policy_tag {
                // if let Some(item) = status_bin
                //     .rarity_status_map
                //     .get(&rarity_items.rarity)
                //     .unwrap()
                //     .categories_chosen_guarantee_item_map
                //     .get(priority_policy_tag)
                // {
                if rarity_items.rarity == gachaconf.common_properties.s_item_rarity {
                    up_s_item_list = chooseable_up_list.clone();
                } else if rarity_items.rarity == gachaconf.common_properties.a_item_rarity {
                    up_a_item_list = vec![];
                }
                // }
            }
        }

        let need_item_info_list: Vec<NeedItemInfo> = vec![NeedItemInfo {
            need_item_id: target_pool.cost_item_id,
            need_item_count: 1,
        }];

        let mut result = Gacha {
            gacha_schedule_id: target_pool.gacha_schedule_id,
            gacha_parent_schedule_id: target_pool.gacha_parent_schedule_id,
            gacha_type: target_pool.gacha_type,
            start_timestamp: target_pool.start_time.timestamp(),
            end_timestamp: target_pool.end_time.timestamp(),
            discount_avaliable_num,
            discount_ten_roll_prize,
            advanced_s_guarantee,
            s_guarantee,
            a_guarantee,
            need_item_info_list,
            free_select_progress,
            free_select_required_pull,
            free_select_item_list,
            chosen_up_item,
            // nammdglepbk: 563,
            // hgmcofcjmbg: 101,
            // akggbhgkifd: chooseable_up_list.clone(),
            chooseable_up_list,
            ..Gacha::default()
        };
        if up_s_item_list.len() > 0 {
            result.up_s_item_list = up_s_item_list;
        }
        if up_a_item_list.len() > 0 {
            result.up_a_item_list = up_a_item_list;
        }
        result
    }

    /// Get the actual item cost count (counting discount).
    pub fn get_actual_cost_count<'bin, 'conf>(
        &'bin mut self,
        target_pool: &'conf CharacterGachaPool,
        pull_count: &u32,
    ) -> u32 {
        let gachaconf = global_gacha_config();

        if *pull_count == 10 {
            let discount_tag = &gachaconf.common_properties.ten_pull_discount_tag;
            if target_pool.discount_policy_tags.contains(&discount_tag) {
                let status_bin = self
                    .gacha_status_map
                    .get_mut(&target_pool.sharing_guarantee_info_category)
                    .unwrap();
                let discount_policy = gachaconf
                    .discount_policies
                    .ten_pull_discount_map
                    .get(discount_tag)
                    .unwrap();
                let usage = status_bin.discount_usage_map.get_mut(discount_tag).unwrap();
                if *usage < discount_policy.use_limit {
                    *usage += 1;
                    return discount_policy.discounted_prize;
                }
            }
        }

        pull_count.clone()
    }

    pub fn request_free_agent<'bin, 'conf>(
        &'bin mut self,
        target_pool: &'conf CharacterGachaPool,
        item_id: &ItemID,
    ) -> GachaAddedItemType {
        let gachaconf = global_gacha_config();
        let sharing_guarantee_category_tag = &target_pool.sharing_guarantee_info_category;
        let status_bin = self
            .gacha_status_map
            .get_mut(sharing_guarantee_category_tag)
            .unwrap();
        let item_id = item_id.value();

        let mut free_select_policy: Option<&FreeSelectItem> = None;
        let mut free_select_progress: u32 = 0;
        let mut free_select_required_pull: u32 = 0;
        for discount_policy_tag in target_pool.discount_policy_tags.iter() {
            if gachaconf
                .discount_policies
                .free_select_map
                .contains_key(discount_policy_tag)
            {
                let policy = gachaconf
                    .discount_policies
                    .free_select_map
                    .get(discount_policy_tag)
                    .unwrap();
                let free_select_demand_idx = usize::try_from(
                    *(status_bin
                        .discount_usage_map
                        .get(&policy.free_select_usage_record_tag)
                        .unwrap()),
                )
                .unwrap();
                if policy.milestones.len() <= free_select_demand_idx {
                    continue;
                }

                let free_select_actual_progress = status_bin
                    .discount_usage_map
                    .get(&policy.free_select_progress_record_tag)
                    .unwrap();
                free_select_policy = Some(policy);
                free_select_required_pull = policy
                    .milestones
                    .get(free_select_demand_idx)
                    .unwrap()
                    .to_owned();
                free_select_progress = min(free_select_required_pull, *free_select_actual_progress);
            }
        }

        if let None = free_select_policy {
            tracing::info!(
                "refuse free agent because: pool of parent_schedule_id {} hasn't defined free agent discount yet (or used up chance)",
                target_pool.gacha_parent_schedule_id
            );
            return GachaAddedItemType::None;
        } else if free_select_progress < free_select_required_pull {
            tracing::info!(
                "refuse free agent because: use pulled {free_select_progress} (after last free agent) in parent_schedule_id {}, required {free_select_required_pull}",
                target_pool.gacha_parent_schedule_id
            );
            return GachaAddedItemType::None;
        }

        let free_select_policy = free_select_policy.unwrap();
        let mut item_type: GachaAddedItemType = GachaAddedItemType::None;
        for rarity_items in target_pool.gacha_items.iter() {
            if rarity_items.rarity != free_select_policy.rarity {
                continue;
            }
            for (category_tag, category) in rarity_items.categories.iter() {
                if !free_select_policy.category_tags.contains(category_tag) {
                    continue;
                }
                if category.item_ids.contains(&item_id) {
                    item_type = category.item_type.clone();
                }
            }
        }

        if item_type != GachaAddedItemType::None {
            (*status_bin
                .discount_usage_map
                .get_mut(&free_select_policy.free_select_usage_record_tag)
                .unwrap()) += 1;
            (*status_bin
                .discount_usage_map
                .get_mut(&free_select_policy.free_select_progress_record_tag)
                .unwrap()) -= free_select_required_pull;
        }
        item_type
    }

    pub fn choose_gacha_up<'bin, 'conf>(
        &'bin mut self,
        target_pool: &'conf CharacterGachaPool,
        item_id: &ItemID,
    ) -> bool {
        let gachaconf = global_gacha_config();
        let item_id = item_id.value();
        for rarity_items in target_pool.gacha_items.iter() {
            for guarantee_policy_tag in rarity_items.category_guarantee_policy_tags.iter() {
                let category_guarantee_policy = gachaconf
                    .category_guarantee_policy_map
                    .get(guarantee_policy_tag)
                    .unwrap();
                if !category_guarantee_policy.chooseable {
                    continue;
                }
                let mut up_category: Option<&String> = None;
                for (category_tag, category) in rarity_items.categories.iter() {
                    if category.item_ids.contains(&item_id) {
                        up_category = Some(category_tag);
                        break;
                    }
                }
                if let None = up_category {
                    continue;
                };
                let up_category = up_category.unwrap();

                let progress_bin = self
                    .gacha_status_map
                    .get_mut(&target_pool.sharing_guarantee_info_category)
                    .unwrap()
                    .rarity_status_map
                    .get_mut(&rarity_items.rarity)
                    .unwrap();
                match progress_bin
                    .categories_chosen_guarantee_item_map
                    .entry(guarantee_policy_tag.clone())
                {
                    Occupied(mut occupied_entry) => {
                        occupied_entry.insert(item_id);
                    }
                    Vacant(vacant_entry) => {
                        vacant_entry.insert(item_id);
                    }
                };
                match progress_bin
                    .categories_chosen_guarantee_category_map
                    .entry(up_category.clone())
                {
                    Occupied(mut occupied_entry) => {
                        occupied_entry.insert(up_category.clone());
                    }
                    Vacant(vacant_entry) => {
                        vacant_entry.insert(up_category.clone());
                    }
                };
                return true;
            }
        }

        false
    }
}
