use itertools::Itertools;
use qwer::{phashmap, phashset, PropertyHashMap, PropertyHashSet};
use std::collections::HashMap;

use crate::data;

use super::*;

pub async fn on_rpc_hollow_move(
    session: &mut NetworkSession,
    arg: &RpcHollowMoveArg,
) -> Result<RpcHollowMoveRet> {
    tracing::info!("Hollow movement {:?}", &arg);

    let destination_pos = *arg.positions.last().unwrap();
    let scene_uid = session.ns_prop_mgr.player_info.read().scene_uid.unwrap();

    let (ptc_hollow_grid, ptc_sync_hollow_event) = session
        .context
        .hollow_grid_manager
        .move_to(destination_pos, scene_uid);

    session
        .push_rpc_arg(PTC_HOLLOW_GRID_ID, ptc_hollow_grid)
        .await?;

    if let Some(ptc_sync_hollow_event) = ptc_sync_hollow_event {
        session
            .push_rpc_arg(PTC_SYNC_HOLLOW_EVENT_INFO_ID, ptc_sync_hollow_event)
            .await?;
    }

    let pos = PtcPositionInHollowChangedArg {
        player_uid: session.player_uid().raw(),
        hollow_level: arg.hollow_level,
        position: destination_pos,
    };

    session
        .push_rpc_arg(PTC_POSITION_IN_HOLLOW_CHANGED_ID, pos)
        .await?;

    let ptc_hollow_grid_around = session
        .context
        .hollow_grid_manager
        .check_around(destination_pos, scene_uid);
    
    session
        .push_rpc_arg(PTC_HOLLOW_GRID_ID, ptc_hollow_grid_around)
        .await?;

    Ok(RpcHollowMoveRet::new(
        arg.hollow_level,
        *arg.positions.last().unwrap(),
    ))
}

pub async fn on_rpc_run_hollow_event_graph(
    session: &mut NetworkSession,
    arg: &RpcRunHollowEventGraphArg,
) -> Result<RpcRunHollowEventGraphRet> {
    tracing::info!("Run hollow event graph {:?}", arg);

    let cur_hollow = session
        .context
        .hollow_grid_manager
        .get_cur_chessboard_id();

    let start_position = session
        .context
        .hollow_grid_manager
        .get_cur_position_in_hollow();

    let event_on_hollow_start: i32;
    
    if cur_hollow == 2020701 { event_on_hollow_start = 1000108; }
    else { event_on_hollow_start = 1000; }

    let scene_uid = session.ns_prop_mgr.player_info.read().scene_uid.unwrap();

    if arg.event_graph_uid == 3405096459205834 {
        // Perform event at the beginning of a level
        let finish_perform = PtcSyncHollowEventInfoArg {
            event_graph_uid: 3405096459205834,
            hollow_event_template_id: event_on_hollow_start,
            event_graph_id: event_on_hollow_start,
            updated_event: EventInfo {
                id: 1000,
                cur_action_id: -1,
                action_move_path: vec![1001, 1002, -1],
                state: EventState::Finished,
                prev_state: EventState::Running,
                cur_action_info: ActionInfo::None {},
                cur_action_state: ActionState::Init,
                predicated_failed_actions: phashset![],
                stack_frames: Vec::new(),
            },
            specials: phashmap![],
        };
        session
            .push_rpc_arg(PTC_SYNC_HOLLOW_EVENT_INFO_ID, finish_perform)
            .await?;

        let (ptc_hollow_grid, ptc_sync_hollow_event) =
            session.context.hollow_grid_manager.move_to(start_position, scene_uid);

        session
            .push_rpc_arg(PTC_HOLLOW_GRID_ID, ptc_hollow_grid)
            .await?;
        if let Some(ptc_sync_hollow_event) = ptc_sync_hollow_event {
            session
                .push_rpc_arg(PTC_SYNC_HOLLOW_EVENT_INFO_ID, ptc_sync_hollow_event)
                .await?;
        }
   
    } else {
        let (sync_hollow_event, hollow_grid, trigger_battle_id, hollow_finished) = session
            .context
            .hollow_grid_manager
            .run_event_graph(arg.event_graph_uid, arg.event_id, arg.move_path.clone());

        if !hollow_finished {
            session
                .push_rpc_arg(PTC_SYNC_HOLLOW_EVENT_INFO_ID, sync_hollow_event)
                .await?;
        }
        session
            .push_rpc_arg(PTC_HOLLOW_GRID_ID, hollow_grid)
            .await?;

        if hollow_finished {
            let _ = session
                .context
                .dungeon_manager
                .hollow_finished()
                .send_changes(session)
                .await?;

            let ptc_dungeon_quest_finished = PtcDungeonQuestFinishedArg {
                player_uid: session.player_uid().raw(),
                quest_id: 1001000102,
                success: true,
                reward_items: phashmap![],
                statistics: phashmap![(QuestStatisticsType::ArrivedLevel, 1)],
            };

            session
                .push_rpc_arg(PTC_DUNGEON_QUEST_FINISHED_ID, ptc_dungeon_quest_finished)
                .await?;
        }

        if let Some(trigger_battle_id) = trigger_battle_id {
            let hollow_uid = *session
                .ns_prop_mgr
                .player_info
                .read()
                .scene_uid
                .as_ref()
                .unwrap();
            let battle_scene_uid = session
                .context
                .dungeon_manager
                .create_fight(trigger_battle_id, hollow_uid)
                .send_changes(session)
                .await?;

            let ptc_position_in_hollow_changed = PtcPositionInHollowChangedArg {
                player_uid: session.player_uid().raw(),
                hollow_level: 1,
                position: session
                    .context
                    .hollow_grid_manager
                    .get_cur_position_in_hollow(),
            };

            session
                .push_rpc_arg(
                    PTC_POSITION_IN_HOLLOW_CHANGED_ID,
                    ptc_position_in_hollow_changed,
                )
                .await?;

            session
                .push_rpc_arg(
                    PTC_ENTER_SCENE_ID,
                    session
                        .context
                        .dungeon_manager
                        .enter_battle(battle_scene_uid)
                        .send_changes(session)
                        .await?,
                )
                .await?;
        }
    }

    Ok(RpcRunHollowEventGraphRet::new())
}

