import type { VoerkaI18nLanguageMessages } from "@/types";
import type { VoerkaI18nScope } from "..";
import { isFunction  } from "flex-tools/typecheck/isFunction";
import { isPlainObject } from "flex-tools/typecheck/isPlainObject"; 
import { IAsyncSignal,asyncSignal } from "flex-tools/async/asyncSignal";

/**
 * 语言包补丁
 * 
 */
export class PatchMessageMixin{    
	protected _patching:IAsyncSignal | undefined
	private _getPatchKey(this:VoerkaI18nScope,language:string){
		return `voerkai18n_${this.id}_${language}_patched_messages`
	}
    /**
     * 清除保存在本地的补丁语言包
     * @param language 
     */
    clearPatchedMessages(this:VoerkaI18nScope,language?:string) {
        if(this.storage){
            let langs = language ? [language] : this.languages.map(language=>language.name);
            for(let lang of langs){
                this.storage.remove(this._getPatchKey(lang));
            }
        }
    } 
	/**
	 * 当指定了默认语言包加载器后，会从服务加载语言补丁包来更新本地的语言包
	 *
	 * 补丁包会自动存储到本地的LocalStorage中
	 *
	 * @param {*} messages
	 * @param {*} language
	 * @returns {Promise<number>} 返回补丁包的数量
	 */
	protected async _patch(this:VoerkaI18nScope, language?:string){
		this._patching = asyncSignal();
		if (!language) language = this.activeLanguage;
      	// 1. 从本地存储中恢复补丁
        this._restorePatchedMessages(this.activeMessages, language); 
		// 2. 从远程加载语言包补丁		
		try {
			if (isFunction(this.loader)){
				const pachedMessages = (await this._loadMessagesFromLoader(language)) as unknown as VoerkaI18nLanguageMessages;
				if(isPlainObject(pachedMessages)){
					Object.assign(this._activeMessages, pachedMessages);
					this._setPatchedMessages(pachedMessages, language);
					this.emit('patched',{ language:language,scope:this.id })
				}
			}			
		}catch (e:any) {
			this.logger.warn(`从远程加载语言补丁包<${language}>时出错: ${e.stack}(scope=${this.id})`);
		}finally{
			this._patching?.resolve()
			this._patching = undefined
		}
	}
	/**
	 * 从本地存储中读取语言包补丁合并到当前语言包中
	 */
	protected _restorePatchedMessages(this:VoerkaI18nScope,messages:VoerkaI18nLanguageMessages,language:string) {
		const patchedMessages = this._getPatchedMessages(language);
		if (isPlainObject(patchedMessages)){
            Object.assign(messages, patchedMessages);
            this.emit('restore',{language,scope:this.id})
            this.logger.debug(`成功恢复补丁语言包<${language}>(scope=${this.id})`);
		}
	}
	/**
	 * 将读取的补丁包保存到本地的LocalStorage中
	 *
	 * 为什么要保存到本地的LocalStorage中？
	 *
	 * 因为默认语言是静态嵌入到源码中的，而加载语言包补丁是延后异步的，
	 * 当应用启动第一次就会渲染出来的是没有打过补丁的内容。
	 *
	 * - 如果还需要等待从服务器加载语言补丁合并后再渲染会影响速度
	 * - 如果不等待从服务器加载语言补丁就渲染，则会先显示未打补丁的内容，然后在打完补丁后再对应用进行重新渲染生效
	 *   这明显不是个好的方式
	 *
	 * 因此，采用的方式是：
	 * - 加载语言包补丁后，将之保存到到本地的LocalStorage中
	 * - 当应用加载时会查询是否存在补丁，如果存在就会合并渲染
	 *
	 * @param {*} messages
	 */
	protected _setPatchedMessages(this:VoerkaI18nScope,messages:VoerkaI18nLanguageMessages, language:string) {
        if(!this.attached && !this.storage) return 
		try {
            this.storage && this.storage.set(this._getPatchKey(language),JSON.stringify(messages));
		} catch (e:any) {
			this.logger.error(`保存语言包补丁(${language})时出错: ${e.stack}(scope=${this.id})`);
		}
	}

	/**
	 * 从本地缓存中读取补丁语言包
	 * @param {*} language
	 * @returns
	 */
	protected _getPatchedMessages(this:VoerkaI18nScope,language:string) {
		try {
            if(this.storage && this.options.cachePatch){
                return this.storage.get(this._getPatchKey(language)) 
            }else{
                return {};
            }
		} catch (e:any) {
            this.logger.error(`读取语言包补丁(${language})时出错:${e.stack}(scope=${this.id})`);
			return {};
		}
	}
}

