use std::{collections::BTreeMap, sync::Mutex};

#[derive(Clone)]
pub struct GateserverInfo {
    pub app_id: u32,
    #[allow(dead_code)]
    pub inner_ip: String,
    pub outer_ip: String,
    pub listen_port: u16,
    pub use_domain_name: bool,
    pub domain_name: String,
    pub load: u32,
}

pub struct GateserverMgr {
    gateserver_map: Mutex<BTreeMap<u32, GateserverInfo>>,
}

impl GateserverMgr {
    pub fn new() -> Self {
        Self {
            gateserver_map: Mutex::new(BTreeMap::new()),
        }
    }

    pub fn add_gateserver(
        &self,
        app_id: u32,
        inner_ip: String,
        outer_ip: String,
        listen_port: u16,
        use_domain_name: bool,
        domain_name: String,
        load: u32,
    ) {
        let mut gateserver_map = self.gateserver_map.lock().unwrap();
        gateserver_map.insert(
            app_id,
            GateserverInfo {
                app_id,
                inner_ip: inner_ip.clone(),
                outer_ip: outer_ip.clone(),
                listen_port,
                use_domain_name,
                domain_name: domain_name.clone(),
                load,
            },
        );

        tracing::info!("add gateserver app_id={}, inner_ip={}, outer_ip={}, listen_port={} use_domain_name={} domain_name={}. cur_total_server_count={}", app_id, inner_ip, outer_ip, listen_port, use_domain_name, domain_name, gateserver_map.len());
    }

    pub fn get_gateserver(&self, uid: u32) -> Option<GateserverInfo> {
        let gateserver_map = self.gateserver_map.lock().unwrap();
        match gateserver_map
            .values()
            .min_by_key(|g| g.load)
            .map(|g| g.clone())
        {
            Some(info) => {
                tracing::info!("get gateserver {} app_id: to uid={uid}", info.app_id);
                Some(info)
            }
            None => None,
        }
    }

    pub fn sync_gateserver_load(&self, app_id: u32, load: u32) {
        let mut gateserver_map = self.gateserver_map.lock().unwrap();

        let Some(info) = gateserver_map.get_mut(&app_id) else {
            tracing::warn!("fail to find gateserver app_id={app_id}");
            return;
        };

        if info.load != load {
            info.load = load;
            tracing::info!("sync gateserver load app_id={app_id}, load={load}");
        }
    }
}
