use common::util;
use data::tables::{self, BattleEventConfigID, HollowQuestID};
use proto::{
    DungeonInfo, DungeonItemData, FightQuestInfo, LongFightInfo, LongFightSceneInfo, SceneInfo,
    WeatherPoolInfo,
};
use thiserror::Error;

use crate::logic::{
    battle::{
        drop::FightDropPool,
        unit::{AvatarUnit, BuddyUnit},
        BuddyParam, DungeonQuestManager, LogicVariableTable, TeamDataItem,
    },
    BuddyTeamType, EHollowQuestType, ELocalPlayType, ESceneType, TimePeriodType, WeatherType,
};

use super::NapGameMode;

#[derive(Error, Debug)]
pub enum LongFightGameError {
    #[error("Tried to run quest of type {0:?} using LongFight logic")]
    InvalidQuestType(EHollowQuestType),
    #[error("Battle group not found, quest id: {0}")]
    BattleGroupNotFound(u32),
}

pub struct LongFightGame {
    pub quest_id: u32,
    pub battle_event_id: BattleEventConfigID,
    pub play_type: ELocalPlayType,
    pub time_period: TimePeriodType,
    pub weather: WeatherType,
    pub start_timestamp: i64,
    pub team_data: TeamDataItem,
    pub variable_table: LogicVariableTable,
    pub fight_drop_pool: FightDropPool,
    pub quest_manager: DungeonQuestManager,
}

impl LongFightGame {
    const RALLY_GUIDANCE_BUDDY_ID: u32 = 50001;

    pub fn create_rally_game(
        quest_id: HollowQuestID,
        avatars: &[u32],
        buddy_id: u32,
        time_period: TimePeriodType,
        weather: WeatherType,
    ) -> Result<Self, LongFightGameError> {
        let template = quest_id.template();
        let quest_type = EHollowQuestType::from(template.hollow_quest_type);

        if quest_type != EHollowQuestType::RallyBattle {
            return Err(LongFightGameError::InvalidQuestType(quest_type));
        }

        let Some(battle_group) = tables::battle_group_config_template_tb::iter()
            .find(|tmpl| tmpl.quest_id == template.id.value())
        else {
            return Err(LongFightGameError::BattleGroupNotFound(template.id.value()));
        };

        let mut buddy_params = vec![BuddyParam(
            Self::RALLY_GUIDANCE_BUDDY_ID,
            BuddyTeamType::RallyGuidance,
        )];

        if buddy_id != 0 {
            buddy_params.push(BuddyParam(buddy_id, BuddyTeamType::Fighting));
        }

        Ok(Self {
            quest_id: template.id.value(),
            battle_event_id: battle_group.battle_event_id,
            play_type: ELocalPlayType::RallyLongFight,
            time_period,
            weather,
            start_timestamp: util::cur_timestamp() as i64,
            team_data: TeamDataItem::new(avatars, &buddy_params),
            variable_table: LogicVariableTable::new(battle_group.battle_event_id),
            fight_drop_pool: FightDropPool::new(battle_group.battle_event_id),
            quest_manager: DungeonQuestManager::new_for_battle_group(battle_group.id),
        })
    }
}

impl NapGameMode for LongFightGame {
    fn scene_type(&self) -> ESceneType {
        ESceneType::LongFight
    }

    fn scene_info(&self) -> Option<SceneInfo> {
        Some(SceneInfo {
            scene_type: self.scene_type() as u32,
            battle_event_id: self.battle_event_id.value(),
            play_type: self.play_type as u32,
            long_fight_scene_info: Some(LongFightSceneInfo {
                fight_data: Some(LongFightInfo {
                    fight_quest_info: Some(FightQuestInfo {
                        fight_variable_map: self.variable_table.to_client(),
                        ..Default::default()
                    }),
                    ..Default::default()
                }),
                fight_drop_info: Some(self.fight_drop_pool.to_client()),
                weather_pool: Some(WeatherPoolInfo {
                    time_period: self.time_period.to_string(),
                    weather: self.weather.to_string(),
                    febgjinpcbp: true,
                    bejeblcfcha: true,
                    ..Default::default()
                }),
                ..Default::default()
            }),
            ..Default::default()
        })
    }

    fn dungeon_info(&self) -> Option<DungeonInfo> {
        Some(DungeonInfo {
            quest_id: self.quest_id,
            start_timestamp: self.start_timestamp,
            dungeon_item_data: Some(DungeonItemData::default()),
            avatar_list: self
                .team_data
                .avatar_member_list
                .iter()
                .map(AvatarUnit::to_client)
                .collect(),
            buddy_list: self
                .team_data
                .equipped_buddy_list
                .iter()
                .map(BuddyUnit::to_client)
                .collect(),
            dungeon_quest_info: Some(self.quest_manager.to_client()),
            ..Default::default()
        })
    }
}
