import { SandBox } from './proxy/sandBox';
import { loadHtml } from './utils/source';
import { errorLog } from './utils/log';
import { getURL } from './utils';
import { MustardApp } from './element'; 
import { addInstance, removeInstance } from './global';
import { MustardState, decodeState, initState } from './proxy/proxyHistory';
import { IAppConstructor, IAppStatus, MustardName, MustardURL, SpurceValue } from './typings';
import { EventCenterMicorLife } from './communication';


export default class App {
    baseUrl: MustardURL;
    url: MustardURL;
    name: MustardName;
    container: MustardApp;

    sandbox: SandBox;

    loadCount: number = 0;
    status = IAppStatus.create;

    state: MustardState; // document 来源
    microLifeCenter: EventCenterMicorLife; // 生命周期通讯

    // 存放动态资源
    source = {
        links: new Map<string, SpurceValue>(), // 存放links
        scripts: new Map<string, SpurceValue>(), // 存放scripts
        domClick: '\n;' // 存放 dom attrs 上的事件
    };

    constructor ({ name, url: baseUrl, container }: IAppConstructor) {
        this.name = name;
        this.baseUrl = baseUrl;
        this.container = container;
        this.microLifeCenter = new EventCenterMicorLife(this.name);
        this.init();
    }

    // 刷新 卸载->初始化
    reload () {
        this.unmount(true);
        this.init();
    }

    // 初始化
    init () {
        this.status = IAppStatus.create;
        this.microLifeCenter.dispatchLife(this.status);
        // 初始化立刻存入 mustardAppInfos.appInstanceMap
        addInstance(this.name, this); 

        this.loadCount = 0;
        // 设置资源的真正地址
        this.url = getURL(this.name, this.baseUrl).href;
        
        this.source = {
            links: new Map<string, SpurceValue>(), // 存放links
            scripts: new Map<string, SpurceValue>(), // 存放scripts
            domClick: '\n;' // 存放 dom attrs 上的事件
        };

        // 刷新页面时，为了保证 document.origin 正确，默认取history
        this.state = decodeState(this.name);

        if(!this.state) {
            initState(this.name, '', '', this.url);
            this.state = decodeState(this.name);
        }
        
        this.status = IAppStatus.loading;
        this.microLifeCenter.dispatchLife(this.status);

        // 加载对应的资源
        loadHtml(this);
        // 初始化砂箱
        this.sandbox = new SandBox(this.name, this.url);
    }

    // 资源加载完时执行
    onLoad (htmlDom: HTMLElement) {
        this.loadCount += 1;
        // 第二次执行且组件未卸载时执行渲染
        if (this.loadCount === 2 && this.status !== IAppStatus.unmount) {
            // 执行mount方法
            this.mount(htmlDom);
        }
    }

    /**
     * 资源加载完成后进行渲染
     */
    mount (html: HTMLElement) {
        this.sandbox.start();
        // 克隆DOM节点
        const cloneHtml = html.cloneNode(true);
        // 创建一个fragment节点作为模版，这样不会产生冗余的元素
        const fragment = document.createDocumentFragment();
        Array.from(cloneHtml.childNodes).forEach((node) => {
            fragment.appendChild(node);
        });

        // 将格式化后的DOM结构插入到容器中
        this.container.appendChild(fragment);

        // 执行js
        let scripts = '';
        this.source.scripts.forEach((info) => {
            scripts += info.code + '\n;';
        });

        {
            (0, eval)(this.sandbox.bindScope(scripts + '\n;' + this.source.domClick));
        }

        // 标记应用为已渲染
        this.status = IAppStatus.mount;
        this.microLifeCenter.dispatchLife(this.status);
    }

    /**
     * 卸载应用
     * 执行关闭沙箱，清空缓存等操作
     * @param destory 是否销毁应用
     */
    unmount (destory: boolean) {
        this.sandbox.stop(); // 暂停沙箱
        this.status = IAppStatus.unmount;
        this.microLifeCenter.dispatchLife(this.status);
        if (destory) {
            this.destory();
        }
    }

    /**
     * 销毁应用
     */
    destory () {
        this.sandbox = null;
        this.container.innerHTML = '';
        this.status = IAppStatus.destory;
        this.microLifeCenter.dispatchLife(this.status);
        removeInstance(this.name);
    }

    /**
     * 子应用加载失败
     * @param error 失败原因
     */
    error (error: Error): void {
        errorLog(error);
        this.status = IAppStatus.error;
        this.microLifeCenter.dispatchLife(this.status);
        this.microLifeCenter = null;
    }
}