use super::MessageContext;
use trigger_codegen::handlers;

#[handlers]
mod scene_module {
    use tracing::debug;
    use trigger_logic::scene::{ELocalPlayType, ESceneType};
    use trigger_sv::{
        message::{AvailableServerProtocolMessage, GameStateData},
        net::ServerType,
    };

    use crate::logic::{dungeon_util, scene::ServerlessStateData, scene_util};

    pub async fn on_enter_world(context: &mut MessageContext<'_, '_>, _request: EnterWorldCsReq) {
        if let Some(procedure_id) = context.player.beginner_procedure_id() {
            debug!("entering beginner procedure, id: {procedure_id}");

            context
                .session
                .load_serverless_state(
                    ServerlessStateData::FreshScene { procedure_id },
                    context.request_id,
                    EnterWorldScRsp::default(),
                )
                .await;
            return;
        }

        let scene_model = &mut context.player.scene_model;

        let scene_to_enter = match scene_model.get_current_scene().await {
            Some(current_scene)
                if scene_util::persists_on_relogin(
                    ESceneType::try_from(current_scene.scene_type).unwrap(),
                ) =>
            {
                Some(current_scene)
            }
            _ => scene_model.get_default_scene().await,
        };

        let scene_to_enter = if scene_to_enter.is_none() {
            debug!(
                "player with uid {} has no scene to enter, default hall scene will be created",
                context.session.player_uid
            );

            let scene = scene_model.create_scene_info(ESceneType::Hall).await;
            scene_model.set_default_scene(&scene).await;

            scene
        } else {
            scene_to_enter.unwrap()
        };

        context
            .session
            .change_game_state(
                context.request_id,
                EnterWorldScRsp { retcode: 0 },
                context
                    .player
                    .build_state_reentrant_data(&scene_to_enter)
                    .unwrap(),
                &scene_to_enter,
                context.player,
                false,
            )
            .await;

        context.player.scene_model.clear_abandoned_scenes().await;
    }

    pub async fn on_post_enter_world(
        _context: &mut MessageContext<'_, '_>,
        _request: PostEnterWorldCsReq,
    ) -> PostEnterWorldScRsp {
        PostEnterWorldScRsp { retcode: 0 }
    }

    pub async fn on_scene_transition(
        _context: &mut MessageContext<'_, '_>,
        request: SceneTransitionCsReq,
    ) -> SceneTransitionScRsp {
        debug!("{request:?}");

        SceneTransitionScRsp { retcode: 0 }
    }

    pub async fn on_enter_section_complete(
        _context: &mut MessageContext<'_, '_>,
        _request: EnterSectionCompleteCsReq,
    ) -> EnterSectionCompleteScRsp {
        EnterSectionCompleteScRsp { retcode: 0 }
    }

    pub async fn on_refresh_section(
        _context: &mut MessageContext<'_, '_>,
        _request: RefreshSectionCsReq,
    ) -> RefreshSectionScRsp {
        RefreshSectionScRsp {
            retcode: 0,
            refresh_status: 0,
        }
    }

    pub async fn on_enter_training_room(
        context: &mut MessageContext<'_, '_>,
        request: EnterTrainingRoomCsReq,
    ) {
        let scene_model = &mut context.player.scene_model;
        let scene_info = scene_model.create_scene_info(ESceneType::Fight).await;

        let dungeon_equip =
            dungeon_util::build_dungeon_equip_info(&context.player, &request.avatar_id_list);

        context
            .session
            .change_game_state(
                context.request_id,
                EnterTrainingRoomScRsp { retcode: 0 },
                GameStateData::Fight {
                    play_type: ELocalPlayType::TrainingRoom.into(),
                    quest_id: request.quest_id,
                    buddy_id: request.buddy_id,
                    avatar_id_list: request.avatar_id_list,
                    dungeon_equip,
                },
                &scene_info,
                context.player,
                true,
            )
            .await;
    }

