import App from './app';
import { changeDomPropety } from './proxy/proxyDocument';
import { appIsExist, getAppFromInstance }from './global';
import { AppName, IAppStatusCN, MainMustardApp, MustardName, MustardURL } from './typings';
import { addEventListenerUrl, checkUrl } from './check/urlCheck';
import { changeHistoryPropety } from './proxy/proxyHistory';
import { EventCenterBaseApp } from './communication';
import { isFunction, isIAppStatusKey } from './utils/tools';
import { nextTick } from './utils';

const eventCenter = new EventCenterBaseApp();
const rawSetAttribute = HTMLElement.prototype.setAttribute;
const rawAddEventListener = HTMLElement.prototype.addEventListener;
const rawRemoveEventListener = HTMLElement.prototype.removeEventListener;

type AddEventListenerParams = Parameters<typeof rawAddEventListener>
type EventListenerType = 'dataChange' | IAppStatusCN | keyof HTMLElementEventMap;
type EventListenerListener = AddEventListenerParams[1];
type EventListenerOptions = AddEventListenerParams[2];

export class MustardApp extends HTMLElement {
    url:MustardURL = ''; // 子应用资源地址
    name:MustardName = ''; // 子应用标识
    // keepAlive:boolean = true; // dom移除是否保活

    checkUrlStop:()=>void; // URL 校验关闭

    static get observedAttributes () {
        return ['name', 'url'];
    }

    constructor () {
        super();
    }

    // 组件刷新
    reload () {
        const app = getAppFromInstance(this.name);
        if(app) {
            app.reload();
        }
    }

    // 子应用添加至页面
    connectedCallback () {
        if(!appIsExist(this.name) && this.url) {
            nextTick(() => {
                new App({
                    url: this.url,
                    name: this.name,
                    container: this
                });
            });
        }
        // 开启URL校验
        this.checkUrlStop = addEventListenerUrl(()=>{
            checkUrl(this.name);
        });
    }

    // 子应用从页面中移除
    disconnectedCallback () {
        // 关闭URL校验
        this.checkUrlStop?.();
        // getAppFromInstance(this.name)?.unmount(true);
    }

    // 子应用移动至新页面。
    adoptedCallback () {}

    /**
     * 属性变化
     * @param name 属性名
     * @param oldValue 属性旧值
     * @param newValue 属性新值
     */
    attributeChangedCallback (name, oldValue, newValue) {
        if(!this.name && name === 'name') {
            if(newValue === MainMustardApp) {
                throw new Error('子应用标识非法');
            }
            if(appIsExist(name)) {
                throw new Error(`子应用标识已存在: ${name}`);
            }else{
                this.name = newValue;
            }
        }else if(!this.url && name === 'url') {
            this.url = newValue;
        }
    }

    setAttribute (key:string, value:unknown) {
        if (/^mustard-app/i.test(this.tagName) && key === 'data') {
            // 发送数据
            eventCenter.dispatch(this.name, value);
        } else {
            rawSetAttribute.call(this, key, value);
        }
    }

    addEventListener (type: EventListenerType, listener: EventListenerListener, options?: EventListenerOptions): void {
        if(isFunction(listener)) {
            if(type === 'dataChange') {
                eventCenter.onData(this.name, listener);
            }else if(isIAppStatusKey(type)) {
                eventCenter.onLife(this.name, type, listener);
            }else{
                rawAddEventListener.call(this, type, listener, options);
            }
        }
    }

    removeEventListener (type: EventListenerType, listener: EventListenerListener, options?: EventListenerOptions): void {
        if(isFunction(listener)) {
            if(type === 'dataChange') {
                eventCenter.offData(this.name, listener);
            }else if(isIAppStatusKey(type)) {
                eventCenter.offLife(this.name, type, listener);
            }else{
                rawRemoveEventListener.call(this, type, listener, options);
            }
        }
    }
}

export function defineMustardAppElement () {
    if(!customElements.get(AppName)) {
        changeDomPropety();
        changeHistoryPropety();
        customElements.define(AppName, MustardApp);
    }
}