pub async fn on_rpc_start_hollow_quest(
    session: &NetworkSession,
    arg: &RpcStartHollowQuestArg,
) -> Result<RpcStartHollowQuestRet> {
    tracing::info!("start hollow quest: {arg:?}");
    

    // Set avatar HP properties
    for (_idx, avatar_uid) in &arg.avatar_map {
        let update_properties = {
            let player_info = session.ns_prop_mgr.player_info.read();
            let items = player_info.items.as_ref().unwrap();
            let Some(ItemInfo::Avatar { id, .. }) = items
                .iter()
                .find(|(uid, _)| **uid == *avatar_uid)
                .map(|(_, item)| item)
            else {
                return Ok(RpcStartHollowQuestRet::error(
                    ErrorCode::ObjectNotExist,
                    Vec::new(),
                ));
            };

            let avatar_config = data::iter_avatar_config_collection()
                .find(|c| c.id == *id)
                .unwrap();

            PtcPropertyChangedArg {
                scene_unit_uid: *avatar_uid,
                is_partial: true,
                changed_properties: phashmap![(1, avatar_config.hp), (111, avatar_config.hp)],
            }
        };

        session
            .push_rpc_arg(PTC_PROPERTY_CHANGED_ID, update_properties)
            .await?;
    }

    let avatars = arg
        .avatar_map
        .iter()
        .sorted_by_key(|kv| kv.0)
        .map(|(_idx, uid)| *uid)
        .collect::<Vec<_>>();

    //time of day
    let hour: u64;
    //check for current hollow quest
    let hollow_quest = arg.hollow_quest_id;
    //level
    let level: u8;
    //hollow id
    let hollow_id: i32;

    //Quest List:

    if hollow_quest == 10010106 {             //The light of day (Rescue)
        hour = 18; //Set in-game time to 6 PM
        hollow_id = 10112;
        level = 2;

    } else if hollow_quest == 10010102 {      //Lost and found
        hour = 23; //Set in-game time to 11 PM
        hollow_id = 10102;
        level = 3;

    } else if hollow_quest == 10010103 {      //Engulfed by the Hollow (Time Trial)
        hour = 11; //Set in-game time to 11 AM
        hollow_id = 10103;
        level = 4;

    } else if hollow_quest == 1990035 {      //Descent (High-Risk 1)
        hour = 23; //Set in-game time to 11 PM
        hollow_id = 19935;
        level = 5;

    } else {                     //Prologue ("Where Despair Breeds" is used because trial units don't work), loads by default
        hour = 8; //Set in-game time to 8 AM
        hollow_id = 10001;
        level = 1;
    }

    //Set in-game time
        session
        .push_rpc_arg(
            PTC_SYNC_SCENE_TIME_ID,
            PtcSyncSceneTimeArg {
                timestamp: 3600 * hour * 1000,
                last_timestamp: 0,
            },
        )
        .await?;

        let (dungeon_uid, scene_uid) = session
            .context
            .dungeon_manager
            .create_hollow(hollow_id, hollow_quest, &avatars)
            .send_changes(session)
            .await?;

        session
            .context
            .quest_manager
            .add_quest_to_collection(
                dungeon_uid,
                QuestInfo::DungeonInner {
                    id: 1001000102,
                    finished_count: 0,
                    collection_uid: 0,
                    progress: 0,
                    parent_quest_id: hollow_quest,
                    state: QuestState::InProgress,
                    finish_condition_progress: phashmap![],
                    progress_time: 2111605,
                    sort_id: 2000,
                },
            )
            .send_changes(session)
            .await?;

        let ptc_enter_scene = session
            .context
            .dungeon_manager
            .enter_scene(scene_uid)?
            .send_changes(session)
            .await?
            .clone();

        if level == 1 {
            session.context.hollow_grid_manager.init_prologue();
        } else if level == 2 {
            session.context.hollow_grid_manager.init_rescue();
        } else if level == 3 {
            session.context.hollow_grid_manager.init_lost();
        } else if level == 4 {
            session.context.hollow_grid_manager.init_time_trial();
        }  else if level == 5 {
            session.context.hollow_grid_manager.init_descent();
        }


    session
        .push_rpc_arg(
            PTC_SYNC_HOLLOW_GRID_MAPS_ID,
            session
                .context
                .hollow_grid_manager
                .sync_hollow_maps(session.player_uid().raw(), scene_uid),
        )
        .await?;

    let ptc_position_in_hollow_changed = PtcPositionInHollowChangedArg {
        player_uid: session.player_uid().raw(),
        hollow_level: 1,
        position: session
            .context
            .hollow_grid_manager
            .get_cur_position_in_hollow(),
    };

    session
        .push_rpc_arg(
            PTC_POSITION_IN_HOLLOW_CHANGED_ID,
            ptc_position_in_hollow_changed,
        )
        .await?;

    let event_on_hollow_start: i32;
    
    if level == 1 { event_on_hollow_start = 1000108; }
    else { event_on_hollow_start = 1000; }
    
    let ptc_sync_hollow_event_info = PtcSyncHollowEventInfoArg {
        event_graph_uid: 3405096459205834,
        hollow_event_template_id: event_on_hollow_start,
        event_graph_id: event_on_hollow_start,
        updated_event: EventInfo {
            id: 1000,
            cur_action_id: 1001,
            action_move_path: vec![1001],
            state: EventState::WaitingClient,
            prev_state: EventState::Running,
            cur_action_info: ActionInfo::None {},
            cur_action_state: ActionState::Init,
            predicated_failed_actions: phashset![],
            stack_frames: Vec::new(),
        },
        specials: phashmap![],
    };

    session
        .push_rpc_arg(PTC_SYNC_HOLLOW_EVENT_INFO_ID, ptc_sync_hollow_event_info)
        .await?;

    session
        .push_rpc_arg(PTC_ENTER_SCENE_ID, ptc_enter_scene)
        .await?;



    Ok(RpcStartHollowQuestRet::new())

}