    pub async fn on_leave_cur_scene(
        context: &mut MessageContext<'_, '_>,
        _request: LeaveCurSceneCsReq,
    ) {
        if let Some(scene_uid) = context.session.get_cur_scene_uid() {
            context.player.scene_model.on_leave_scene(scene_uid).await;
        }

        let default_scene = context
            .player
            .scene_model
            .get_default_scene()
            .await
            .unwrap();

        context
            .session
            .change_game_state(
                context.request_id,
                LeaveCurSceneScRsp { retcode: 0 },
                context
                    .player
                    .build_state_reentrant_data(&default_scene)
                    .unwrap(),
                &default_scene,
                context.player,
                false,
            )
            .await;
    }

    pub async fn on_advance_beginner_procedure(
        context: &mut MessageContext<'_, '_>,
        request: AdvanceBeginnerProcedureCsReq,
    ) {
        let rsp = if let Some(procedure_id) = context.player.beginner_procedure_id() {
            if request.procedure_id == procedure_id + 1 {
                context
                    .player
                    .advance_beginner_procedure(request.procedure_id)
                    .await;

                let rsp = AdvanceBeginnerProcedureScRsp {
                    retcode: 0,
                    next_procedure_id: request.procedure_id,
                };

                if context.player.beginner_procedure_id().is_none() {
                    // Beginner procedure finished, enter the hall scene now
                    let scene_model = &mut context.player.scene_model;
                    let scene = scene_model.create_scene_info(ESceneType::Hall).await;
                    scene_model.set_default_scene(&scene).await;

                    context
                        .session
                        .change_game_state(
                            context.request_id,
                            rsp,
                            context.player.build_state_reentrant_data(&scene).unwrap(),
                            &scene,
                            context.player,
                            false,
                        )
                        .await;

                    None
                } else {
                    Some(rsp)
                }
            } else {
                debug!(
                    "invalid procedure transition (from: {}, to: {})",
                    procedure_id, request.procedure_id
                );

                Some(AdvanceBeginnerProcedureScRsp {
                    retcode: 1,
                    ..Default::default()
                })
            }
        } else {
            Some(AdvanceBeginnerProcedureScRsp {
                retcode: 1,
                ..Default::default()
            })
        };

        if let Some(rsp) = rsp {
            context
                .session
                .network_mgr
                .send_to(
                    ServerType::GateServer,
                    0,
                    AvailableServerProtocolMessage {
                        session_id: context.session.id,
                        ack_request_id: context.request_id,
                        notifies: Vec::new(),
                        response: Some(rsp.into()),
                    },
                )
                .await;
        }
    }

    pub async fn on_beginnerbattle_begin(
        _context: &mut MessageContext<'_, '_>,
        request: BeginnerbattleBeginCsReq,
    ) -> BeginnerbattleBeginScRsp {
        BeginnerbattleBeginScRsp {
            retcode: 0,
            battle_uid: request.battle_id as i64,
        }
    }

    pub async fn on_beginnerbattle_rebegin(
        _context: &mut MessageContext<'_, '_>,
        _request: BeginnerbattleRebeginCsReq,
    ) -> BeginnerbattleRebeginScRsp {
        BeginnerbattleRebeginScRsp { retcode: 0 }
    }

    pub async fn on_beginnerbattle_end(
        _context: &mut MessageContext<'_, '_>,
        _request: BeginnerbattleEndCsReq,
    ) -> BeginnerbattleEndScRsp {
        BeginnerbattleEndScRsp { retcode: 0 }
    }

    pub async fn on_active_hollow_check_point(
        _context: &mut MessageContext<'_, '_>,
        request: ActiveHollowCheckPointCsReq,
    ) -> ActiveHollowCheckPointScRsp {
        // TODO: forward it to battle-server and actually handle

        debug!("{:?}", request.check_point);
        ActiveHollowCheckPointScRsp { retcode: 0 }
    }
}
