/**
 * 国际化模块
 */

import { Singleton, Container, Inject } from "typescript-ioc";
import { Context, logger } from "mslib"
import { TyGlobalApiClient, Token } from "./TyGlobalApi";
import { redisClient } from "./Redis";
import Contants, { IS_PARK, IS_PROD } from "./Contants";
import { GlobalConfig, IocConfigProvider } from "./Config";
import { retry } from "./Retry";

/**
 * 国际化服务类
 */
@Singleton
export class GlobalService {
    @Inject
    tyGlobalApiClient: TyGlobalApiClient;

    /**
     * 获取语言包
     * 
     * 返回值为什么没有使用Map来做?
     * 
     * 1.Map直接转json是个空对象{}，可以使用Object.fromEntries转换(map的key变成属性，map的value变成属性值)再转json;
     * 
     * 2.js弱语言可以使用key作为属性名，但使用数据结构{ key: string, value: string }来存储具有通用性;
     */
    async getLanguagePacks(args: { token: Token, language?: string }): Promise<{
        [lang: string]: { key: string, value: string }[]
    }> {

        let apiResponse = await this.tyGlobalApiClient.get({
            token: args.token,
            type: 1
        });

        let packs = {};
        packs['zh_CN'] = [];
        let cn_pack = {};

        /**
         统一接口返回的语言包数据是平铺的格式:{language,key,value,cn},且每条条目中都包含他的中文翻译
         */
        for (const item of apiResponse.data?.data) {
            if (!packs[item.language]) packs[item.language] = [];

            packs[item.language].push({ key: item.key, value: item.value });
            if (!Reflect.has(cn_pack, item.key)) cn_pack[item.key] = item.cn;
        }

        for (const key in cn_pack) {
            packs['zh_CN'].push({ key: key, value: cn_pack[key] });
        }

        if (Contants.IS_LOCAL_DEBUG) {
            packs['en_US'].push({ key: 'config_alert_offlineAlertThresholdTime2', value: '1111111111111111111111111111111' });
            packs['zh_CN'].push({ key: 'config_alert_offlineAlertThresholdTime2', value: '这是中文翻译后的结果' });
        }

        return packs;
    }
}

/**
 * 默认语种：zh_CN
 */
export let DEFAULT_LANG = 'zh_CN';

/**
 * i18n国际化实例
 * 
 * 当config.global.mode=PARK时，从场端redis加载语言包
 * 
 * 当config.global.mode=CLOUD时，从云端redis加载语言包
 * 
 * 基本用法：
 * 
 * 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' })
 * 
 * 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();

        setInterval(async () => {
            try {
                await configure_i18n();
                logger.info('i18n刷新语言包成功');
            } catch (error) {
                logger.error('i18n刷新语言包失败', error);
            }
        }, IS_PROD ? 1000 * 60 * 10 : 1000 * 60 * 5);

        await configure_i18n();

        logger.info('i18n初始化成功');
    } catch (error) {
        logger.error('i18n初始化失败', error);
    }
}

async function configure_i18n() {
    if (Contants.IS_PARK) {
        await configure_i18n_park();
    } else {
        await configure_i18n_cloud();
    }
}

async function configure_i18n_cloud() {
    let packs = await redisClient.hgetall('translation_1102011');
    let staticCatalog = {};

    for (const key in packs) {
        try {
            let pack = {};

            JSON.parse(packs[key]).forEach(s => {
                pack[s.key] = s.value;
            });

            staticCatalog[key] = pack;
        } catch (error) {
            logger.error(`加载云端redis语言包失败,lang:${key}`, error);
        }
    }

    logger.info(`加载云端redis语言包成功,langs:${Reflect.ownKeys(staticCatalog).join(',')}`);

    i18n.configure({
        locales: Reflect.ownKeys(staticCatalog),
        //不存在的语种默认按照中文翻译
        fallbacks: { '*': 'zh_CN' },
        staticCatalog: staticCatalog,
        mustacheConfig: {
            tags: ['{', '}'],
            disable: false
        },
        logDebugFn: function (msg) {
            logger.debug(msg);
        },
        logWarnFn: function (msg) {
            logger.warn(msg);
        },
        logErrorFn: function (msg) {
            logger.error(msg);
        },
        missingKeyFn: function (locale, value) {
            return value;
        },
    });
}

async function configure_i18n_park() {
    let staticCatalog = {};
    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 (!staticCatalog[current_lang]) staticCatalog[current_lang] = {};

        let json = await redisClient.hget('t_realtime_data', field);

        JSON.parse(json).forEach(s => {
            staticCatalog[current_lang][s.key] = s.value;
        });
    }

    logger.info(`加载场端redis语言包成功,langs:${Reflect.ownKeys(staticCatalog).join(',')}`);

    i18n.configure({
        locales: Reflect.ownKeys(staticCatalog),
        //不存在的语种默认按照中文翻译
        fallbacks: { '*': 'zh_CN' },
        staticCatalog: staticCatalog,
        mustacheConfig: {
            tags: ['{', '}'],
            disable: false
        },
        logDebugFn: function (msg) {
            logger.debug(msg);
        },
        logWarnFn: function (msg) {
            logger.warn(msg);
        },
        logErrorFn: function (msg) {
            logger.error(msg);
        },
        missingKeyFn: function (locale, value) {
            return value;
        },
    });
}

let $regExp = /\$#{{(.+?)\}}/g;

/**
 * 翻译数据，只翻译包含$#{{xxx}}的字符串
 * 
 * @param data 翻译的数据，可以是任意对象
 * @param lang 目标语种,如果i18n已配置默认语种,不用传
 */
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 目标语种,如果i18n已配置默认语种,不用传
 */
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;
        }
    }
}

/**
 * 获取车场主语言
 * 
 * 1.云端从redis取
 * 
 * 2.场端从sqlite取
 */
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;
    }
}

/**
 * 获取语种
 * 
 * 优先级：请求头 > redis(云端)/sqlite(场端) > zh_CN
 * @param parkId 
 */
export async function getLang(parkId?: any) {
    let lang = null;
    let headers = Context.getRequestHeaders();
    lang = headers?.language;

    try {
        if (!lang && IS_PARK && parkId) {
            lang = await getParkLang(parkId);
            if (!IS_PROD) logger.info(`读取车场主语言成功,${lang || '未设置'}`);
        }
    } catch (error) {
        logger.error(error);
    }

    return lang || DEFAULT_LANG;
}

/**
 * 使用i18n对任意类型的对象中的所有字符串进行翻译
 * 
 * 语种优先级：请求头 > 车场主语言 > zh_CN
 * @param data 翻译的数据，可以是任意对象
 * @param parkId 车场id,用来获取车场主语言
 */
export async function t(data: any, parkId?: any) {
    if (!i18n || !data) return data;

    let lang = await getLang(parkId);
    return translate(data, lang);
}

/**
 * 使用i18n对任意类型的对象中的所有包含$#{{xxx}}的字符串进行翻译
 * 
 * 语种优先级：请求头 > 车场主语言 > zh_CN
 * @param data 翻译的数据，可以是任意对象
 * @param parkId 车场id,用来获取车场主语言
 */
export async function $t(data: any, parkId?: any) {
    if (!i18n || !data) return data;

    let lang = await getLang(parkId);
    return $translate(data, lang);
}