use lazy_static::lazy_static;
use prost::Message;
use tokio::sync::Mutex;

use crate::{
    net::{
        tools::{AvatarJson, JsonData, Position},
        tools_res::{PropState, GAME_RESOURCES},
    },
    util,
};

use super::*;

#[derive(Message)]
pub struct Dummy {}

pub async fn on_get_cur_scene_info_cs_req(
    session: &mut PlayerSession,
    _body: &GetCurSceneInfoCsReq,
) -> Result<()> {
    let mut player = JsonData::load().await;
    let entry = player.scene.entry_id;

    let scene = load_scene(session, &mut player, entry, false, Option::<u32>::None).await;

    let resp = GetCurSceneInfoScRsp {
        retcode: 0,
        scene: if let Ok(scene) = scene {
            Some(scene)
        } else {
            Some(SceneInfo {
                game_mode_type: 1,
                entry_id: player.scene.entry_id,
                plane_id: player.scene.plane_id,
                floor_id: player.scene.floor_id,
                ..Default::default()
            })
        },
    };

    session.send(CMD_GET_CUR_SCENE_INFO_SC_RSP, resp).await?;
    if !player.position.is_empty() {
        session
            .send(
                CMD_SCENE_ENTITY_MOVE_SC_NOTIFY,
                SceneEntityMoveScNotify {
                    entity_id: 0,
                    entry_id: player.scene.entry_id,
                    motion: Some(player.position.to_motion()),
                    ..Default::default()
                },
            )
            .await?;
    };

    Ok(())
}

// enterscene
pub async fn on_lckgkdehclb(session: &mut PlayerSession, request: &Lckgkdehclb) -> Result<()> {
    let mut player = JsonData::load().await;

    // send packet first
    session
        .send(CMD_ENTER_SCENE_SC_RSP, Dummy::default())
        .await?;

    load_scene(
        session,
        &mut player,
        request.entry_id,
        true,
        Some(request.maplanefddc),
    )
    .await?;

    Ok(())
}

// getscenemapinfocsreq
pub async fn on_fkjoeabiioe(sesison: &mut PlayerSession, request: &Fkjoeabiioe) -> Result<()> {
    let mut map_infos = Vec::<Fjniajephmj>::new();

    for entry_id in &request.dmkkkfnkofh {
        let mut map_info = Fjniajephmj {
            retcode: 0,
            dcbdhkkkpgd: vec![
                Gbiimoglajl {
                    gommoeicmjg: Kihbdaniehp::MapInfoChestTypeNormal.into(),
                    ..Default::default()
                },
                Gbiimoglajl {
                    gommoeicmjg: Kihbdaniehp::MapInfoChestTypePuzzle.into(),
                    ..Default::default()
                },
                Gbiimoglajl {
                    gommoeicmjg: Kihbdaniehp::MapInfoChestTypeChallenge.into(),
                    ..Default::default()
                },
            ],
            entry_id: *entry_id,
            ..Default::default()
        };

        for i in 0..100 {
            map_info.phicefeaigb.push(i)
        }

        let group_config = GAME_RESOURCES.map_entrance.get(entry_id).and_then(|v| {
            GAME_RESOURCES
                .level_group
                .get(&format!("P{}_F{}", v.plane_id, v.floor_id))
        });
        if let Some(level) = group_config {
            // add teleports
            for teleport in &level.teleports {
                map_info.ojlnmnehgai.push(*teleport.0)
            }

            for (group_id, group) in &level.group_items {
                map_info.pmolfbcbfpe.push(Gecjjlmabhp {
                    group_id: *group_id,
                    ..Default::default()
                });

                // prop
                for prop in &group.props {
                    map_info.cgkfbhoadpc.push(Kangcibfhee {
                        group_id: prop.group_id,
                        state: if prop.prop_state_list.contains(&PropState::CheckPointEnable) {
                            PropState::CheckPointEnable as u32
                        } else {
                            prop.state.clone() as u32
                        },
                        ifjocipnpgd: prop.id,
                    });
                }
            }
        }

        // Debug
        // tokio::fs::write(format!("./text-{}.txt", entry_id), format!("{:#?}", map_info)).await?;
        map_infos.push(map_info)
    }

    sesison
        .send(
            CMD_GET_SCENE_MAP_INFO_SC_RSP,
            Cegeebldbke {
                retcode: 0,
                mhefdgcamjl: map_infos,
                ..Default::default()
            },
        )
        .await?;

    Ok(())
}

lazy_static! {
    static ref NEXT_SCENE_SAVE: Mutex<u64> = Mutex::new(0);
}

