Documentation
use crate::base_config::BaseConfig;
use crate::card_service::replay::RePlayClickHouseDB;
use crate::config::Config;
use crate::database;
use crate::database::mysql::{ConfigValueCache, GameSetting, GameSlotsLevel, SlotsBetRatios};
use crate::robot_service::RobotManager;
use crate::services::{BroadcastService, MasterService, ProxyService};
use crate::timer::{CommitReplayTimer, Timer, TimerManager};
use aqueue::{Actor, RwModel};
use once_cell::sync::{Lazy, OnceCell};
use std::collections::hash_map::Values;
use std::collections::HashMap;
use std::env::current_dir;
use std::path::{Path, PathBuf};
use std::{env, fs};

/// 当前运行路径
pub static CURRENT_EXE_PATH: Lazy<String> = Lazy::new(|| match env::current_exe() {
    Ok(path) => {
        if let Some(current_exe_path) = path.parent() {
            return current_exe_path.to_string_lossy().to_string();
        }
        panic!("current_exe_path get error: is none");
    }
    Err(err) => panic!("current_exe_path get error:{err:?}"),
});

/// 是否已经加载.env
pub static DOTENV: Lazy<bool> = Lazy::new(|| dotenv::dotenv().is_ok());

/// PEM 证书
#[cfg(feature = "remote_load")]
pub static PEM_FILE: Lazy<PathBuf> = Lazy::new(|| {
    if *DOTENV {
        let pem_file = env::var("cert").expect("env key cert not found!,Please write in .env file");
        load_path(&pem_file)
    } else {
        let pem_file = env::var("cert").expect("env key cert not found!,Not found .env file");
        load_path(&pem_file)
    }
});

#[cfg(feature = "remote_load")]
pub static URL: Lazy<String> = Lazy::new(|| {
    if *DOTENV {
        env::var("url").expect("env url not found!,Please write in .env file")
    } else {
        env::var("url").expect("env url not found!,Not found .env file")
    }
});

#[cfg(feature = "remote_load")]
pub static CONFIG: Lazy<Config> =
    Lazy::new(|| CONFIG_CELL.get().expect("CONFIG_CELL not set!").clone());

#[cfg(feature = "remote_load")]
pub static CONFIG_CELL: OnceCell<Config> = OnceCell::new();

#[cfg(feature = "remote_load")]
pub async fn load_config() -> anyhow::Result<Config> {
    let cert = fs::read(&*PEM_FILE).expect("read client_cert_key.pem file error");
    // 构建 Identity
    let identity = reqwest::Identity::from_pem(&cert).expect("load client_cert_key.pem error");
    let client = reqwest::Client::builder()
        .use_rustls_tls()
        .identity(identity)
        .danger_accept_invalid_certs(true)
        .danger_accept_invalid_hostnames(true)
        .build()?;

    let path = format!("{}/cfg/game/config.toml", *URL);
    log::trace!("load config url:{path}");
    let resp = client.get(&path).send().await?;
    let context = resp.text().await?;
    Config::load_config(&context)
}

#[cfg(feature = "remote_load")]
pub static BASE_CONFIG: Lazy<BaseConfig> = Lazy::new(|| {
    BASE_CONFIG_CELL
        .get()
        .expect("BASE_CONFIG_CELL not set!")
        .clone()
});

#[cfg(feature = "remote_load")]
pub static BASE_CONFIG_CELL: OnceCell<BaseConfig> = OnceCell::new();

#[cfg(feature = "remote_load")]
pub async fn load_base_config() -> anyhow::Result<BaseConfig> {
    let cert = fs::read(&*PEM_FILE).expect("read client_cert_key.pem file error");
    // 构建 Identity
    let identity = reqwest::Identity::from_pem(&cert).expect("load client_cert_key.pem error");
    let client = reqwest::Client::builder()
        .use_rustls_tls()
        .identity(identity)
        .danger_accept_invalid_certs(true)
        .danger_accept_invalid_hostnames(true)
        .build()?;

    let game_dir = current_exe_dir_name();
    let path = format!("{}/cfg/game/{game_dir}/base_config.toml", *URL);
    log::trace!("load config url:{path}");
    let resp = client.get(&path).send().await?;
    let context = resp.text().await?;
    BaseConfig::load_config(&context)
}

/// 基本配置
#[cfg(all(feature = "local_load", not(feature = "remote_load")))]
pub static BASE_CONFIG: Lazy<BaseConfig> = Lazy::new(|| {
    BaseConfig::load_config(
        &load_content("base_config.toml").expect("read base_config.toml file error"),
    )
    .expect("toml base_config.toml error")
});

/// 配置
#[cfg(all(feature = "local_load", not(feature = "remote_load")))]
pub static CONFIG: Lazy<Config> = Lazy::new(|| {
    Config::load_config(&load_content("config.toml").expect("read config.toml file error"))
        .expect("toml config.toml error")
});

/// 代理管理器
pub static PROXY: Lazy<RwModel<ProxyService>> = Lazy::new(|| RwModel::new(ProxyService::default()));

/// 广播服务
pub static BROADCAST_SERVICE: Lazy<BroadcastService> = Lazy::new(|| BroadcastService);

/// MASTER 服务器
pub static MASTER_SERVICE: Lazy<MasterService> =
    Lazy::new(|| MasterService::new(BASE_CONFIG.master.clone()));

/// MYSQL数据库
pub static DATABASE: Lazy<database::mysql::Database> =
    Lazy::new(|| database::mysql::Database::new(&CONFIG.master_db).expect("load db error"));

