Documentation
use crate::static_def::DATABASE;
use crate::time::timestamp;
use anyhow::*;
use aqueue::Actor;
use log::*;
use std::collections::HashMap;

use super::ConfigValue;

/// 配置 config_value 多久超时,超时之后需要重新加装
/// 5分钟
const CONFIG_VALUE_OUT_TIME: i64 = 5 * 600000000i64;

/// Config value 读取写入器
#[derive(Default)]
pub struct ConfigValueCache {
    config_value: HashMap<String, ConfigValue>,
}

pub enum ConfigValueType {
    I64(i64),
    F64(f64),
    String(String),
}

/// 数据库 配置 value加载器
pub trait DBConfigValue: Send + Sync {
    /// 加载表返回值
    fn load_config_value(value: &ConfigValue) -> Option<Self>
    where
        Self: Sized;

    /// 修改值
    fn modify_config_value(self, value: &mut ConfigValue);
}

/// 实现字符串
impl DBConfigValue for String {
    #[inline]
    fn load_config_value(value: &ConfigValue) -> Option<Self> {
        value.value_varchar.clone()
    }

    #[inline]
    fn modify_config_value(self, value: &mut ConfigValue) {
        value.value_varchar = Some(self)
    }
}
/// 实现i64
impl DBConfigValue for i64 {
    #[inline]
    fn load_config_value(value: &ConfigValue) -> Option<Self> {
        value.value_bigint
    }

    #[inline]
    fn modify_config_value(self, value: &mut ConfigValue) {
        value.value_bigint = Some(self)
    }
}
/// 实现f64
impl DBConfigValue for f64 {
    #[inline]
    fn load_config_value(value: &ConfigValue) -> Option<Self> {
        value.value_double
    }

    #[inline]
    fn modify_config_value(self, value: &mut ConfigValue) {
        value.value_double = Some(self)
    }
}

impl ConfigValueCache {
    #[inline]
    pub fn new() -> Self {
        Self::default()
    }

    /// 加装数据库 config_value 表,顺便缓存表
    #[inline]
    async fn load_config_value<T: DBConfigValue>(&mut self, key: &str) -> Result<Option<T>> {
        let value = DATABASE.load_config_value(key).await?;
        if let Some(mut value) = value {
            value.create_time = timestamp();
            let ret = T::load_config_value(&value);
            self.config_value.insert(value.key.clone(), value);
            Ok(ret)
        } else {
            warn!("not found config key:{}", key);
            Ok(None)
        }
    }

    /// 获取数据库配置
    #[inline]
    async fn read_config_value<T: DBConfigValue>(&mut self, key: &str) -> Result<Option<T>> {
        if let Some(value) = self.config_value.get(key) {
            // 检查超时,如果没有超时就直接给值,否则走下面重新加载
            if value.create_time + CONFIG_VALUE_OUT_TIME > timestamp() {
                return Ok(T::load_config_value(value));
            }
        }
        self.load_config_value(key).await
    }
}

pub trait IConfigValue {
    /// 获取数据库配置
    async fn get_config_value<T: DBConfigValue + 'static>(&self, key: &str) -> Result<Option<T>>;
}

impl IConfigValue for Actor<ConfigValueCache> {
    #[inline]
    async fn get_config_value<T: DBConfigValue + 'static>(&self, key: &str) -> Result<Option<T>> {
        self.inner_call(|inner| async move { inner.get_mut().read_config_value::<T>(key).await })
            .await
    }
}