pub async fn on_scene_entity_move_cs_req(
    session: &mut PlayerSession,
    request: &SceneEntityMoveCsReq,
) -> Result<()> {
    let mut player = JsonData::load().await;
    let mut timestamp = NEXT_SCENE_SAVE.lock().await;

    if util::cur_timestamp_ms() <= *timestamp {
        session
            .send(CMD_SCENE_ENTITY_MOVE_SC_RSP, Dummy::default())
            .await?;

        return Ok(());
    }

    // save every 5 sec
    *timestamp = util::cur_timestamp_ms() + (5 * 1000);

    for entity in &request.entity_motion_list {
        if entity.entity_id != 0 {
            continue;
        }

        if let Some(motion) = &entity.motion {
            if let Some(pos) = &motion.aomilajjmii {
                player.position.x = pos.baimdminomk;
                player.position.y = pos.bemlopmcgch;
                player.position.z = pos.bagloppgnpb;
            }
            if let Some(rot) = &motion.eiaoiankefd {
                player.position.rot_y = rot.bemlopmcgch;
            }
        }
    }

    player.save().await;
    session
        .send(CMD_SCENE_ENTITY_MOVE_SC_RSP, Dummy::default())
        .await
}

pub type GetEnteredSceneCsReq = Dummy;
pub async fn on_get_entered_scene_cs_req(
    session: &mut PlayerSession,
    _: &GetEnteredSceneCsReq,
) -> Result<()> {
    let scenes = GAME_RESOURCES
        .map_entrance
        .iter()
        .filter(|(_, v)| {
            !v.finish_main_mission_list.is_empty() || !v.finish_sub_mission_list.is_empty()
        })
        .map(|(_, v)| Lpllljogfeh {
            floor_id: v.floor_id,
            plane_id: v.plane_id,
        })
        .collect::<Vec<_>>();

    session
        .send(
            CMD_GET_ENTERED_SCENE_SC_RSP,
            Mkgidalegbd {
                lejonbbgdnn: scenes,
                retcode: 0,
            },
        )
        .await
}

// getunlockteleportcsreq
pub async fn on_kkbapmgmmcb(_session: &mut PlayerSession, _request: &Kkbapmgmmcb) -> Result<()> {
    Ok(())
}

