#[macro_export]
macro_rules! auth_req_handlers {
    ($($module:ident::$name:ident;)*) => {
        pub async fn handle_auth_request(session: &crate::net::NetSession, packet: &crate::net::NetPacket, state: &crate::ServerState) -> Result<bool, crate::handlers::core::PacketHandlingError> {
            use ::prost::Message;
            use crate::handlers::core::NetError;
            use crate::handlers::core::PacketHandlingError;
            ::paste::paste! {
                match packet.cmd_id {
                    $(
                        ::proto::[<$name CsReq>]::CMD_ID => {
                            let mut req = ::proto::[<$name CsReq>]::decode(&*packet.body)?;
                            req.xor_fields();

                            let rsp = match crate::handlers::$module::[<on_ $name:snake>](&session, &state.player_mgr, req).await {
                                Ok(rsp) => {
                                    ::tracing::info!(concat!("[LOGIN] successfully handled request ", stringify!($name)));
                                    rsp
                                },
                                Err(NetError::ErrCode(retcode)) => {
                                    ::tracing::info!("[LOGIN] request {} was handled, retcode: {retcode:?}", stringify!($name));
                                    [<$name ScRsp>] {
                                        retcode: retcode.into(),
                                        ..Default::default()
                                    }
                                },
                                Err(err) => return Err(PacketHandlingError::Net(err)),
                            };

                            session.send_rsp(packet.head.packet_id, rsp).await?;
                            Ok(true)
                        }
                    )*
                    _ => Ok(false),
                }
            }
        }
    };
}

#[macro_export]
macro_rules! req_handlers {
    ($($module:ident::$name:ident;)*) => {
        pub async fn handle_request(session: &crate::net::NetSession, packet: &crate::net::NetPacket, state: &crate::ServerState) -> Result<bool, crate::handlers::core::PacketHandlingError> {
            use ::prost::Message;
            use crate::handlers::core::NetError;
            use crate::handlers::core::PacketHandlingError;
            ::paste::paste! {
                match packet.cmd_id {
                    $(
                        ::proto::[<$name CsReq>]::CMD_ID => {
                            let player_uid = *session.player_uid().unwrap(); // Should never be None here
                            let Some(player_lock) = state.player_mgr.get_player(player_uid).await else {
                                ::tracing::error!("PlayerManager::get_player returned None, uid={player_uid}");
                                return Ok(true);
                            };

                            let mut req = ::proto::[<$name CsReq>]::decode(&*packet.body)?;
                            req.xor_fields();

                            let rsp = match crate::handlers::$module::[<on_ $name:snake>](&session, &mut *player_lock.lock().await, req).await {
                                Ok(rsp) => {
                                    ::tracing::info!(concat!("successfully handled request ", stringify!($name)));
                                    rsp
                                }
                                Err(NetError::ErrCode(retcode)) => {
                                    ::tracing::info!("request {} was handled, retcode: {retcode:?}", stringify!($name));
                                    [<$name ScRsp>] {
                                        retcode: retcode.into(),
                                        ..Default::default()
                                    }
                                },
                                Err(err) => return Err(PacketHandlingError::Net(err)),
                            };

                            session.send_rsp(packet.head.packet_id, rsp).await?;
                            Ok(true)
                        }
                    )*
                    _ => Ok(false),
                }
            }
        }
    };
}

#[macro_export]
macro_rules! notify_handlers {
    ($($module:ident::$name:ident;)*) => {
        pub async fn handle_notify(session: &crate::net::NetSession, packet: &crate::net::NetPacket, state: &crate::ServerState) -> Result<bool, crate::handlers::core::PacketHandlingError> {
            use ::prost::Message;
            use crate::handlers::core::NetError;
            use crate::handlers::core::PacketHandlingError;
            ::paste::paste! {
                match packet.cmd_id {
                    $(
                        ::proto::[<$name Notify>]::CMD_ID => {
                            let player_uid = *session.player_uid().unwrap(); // Should never be None here
                            let Some(player_lock) = state.player_mgr.get_player(player_uid).await else {
                                ::tracing::error!("PlayerManager::get_player returned None, uid={player_uid}");
                                return Ok(true);
                            };

                            let mut ntf = ::proto::[<$name Notify>]::decode(&*packet.body)?;
                            ntf.xor_fields();

                            match crate::handlers::$module::[<on_ $name:snake>](session, &mut *player_lock.lock().await, ntf).await {
                                Ok(()) => (),
                                Err(NetError::Logout) => return Err(PacketHandlingError::Logout),
                                Err(err) => return Err(PacketHandlingError::Net(err)),
                            }

                            ::tracing::info!("successfully handled notify {}", stringify!($name));
                            Ok(true)
                        }
                    )*
                    _ => Ok(false),
                }
            }
        }
    };
}

pub use auth_req_handlers;
pub use notify_handlers;
pub use req_handlers;