/// 数据库config value
pub static DB_CONFIG: Lazy<Actor<ConfigValueCache>> =
    Lazy::new(|| Actor::new(ConfigValueCache::new()));

/// 日志写入器
pub static REPLAY_DB: Lazy<Actor<RePlayClickHouseDB>> =
    Lazy::new(|| Actor::new(RePlayClickHouseDB::new(&CONFIG.clickhouse_replay)));

/// 游戏进本设置
pub static GAME_SETTING: OnceCell<GameSetting> = OnceCell::new();

/// 游戏等级配置
pub static GAME_SLOT_LEVEL_SETTING: OnceCell<HashMap<i32, GameSlotsLevel>> = OnceCell::new();

/// 老虎机游戏的押注区间
pub static GAME_SLOT_BET_RATIOS: OnceCell<Vec<SlotsBetRatios>> = OnceCell::new();

/// 机器人服务
pub static ROBOT_MANAGER: Lazy<Actor<RobotManager>> = Lazy::new(|| Actor::new(RobotManager::new()));

/// TIME 管理器
pub static TIMER_MANAGER: Lazy<TimerManager> = Lazy::new(|| {
    let ts = vec![Box::new(CommitReplayTimer) as Box<dyn Timer>];
    TimerManager::new(ts)
});
/// 加载游戏基本设置
pub async fn load_db_setting() -> anyhow::Result<()> {
    //加载游戏基本信息
    GAME_SETTING
        .set(
            DATABASE
                .get_game_setting(BASE_CONFIG.base.server_id)
                .await?,
        )
        .expect("GAME_SETTING reset");

    log::info!("load db game setting info:{:?}", GAME_SETTING.get());

    GAME_SLOT_LEVEL_SETTING
        .set(
            DATABASE
                .load_slots_levels(GAME_SETTING.get().unwrap().game_id)
                .await?
                .into_iter()
                .map(|info| (info.level_id, info))
                .collect(),
        )
        .expect("GAME_SLOT_LEVEL_SETTING reset");

    GAME_SLOT_BET_RATIOS
        .set(
            DATABASE
                .load_slots_bet_ratios(GAME_SETTING.get().unwrap().game_id)
                .await?,
        )
        .expect("GAME_SLOT_BET_RATIOS reset");

    Ok(())
}

/// 获取游戏基本设置
#[inline]
pub fn get_game_setting() -> &'static GameSetting {
    unsafe { GAME_SETTING.get_unchecked() }
}

/// 根据等级id获取游戏等级信息
#[inline]
pub fn get_game_slot_level(level_id: i32) -> Option<&'static GameSlotsLevel> {
    unsafe { GAME_SLOT_LEVEL_SETTING.get_unchecked().get(&level_id) }
}

/// 迭代所有游戏等级信息
#[inline]
pub fn game_slot_level_iter() -> Values<'static, i32, GameSlotsLevel> {
    unsafe { GAME_SLOT_LEVEL_SETTING.get_unchecked().values() }
}

/// 获取老虎机游戏的押注区间
#[inline]
pub fn game_slot_bet_ratios() -> &'static [SlotsBetRatios] {
    unsafe { GAME_SLOT_BET_RATIOS.get_unchecked() }
}

/// 加载文件路径
#[inline]
fn load_path(filename: &str) -> PathBuf {
    let exe_path = format!("{}/{}", CURRENT_EXE_PATH.as_str(), filename);
    let config_path = Path::new(&exe_path);
    let path = {
        if !config_path.exists() {
            let json_path = format!(
                "{}/{}",
                current_dir().expect("not found current dir").display(),
                filename
            );
            let path = Path::new(&json_path);
            if !path.exists() {
                let json_path = format!(
                    "{}/{}",
                    current_dir()
                        .expect("not found current dir")
                        .parent()
                        .expect("not found config file")
                        .display(),
                    filename
                );
                let path = Path::new(&json_path);
                if !path.exists() {
                    panic!("not found config file:{path:?}");
                } else {
                    path.to_path_buf()
                }
            } else {
                path.to_path_buf()
            }
        } else {
            config_path.to_path_buf()
        }
    };
    path
}

/// 加载文件内容
#[inline]
pub fn load_content(filename: &str) -> std::io::Result<String> {
    let path = load_path(filename);
    fs::read_to_string(path)
}

/// 获取执行文件所在的目录名
#[inline]
pub fn current_exe_dir_name() -> String {
    match env::current_exe() {
        Ok(path) => {
            if let Some(parent) = path.parent() {
                if let Some(dir_name) = parent.file_name() {
                    return dir_name.to_string_lossy().to_string();
                }
            }
            panic!("current_exe_dir_name get error: is none");
        }
        Err(err) => panic!("current_exe_dir_name get error:{err:?}"),
    }
}

/// 获取 GameConfig.jsonc
#[cfg(feature = "remote_load")]
pub async fn load_game_config() -> anyhow::Result<Vec<u8>> {
    let cert = fs::read(&*PEM_FILE)?;
    // 构建 Identity
    let identity = reqwest::Identity::from_pem(&cert)?;
    let client = reqwest::Client::builder()
        .use_rustls_tls()
        .identity(identity)
        .danger_accept_invalid_certs(true)
        .danger_accept_invalid_hostnames(true)
        .build()?;

    let game_dir = current_exe_dir_name();
    let path = format!("{}/cfg/game/{game_dir}/GameConfig.jsonc", *URL);
    let resp = client.get(&path).send().await?;
    let context = resp.bytes().await?;
    Ok(context.to_vec())
}