async fn load_scene(
    session: &mut PlayerSession,
    json: &mut JsonData,
    entry_id: u32,
    _save: bool,
    teleport_id: Option<u32>,
) -> Result<SceneInfo> {
    let enterance = GAME_RESOURCES
        .map_entrance
        .get(&entry_id)
        .ok_or_else(|| anyhow::format_err!("Map Entrance Not Found"))?;

    let plane = GAME_RESOURCES
        .maze_plane
        .get(&enterance.plane_id)
        .ok_or_else(|| anyhow::format_err!("Map Plane Not Found"))?;

    let group_config = GAME_RESOURCES
        .level_group
        .get(&format!("P{}_F{}", enterance.plane_id, enterance.floor_id))
        .ok_or_else(|| anyhow::format_err!("Group Config Not Found"))?;

    let mut position = json.position.clone();
    if let Some(teleport_id) = teleport_id {
        if let Some(teleport) = group_config.teleports.get(&teleport_id) {
            let anchor = group_config
                .group_items
                .get(&teleport.anchor_group_id.unwrap_or_default())
                .and_then(|v| v.anchors.get(&teleport.anchor_id.unwrap_or_default()));
            if let Some(anchor) = anchor {
                position.x = (anchor.pos_x * 1000f64) as i32;
                position.y = (anchor.pos_y * 1000f64) as i32;
                position.z = (anchor.pos_z * 1000f64) as i32;
                position.rot_y = (anchor.rot_y * 1000f64) as i32;
            }
        }
    }

    let mut scene_info = SceneInfo {
        floor_id: enterance.floor_id,
        plane_id: enterance.plane_id,
        entry_id,
        game_mode_type: plane.plane_type as u32,
        pbfgagecpcd: plane.world_id,
        ..Default::default()
    };

    let lineup_info = AvatarJson::to_lineup_info(&json.lineups);
    let player_pos = MotionInfo {
        // rot
        eiaoiankefd: Some(Vector {
            baimdminomk: 0,
            bemlopmcgch: position.rot_y,
            bagloppgnpb: 0,
        }),
        // pos
        aomilajjmii: Some(Vector {
            baimdminomk: position.x,
            bemlopmcgch: position.y,
            bagloppgnpb: position.z,
        }),
    };

    let mut loaded_npc: Vec<u32> = vec![];
    let mut prop_entity_id = 10;
    let mut npc_entity_id = 10_000;
    let mut monster_entity_id = 20_000;

    for (group_id, group) in &group_config.group_items {
        let mut group_info = Dhkacjhaoid {
            state: 0,
            group_id: *group_id,
            ..Default::default()
        };

        // Load Props
        for prop in &group.props {
            let prop_state = if prop.prop_state_list.contains(&PropState::CheckPointEnable) {
                PropState::CheckPointEnable
            } else {
                prop.state.clone()
            };

            prop_entity_id += 1;

            let prop_position = Position {
                x: (prop.pos_x * 1000f64) as i32,
                y: (prop.pos_y * 1000f64) as i32,
                z: (prop.pos_z * 1000f64) as i32,
                rot_y: (prop.rot_y * 1000f64) as i32,
            };

            let entity_info = SceneEntityInfo {
                inst_id: prop.id,
                group_id: prop.group_id,
                motion: Some(prop_position.to_motion()),
                prop: Some(ScenePropInfo {
                    prop_id: prop.prop_id,
                    prop_state: prop_state as u32,
                    ..Default::default()
                }),
                entity_id: prop_entity_id,
                ..Default::default()
            };

            group_info.entity_list.push(entity_info);
        }

        // Load NPCs
        for npc in &group.npcs {
            if loaded_npc.contains(&(npc.npcid)) || json.avatars.contains_key(&(npc.npcid)) {
                continue;
            }
            npc_entity_id += 1;
            loaded_npc.push(npc.npcid);

            let npc_position = Position {
                x: (npc.pos_x * 1000f64) as i32,
                y: (npc.pos_y * 1000f64) as i32,
                z: (npc.pos_z * 1000f64) as i32,
                rot_y: (npc.rot_y * 1000f64) as i32,
            };

            let info = SceneEntityInfo {
                inst_id: npc.id,
                group_id: npc.group_id,
                entity_id: npc_entity_id,
                motion: Some(npc_position.to_motion()),
                npc: Some(SceneNpcInfo {
                    egeneneoadj: npc.npcid,
                    ..Default::default()
                }),
                ..Default::default()
            };

            group_info.entity_list.push(info);
        }

        // Load Monsters
        for monster in &group.monsters {
            monster_entity_id += 1;
            let monster_position = Position {
                x: (monster.pos_x * 1000f64) as i32,
                y: (monster.pos_y * 1000f64) as i32,
                z: (monster.pos_z * 1000f64) as i32,
                rot_y: (monster.rot_y * 1000f64) as i32,
            };

            let npc_monster = SceneNpcMonsterInfo {
                monster_id: monster.npcmonster_id,
                event_id: monster.event_id,
                world_level: 6,
                ..Default::default()
            };

            let info = SceneEntityInfo {
                inst_id: monster.id,
                group_id: monster.group_id,
                entity_id: monster_entity_id,
                motion: Some(monster_position.to_motion()),
                npc_monster: Some(npc_monster),
                ..Default::default()
            };

            group_info.entity_list.push(info);
        }

        scene_info.chhmmbdhjpg.push(group_info);
    }

    // load player entity
    let mut player_group = Dhkacjhaoid {
        state: 0,
        group_id: 0,
        ..Default::default()
    };
    for (slot, avatar_id) in &json.lineups {
        player_group.entity_list.push(SceneEntityInfo {
            inst_id: 0,
            entity_id: (*slot) + 1,
            motion: Some(MotionInfo {
                // pos
                aomilajjmii: Some(Vector {
                    baimdminomk: json.position.x,
                    bemlopmcgch: json.position.y,
                    bagloppgnpb: json.position.z,
                }),
                // rot
                eiaoiankefd: Some(Vector {
                    baimdminomk: 0,
                    bemlopmcgch: json.position.rot_y,
                    bagloppgnpb: 0,
                }),
            }),
            actor: Some(SceneActorInfo {
                avatar_type: AvatarType::AvatarFormalType.into(),
                base_avatar_id: *avatar_id,
                map_layer: 0,
                uid: 0,
            }),
            ..Default::default()
        })
    }
    scene_info.chhmmbdhjpg.push(player_group);

    if _save {
        session
            .send(
                CMD_ENTER_SCENE_BY_SERVER_SC_NOTIFY,
                Jdokmmikidp {
                    scene: Some(scene_info.clone()),
                    lineup: Some(lineup_info),
                    ..Default::default()
                },
            )
            .await?;

        session
            .send(
                CMD_SCENE_ENTITY_MOVE_SC_NOTIFY,
                SceneEntityMoveScNotify {
                    entity_id: 0,
                    motion: Some(player_pos),
                    entry_id,
                    ..Default::default()
                },
            )
            .await?;

        json.scene.entry_id = entry_id;
        json.scene.floor_id = enterance.floor_id;
        json.scene.plane_id = enterance.plane_id;
        json.position.x = position.x;
        json.position.y = position.y;
        json.position.z = position.z;
        json.position.rot_y = position.rot_y;
        json.save().await;
    }

    Ok(scene_info)
}
