/**
 * 国际化模块
 */

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 lang ? i18n.__({ phrase: s.substring(4, s.length - 2), locale: lang }) : i18n.__(s.substring(4, s.length - 2));
                    });
                }
            }
        }

        return data;
    } else {
        if (typeof data === 'string' && $regExp.test(data)) {
            return data.replace($regExp, (s) => {
                return lang ? i18n.__({ phrase: s.substring(4, s.length - 2), locale: lang }) : i18n.__(s.substring(4, s.length - 2));
            });
        } 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] = lang ? i18n.__({ phrase: data[key], locale: lang }) : i18n.__(data[key]);
                }
            }
        }
        return data;
    } else {
        if (typeof data === 'string') {
            return lang ? i18n.__({ phrase: data, locale: lang }) : i18n.__(data);
        } else {
            return data;
        }
    }
}

/**
 * 从请求头获取语种
 */
export function getLangFromHeader() {
    let lang = null;
    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;
    }
}