pub async fn on_rpc_end_battle(
    session: &NetworkSession,
    arg: &RpcEndBattleArg,
) -> Result<RpcEndBattleRet> {
    tracing::info!("RpcEndBattle: {:?}", &arg);

    let player_uid = session.player_uid().raw();
    let (sync_event, hollow_finished) = session.context.hollow_grid_manager.battle_finished();

    if arg.success != true {
        let _ = session
            .context
            .dungeon_manager
            .hollow_finished()
            .send_changes(session)
            .await?;

            let ptc_dungeon_quest_finished = PtcDungeonQuestFinishedArg {
                player_uid,
                quest_id: 1001000102,
                success: true,
                reward_items: phashmap![],
                statistics: phashmap![(QuestStatisticsType::ArrivedLevel, 1)],
            };

        session
            .push_rpc_arg(PTC_DUNGEON_QUEST_FINISHED_ID, ptc_dungeon_quest_finished)
            .await?;
    }
        
    if !hollow_finished {
        session
            .push_rpc_arg(PTC_SYNC_HOLLOW_EVENT_INFO_ID, sync_event)
            .await?;
    } else {
        let _ = session
            .context
            .dungeon_manager
            .hollow_finished()
            .send_changes(session)
            .await?;

            let ptc_dungeon_quest_finished = PtcDungeonQuestFinishedArg {
                player_uid,
                quest_id: 1001000102,
                success: true,
                reward_items: phashmap![],
                statistics: phashmap![(QuestStatisticsType::ArrivedLevel, 1)],
            };

        session
            .push_rpc_arg(PTC_DUNGEON_QUEST_FINISHED_ID, ptc_dungeon_quest_finished)
            .await?;
    }

    let ptc_enter_scene = session
        .context
        .dungeon_manager
        .leave_battle()
        .unwrap()
        .send_changes(session)
        .await?
        .clone();

    session
        .push_rpc_arg(
            PTC_SYNC_HOLLOW_GRID_MAPS_ID,
            session.context.hollow_grid_manager.sync_hollow_maps(
                player_uid,
                session.context.dungeon_manager.get_cur_scene_uid(),
            ),
        )
        .await?;

    let ptc_position_in_hollow_changed = PtcPositionInHollowChangedArg {
        player_uid,
        hollow_level: 1,
        position: session
            .context
            .hollow_grid_manager
            .get_cur_position_in_hollow(),
    };

    session
        .push_rpc_arg(
            PTC_POSITION_IN_HOLLOW_CHANGED_ID,
            ptc_position_in_hollow_changed,
        )
        .await?;

    session
        .push_rpc_arg(PTC_ENTER_SCENE_ID, ptc_enter_scene)
        .await?;

    Ok(RpcEndBattleRet::new(
        session
            .context
            .hollow_grid_manager
            .get_cur_event_template_id(),
        HashMap::new(),
    ))
}