/**
 * 国际化模块
 */

 import { Context, logger } from "mslib"
 import { redisClient } from "./Redis";
 import Contants, { IS_PARK, IS_PROD } from "./Contants";
 import { GlobalConfig, IocConfigProvider } from "./Config";
 
 /**
  * 默认语种 `zh_CN`
  */
 export let DEFAULT_LANG = 'zh_CN';
 
 /**
  * i18n实例
  * 
  * 如果已开启国际化开关{@link Config}，那么模块被加载时，{@link redisClient} 创建成功后(语言包存储在redis)，会自动创建该实例，否则为空
  * 
  * ```ts
  * import { i18n, t, $t } from "refs/nslib";
  * 
  * //翻译数据，语种优先级：请求头 > 车场主语言 > zh_CN
  * t('(主相机)', parkId);
  * 
  * //翻译非标数据(数据库)，语种优先级：请求头 > 车场主语言 > zh_CN
  * $t('$#{{(主相机)}}', parkId);
  * 
  * //使用i18n原生方法翻译数据
  * 
  * //根据默认语种进行翻译
  * i18n.__('(主相机)') 
  * i18n.__('Hello %s', 'Keytop') //带占位符%s
  * i18n.__('Hello {{0}}', ['Keytop']) //带占位符{0}
  * i18n.__('Hello {{name}}', { name: 'Keytop' }) //带属性占位符{name}
  * 
  * //指定语种进行翻译
  * i18n.__({ phrase: 'Hello %s', locale: 'en_US' }, 'Keytop1') 
  * i18n.__({ phrase: 'Hello {{0}}', locale: 'en_US' }, ['Keytop1'])
  * i18n.__({ phrase: 'Hello {{name}}', locale: 'en_US' }, { name: 'Keytop1' })
  * ```
  * 
  * @see [i18n](https://www.npmjs.com/package/i18n)
  */
 export let i18n = null;
 
 let config: GlobalConfig = IocConfigProvider.getGlobalConfig();
 
 export async function init() {
     if (config?.enabled !== true) return;
 
     //检测插件
     try {
         const { I18n } = require('i18n');
         i18n = new I18n();
     } catch (error) { }
 
     if (!i18n) return;
 
     try {
         await configure_i18n();
 
         let catalog = i18n.getCatalog();
         logger.info(`i18n初始化成功,语种:${Reflect.ownKeys(catalog).join('、')}`);
     } catch (error) {
         logger.error('i18n初始化失败', error);
     }
 
     //定时刷新语言包
     setInterval(async () => {
         try {
             await configure_i18n();
 
             let catalog = i18n.getCatalog();
             logger.info(`i18n定时刷新语言包成功,语种:${Reflect.ownKeys(catalog).join('、')}`);
         } catch (error) {
             logger.error('i18n定时刷新语言包失败', error);
         }
     }, IS_PROD ? 1000 * 60 * 10 : 1000 * 60 * 5);
 }
 
 async function configure_i18n() {
     let pack = null;
     if (Contants.IS_PARK) {
         pack = await getLanguagePackForPark();
     } else {
         pack = await getLanguagePackForCloud();
     }
 
     i18n.configure({
         locales: Reflect.ownKeys(pack),
         //不存在的语种默认按照中文翻译
         fallbacks: { '*': 'zh_CN' },
         staticCatalog: pack,
         logDebugFn: function (msg) {
             logger.debug(msg);
         },
         logWarnFn: function (msg) {
             if (!IS_PROD) logger.warn(msg);
         },
         logErrorFn: function (msg) {
             logger.error(msg);
         },
         missingKeyFn: function (locale, value) {
             return value;
         },
     });
 }
 
 /**
  * 语言包
  */
 class LanguagePack {
     [lang: string]: { [key: string]: string }
 }
 
 /**
  * 云端获取语言包
  */
 async function getLanguagePackForCloud(): Promise<LanguagePack> {
     let pack = new LanguagePack();
     let keyValues = await redisClient.hgetall('translation_1102011');
 
     for (const key in keyValues) {
         try {
             let current_lang = {};
 
             JSON.parse(keyValues[key]).forEach(s => {
                 current_lang[s.key] = s.value;
             });
 
             pack[key] = current_lang;
         } catch (error) {
             logger.error(`加载云端redis语言包失败,语种:${key}`, error);
         }
     }
 
     return pack;
 }
 
 /**
  * 场端获取语言包
  */
 async function getLanguagePackForPark(): Promise<LanguagePack> {
     let pack = new LanguagePack();
     let fields: string[] = await redisClient.hkeys('t_realtime_data');
     let lang_fields = fields.filter(s => s.startsWith(`translation_1102011`))
 
     for (const field of lang_fields) {
         const [, lang] = field.split(':');
         let current_lang = lang.substring(0, lang.lastIndexOf('_'));
         if (!pack[current_lang]) pack[current_lang] = {};
 
         let json = await redisClient.hget('t_realtime_data', field);
 
         JSON.parse(json).forEach(s => {
             pack[current_lang][s.key] = s.value;
         });
     }
 
     return pack;
 }
 
 let $regExp = /\$#{{(.+?)\}}/g;
 
 /**
  * 翻译数据，只翻译值为`$#{{xxx}}`的字符串
  * 
  * @param data 要翻译的数据，可以是任意对象
  * @param lang 目标语种
  */
 export function $translate(data: any, lang?: string) {
     if (!i18n) return data;
 
     if (typeof data === 'object') {
         for (const key in data) {
             if (typeof data[key] === 'object') {
                 $translate(data[key], lang);
             } else {
                 if (typeof data[key] === 'string' && $regExp.test(data[key])) {
                     data[key] = data[key].replace($regExp, (s) => {
                         return i18n.__({ phrase: s.substring(4, s.length - 2), locale: lang });
                     });
                 }
             }
         }
 
         return data;
     } else {
         if (typeof data === 'string' && $regExp.test(data)) {
             return data.replace($regExp, (s) => {
                 return i18n.__({ phrase: s.substring(4, s.length - 2), locale: lang });
             });
         } else {
             return data;
         }
     }
 }
 
 /**
  * 翻译数据
  * 
  * @param data 要翻译的数据，可以是任意对象
  * @param lang 目标语种
  */
 export function translate(data: any, lang?: string) {
     if (!i18n) return data;
 
     if (typeof data === 'object') {
         for (const key in data) {
             if (typeof data[key] === 'object') {
                 translate(data[key], lang);
             } else {
                 if (typeof data[key] === 'string') {
                     data[key] = i18n.__({ phrase: data[key], locale: lang });
                 }
             }
         }
         return data;
     } else {
         if (typeof data === 'string') {
             return i18n.__({ phrase: data, locale: lang });
         } else {
             return data;
         }
     }
 }
 
 /**
  * 从请求头获取语种
  */
 export async function getLangFromHeader() {
     let lang;
     let headers = Context.getRequestHeaders();
 
     for (const key in headers) {
         if (key.toLocaleLowerCase() === 'accept-language') {
             lang = headers[key]?.replace('-', '_');
         }
     }
 
     return lang;
 }
 
 /**
  * 获取车场主语言
  * 
  * 场端{@link IS_PARK}从sqlite取, 非场端从redis取
  */
 export async function getParkLang(parkId: any) {
     if (!Contants.IS_PARK) {
         return redisClient?.get(`lang-{{${parkId}}}`);
     } else {
         let lang;
 
         const knexInstance = require('knex')({
             client: 'sqlite3',
             connection: {
                 filename: "/home/keytop/data/lotconfig.db"
             }
         });
 
         let configs = await knexInstance('t_config').where({ session: 'charge', name: 'countryAndLanguage' });
         if (configs && configs.length > 0 && configs[0].value) lang = configs[0]?.value;
 
         return lang;
     }
 }
 
 /**
  * 获取语种
  * 
  * 优先级：请求头 > 车场主语言 {@link getParkLang} > zh_CN
  */
 export async function getLang(parkId?: any) {
     let lang = getLangFromHeader();
 
     if (!lang) {
 
         if (IS_PARK && parkId) {
             try {
                 lang = await getParkLang(parkId);
                 if (!IS_PROD) logger.info(`读取车场主语言成功,${lang || '未设置'}`);
             } catch (error) {
                 logger.error(error);
             }
         }
     }
 
     return lang || DEFAULT_LANG;
 }
 
 /**
  * 翻译数据
  * 
  * 语种优先级：请求头 > 车场主语言 {@link getParkLang} > zh_CN
  * @param data 要翻译的数据，可以是任意对象
  * @param parkId 车场id, 用来获取车场主语言
  */
 export async function t(data: any, parkId?: any) {
     if (!i18n || !data) return data;
 
     try {
         let lang = await getLang(parkId);
         return translate(data, lang);
     } catch (error) {
         logger.error(error);
         return data;
     }
 }
 
 /**
  * 翻译数据, 只翻译值为`$#{{xxx}}`的字符串
  * 
  * 语种优先级：请求头 > 车场主语言 {@link getParkLang} > zh_CN
  * @param data 要翻译的数据，可以是任意对象
  * @param parkId 车场id, 用来获取车场主语言
  */
 export async function $t(data: any, parkId?: any) {
     if (!i18n || !data) return data;
 
     try {
         let lang = await getLang(parkId);
         return $translate(data, lang);
     } catch (error) {
         logger.error(error);
         return data;
     }
 }