use prost::Message;
use proto::{PlayerData, SavePlayerDataReq};
use std::error::Error;

use common::{
    impl_net_service,
    net::{tools::Packet, AServer, NetError, ServiceType},
    rpc_handlers, ServiceQualifier,
};

use crate::{config::DbgateConfig, dbgate_service};

pub struct NetworkMgr {
    server: AServer,
}

impl NetworkMgr {
    pub async fn new(config: &DbgateConfig) -> Result<Self, NetError> {
        let inner_ip = config
            .applications
            .get(&ServiceType::Dbgate)
            .unwrap()
            .iter()
            .find(|a| a.app_id == config.app_id)
            .unwrap()
            .inner_ip
            .clone();

        Ok(Self {
            server: AServer::bind(inner_ip).await?,
        })
    }

    pub async fn run(&self) {
        loop {
            let Ok(mut packets) = self.server.recv().await else {
                continue;
            };

            while let Some(packet) = packets.pop_front() {
                if let Err(err) = self.on_recv(packet).await {
                    tracing::warn!("on_recv fail: {err}");
                }
            }
        }
    }

    pub async fn send_rsp(&self, packet: Packet) -> Result<(), NetError> {
        dbgate_service()
            .service_box
            .push_packet_to_service(ServiceQualifier::AppId(packet.head.rpc_id), packet)
            .await
    }

    rpc_handlers! {
        GetPlayerTokenReq;
        PlayerLoginReq;
        SavePlayerDataReq;
    }

    async fn on_get_player_token_req(&self, packet: Packet) -> Result<(), Box<dyn Error>> {
        dbgate_service()
            .login_handler
            .on_get_player_token_req(packet)
            .await
    }

    async fn on_player_login_req(&self, packet: Packet) -> Result<(), Box<dyn Error>> {
        dbgate_service()
            .login_handler
            .on_player_login_req(packet)
            .await
    }

    async fn on_save_player_data_req(&self, packet: Packet) -> Result<(), Box<dyn Error>> {
        let req = packet.get_proto::<SavePlayerDataReq>()?;
        let Ok(player_data) = PlayerData::decode(&*req.player_data) else {
            tracing::error!(
                "on_save_player_data_req: PlayerData::decode failed, uid: {}",
                packet.head.user_id
            );
            return Ok(());
        };

        dbgate_service()
            .sql_op_player_data
            .update_player_data_by_uid(
                player_data.uid as i32,
                player_data.nickname,
                player_data.level as i32,
                player_data.exp as i32,
                player_data.bin,
            )
            .await?;
        Ok(())
    }

    pub async fn default_packet_process_func(&self, _packet: Packet) -> Result<(), Box<dyn Error>> {
        Ok(())
    }
}

impl_net_service!(Dbgate);
