use std::{error::Error, sync::Arc};

use common::{
    db::{self, DbDeployConfigMgr, RedisMgr},
    net::ServiceType,
    ServiceBox,
};
use proto::ENetReason;

use crate::{
    config::GateserverConfig,
    network::{NetworkMgr, PlayerSession},
};

use super::login_handler::LoginHandler;

pub struct GateserverService {
    pub network_mgr: NetworkMgr,
    pub service_box: ServiceBox,
    pub deploy_config_mgr: DbDeployConfigMgr,
    pub redis_mgr: RedisMgr,
    pub config: GateserverConfig,
    pub login_handler: LoginHandler,
}

impl GateserverService {
    pub async fn new(config: GateserverConfig) -> Result<Self, Box<dyn Error>> {
        let deploy_config_mgr =
            DbDeployConfigMgr::new(db::connect(&config.database.connection_string).await?);
        let redis_mgr = RedisMgr::new(&config.database.redis_url).await?;

        let network_mgr = NetworkMgr::new(&config).await?;
        let service_box = ServiceBox::new(config.app_id, &config.applications);

        Ok(Self {
            network_mgr,
            service_box,
            deploy_config_mgr,
            redis_mgr,
            config,
            login_handler: LoginHandler,
        })
    }

    pub async fn run(&self) {
        self.network_mgr.run().await
    }

    pub fn find_player_session(&self, session_id: u32) -> Option<Arc<PlayerSession>> {
        self.network_mgr.find_player_session(session_id)
    }

    pub fn get_service_load(&self) -> u32 {
        self.network_mgr.get_player_session_num() as u32
    }

    pub async fn disconnect(&self, session_id: u32, reason: ENetReason) {
        self.network_mgr.disconnect(session_id, reason).await
    }

    pub fn choose_gameserver_id(&self) -> u32 {
        self.config
            .applications
            .get(&ServiceType::Gameserver)
            .unwrap()
            .first()
            .unwrap()
            .app_id
    }
}
