use data::{
    gacha::{gacha_config::*, global_gacha_config},
    tables::{AvatarBaseID, WeaponID},
};

use super::*;
use chrono::{DateTime, Local};
use proto::*;

pub async fn on_get_gacha_data(
    _session: &NetSession,
    _player: &mut Player,
    req: GetGachaDataCsReq,
) -> NetResult<GetGachaDataScRsp> {
    if req.gacha_type != 3 {
        // tracing::info!("non-supported gacha type {}", body.gacha_type);
        Ok(GetGachaDataScRsp {
            retcode: Retcode::RetSucc.into(),
            gacha_type: req.gacha_type,
            gacha_data: Some(GachaData::default()),
        })
    } else {
        // tracing::info!("construct gacha info");
        Ok(GetGachaDataScRsp {
            retcode: Retcode::RetSucc.into(),
            gacha_type: req.gacha_type,
            gacha_data: Some(generate_all_gacha_info(_player)),
        })
    }
}

pub async fn on_do_gacha(
    _session: &NetSession,
    _player: &mut Player,
    req: DoGachaCsReq,
) -> NetResult<DoGachaScRsp> {
    let gachaconf = global_gacha_config();
    let gacha_model = &mut _player.gacha_model;
    let item_model = &mut _player.item_model;
    let role_model = &mut _player.role_model;
    let pull_time = Local::now();
    let target_pool = get_gacha_pool(
        &gachaconf.character_gacha_pool_list,
        &req.gacha_parent_schedule_id,
        &pull_time,
    );
    if let None = target_pool {
        return Ok(DoGachaScRsp {
            retcode: Retcode::RetSucc.into(),
            ..Default::default()
        });
    };
    let target_pool = target_pool.unwrap();

    // tracing::info!("cost_item_count: {}", req.cost_item_count);
    let mut pull_count = if req.cost_item_count > 1 { 10 } else { 1 };
    let mut cost_count = pull_count;

    if pull_count == 10 {
        let discount_tag = &gachaconf.common_properties.ten_pull_discount_tag;
        if target_pool.discount_policy_tags.contains(&discount_tag) {
            let gacha_bin = &mut gacha_model.gacha_bin;
            let status_bin = gacha_bin
                .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;
                cost_count = discount_policy.discounted_prize;
            }
        }
    }
    if cost_count != req.cost_item_count {
        return Ok(DoGachaScRsp {
            retcode: Retcode::RetFail.into(),
            ..Default::default()
        });
    } else {
        // TODO: cost resource
    }

    let mut gain_item_list: Vec<GainItemInfo> = vec![];
    while pull_count > 0 {
        let pull_result = gacha_model.perform_pull_pool(&pull_time, target_pool);
        let extra_item_bin = pull_result.extra_item_bin.unwrap();
        let uid = match GachaAddedItemType::try_from(pull_result.item_type) {
            Ok(enum_val) => match enum_val {
                GachaAddedItemType::Weapon => match WeaponID::new(pull_result.obtained_item_id) {
                    Some(id) => item_model.add_weapon(id).value(),
                    None => 0,
                },
                GachaAddedItemType::Character => {
                    match AvatarBaseID::new(pull_result.obtained_item_id) {
                        Some(id) => {
                            role_model.add_avatar(id);
                            0
                        }
                        None => 0,
                    }
                }
                _ => 0,
            },
            Err(_err) => 0,
        };
        if extra_item_bin.extra_item_id != 0 {
            item_model.add_resource(
                extra_item_bin.extra_item_id,
                extra_item_bin.extra_item_count,
            );
        }
        gain_item_list.push(GainItemInfo {
            item_id: pull_result.obtained_item_id,
            extra_item_id: extra_item_bin.extra_item_id,
            extra_item_count: extra_item_bin.extra_item_count,
            uid,
            num: 1,
            ..GainItemInfo::default()
        });
        pull_count -= 1;
    }

    Ok(DoGachaScRsp {
        retcode: Retcode::RetSucc.into(),
        gain_item_list,
        gacha_data: Some(generate_all_gacha_info(_player)),
        cost_item_count: req.cost_item_count,
    })
}

fn generate_gacha_info_from_pool(
    gacha_bin: &GachaModelBin,
    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 = gacha_bin
        .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 up_s_item_list: Vec<u32> = vec![];
    let mut up_a_item_list: Vec<u32> = vec![];
    let mut s_guarantee: u32 = 0;
    let mut a_guarantee: u32 = 0;
    for rarity_items in target_pool.gacha_items.iter() {
        for category in rarity_items.categories.values() {
            let probability_model = gachaconf
                .probability_model_map
                .get(&rarity_items.probability_model_tag)
                .unwrap();
            if rarity_items.rarity == common_properties.s_item_rarity {
                if category.is_promotional_items {
                    up_s_item_list = category.item_ids.clone();
                }
                s_guarantee = probability_model.maximum_guarantee_pity - pity_s + 1;
            }
            if rarity_items.rarity == common_properties.a_item_rarity {
                if category.is_promotional_items {
                    up_a_item_list = category.item_ids.clone();
                }
                a_guarantee = probability_model.maximum_guarantee_pity - pity_a + 1;
            }
        }
    }

    let mut discount_ten_roll_prize: u32 = 0;
    let mut discount_avaliable_num: u32 = 0;
    let mut advanced_s_guarantee: u32 = 0;
    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;
            }
        }
    }

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

    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,
        up_s_item_list,
        up_a_item_list,
        advanced_s_guarantee,
        s_guarantee,
        a_guarantee,
        need_item_info_list,
        lpklhoobkbh: target_pool.gacha_parent_schedule_id,
        nammdglepbk: 593,
        hgmcofcjmbg: 101,
        ..Gacha::default()
    }
}

fn generate_all_gacha_info(_player: &Player) -> GachaData {
    let gachaconf = global_gacha_config();
    let gacha_bin = &_player.gacha_model.gacha_bin;
    let mut gacha_list: Vec<Gacha> = vec![];
    for target_pool in gachaconf.character_gacha_pool_list.iter() {
        gacha_list.push(generate_gacha_info_from_pool(
            &gacha_bin,
            target_pool,
            &gachaconf.common_properties,
        ));
    }

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

fn get_gacha_pool<'conf>(
    character_gacha_pool_list: &'conf Vec<CharacterGachaPool>,
    gacha_parent_schedule_id: &u32,
    pull_time: &DateTime<Local>,
) -> Option<&'conf CharacterGachaPool> {
    for target_pool in character_gacha_pool_list.iter() {
        if &target_pool.gacha_parent_schedule_id == gacha_parent_schedule_id
            && target_pool.is_still_open(pull_time)
        {
            return Some(target_pool);
        }
    }
    None
}
