/**
 * 基于Vue 3的扩展支持
 */
import Tnx, {util} from '../../tnxcore/src/tnxcore.ts';
import Text from './components/Text.vue';
import Percent from './components/Percent.vue';
import ScrollView from './components/ScrollView.vue';
import * as Vue from 'vue';
import mitt, {Emitter, EventType} from 'mitt';
import {Router, useRoute} from 'vue-router';
import {VueRouter} from './tnxvue-router.ts';

export {util};

export type EventBus = Emitter<Record<EventType, unknown>> & {
    once(name: EventType, handler: (event: unknown) => void): void;
};

export type ButtonOptions = {
    text: string;
    click: ((yes: boolean, close: () => void) => boolean | undefined) | ((close: () => void) => boolean | undefined);
    type?: string;
}

export type DialogOptions = {
    type?: OpenType;
    theme?: string;
    click?: boolean | ((yes: boolean, close: () => void) => boolean | undefined) | ((close: () => void) => boolean | undefined);
    buttonText?: string | string[];
    buttons?: ButtonOptions[];
    width?: string | number;
    height?: string;
}

export type OpenType = 'alert' | 'confirm' | 'close' | 'none';

export type DrawerOptions = DialogOptions & {
    placement?: 'bottom' | 'right' | 'top' | 'left';
}

export type OpenOptions = DialogOptions & {
    mode?: 'dialog' | 'drawer';
    title?: string;
}

export default class TnxVue extends Tnx {

    router: VueRouter;
    eventBus: EventBus;

    /**
     * 需要注册到Vue中的组件清单
     */
    components: Record<string, Vue.Component> = {
        Text,
        Percent,
        ScrollView,
    };

    constructor(apiBaseUrl: string, id: string = 'tnxvue') {
        super(apiBaseUrl, id);
        this.libs.Vue = Vue;
    }

    route(): ReturnType<typeof useRoute> {
        return useRoute();
    }

    install(app: Vue.App): void {
        for (let key of Object.keys(this.components)) {
            const component = this.components[key];
            app.component(component.name, component);
        }
    }

    createVueApp(rootComponent: Vue.Component, router?: Router, rootProps?: Record<string, any>): Vue.App {
        let app = Vue.createApp(rootComponent, rootProps);
        app.use(this);
        if (router) {
            app.use(router);
            this.router = app.config.globalProperties.$router as VueRouter;
        } else if (this.router) {
            app.config.globalProperties.$router = this.router;
        }

        if (!this.eventBus) {
            this.eventBus = mitt() as EventBus;
        }
        this.eventBus.once = (name: EventType, handler: (event: unknown) => void) => {
            this.eventBus.all.set(name, [handler]);
        }

        return app;
    }

    /**
     * 深度监听指定对象，在created()中调用才能生效
     * @param vm vue页面实例
     * @param target 要监听的对象
     * @param handler 处理函数
     */
    deepWatch(vm: Vue.ComponentPublicInstance, target: object, handler: (newValue: any, oldValue: any, path: string) => void): void {
        vm.$watch(() => {
            try {
                return JSON.stringify(target);
            } catch (e) {
                console.error(e);
            }
            return undefined;
        }, (newValue: string, oldValue: string): void => {
            try {
                if (newValue !== oldValue) {
                    const newObject = JSON.parse(newValue);
                    const oldObject = JSON.parse(oldValue);
                    this.deepCompare(newObject, oldObject, '', handler);
                }
            } catch (e) {
                console.error(e);
            }
        }, {deep: true});
    }

    deepCompare(object1: object, object2: object, path: string, handler: (newValue: any, oldValue: any, path: string) => void): void {
        if (object1) {
            const keys = Object.keys(object1);
            keys.forEach(key => {
                const fullPath = path ? `${path}.${key}` : key;
                if (object2) {
                    if (Array.isArray(object1[key]) && Array.isArray(object2[key])) {
                        object1[key].forEach((item, index) => {
                            this.deepCompare(item, object2[key][index], `${fullPath}[${index}]`, handler);
                        });
                    } else if (typeof object1[key] === 'object' && typeof object2[key] === 'object') {
                        this.deepCompare(object1[key], object2[key], fullPath, handler);
                    } else if (object1[key] !== object2[key]) {
                        handler(object1[key], object2[key], fullPath);
                    }
                } else {
                    handler(object1[key], undefined, fullPath);
                }
            });
        }
    }

    nextTickTimeout(vm: Vue.ComponentPublicInstance, handler: () => void, timeout?: number) {
        vm.$nextTick().then(() => {
            setTimeout(handler, timeout);
        });
    }

    /**
     * 判断指定对象是否组件实例
     * @param obj 对象
     * @returns {boolean} 是否组件实例
     */
    isComponent(obj: any): boolean {
        return (typeof obj === 'object') && (typeof obj.render === 'function');
    }

    dialog(content: string | Vue.Component,
           title?: string,
           options: DialogOptions = {},
           contentProps: Record<string, any> = {}) {
        const buttons = this.getOptionsButtons(options);
        // 默认不实现，由UI框架扩展层实现
        throw new Error('Unsupported function');
    }

    protected getOptionsButtons(options: DialogOptions): ButtonOptions[] {
        const buttons = options.buttons || this.getDefaultDialogButtons(options.type, options.click, options.theme);
        if (options.buttonText) {
            if (!Array.isArray(options.buttonText)) {
                options.buttonText = [options.buttonText];
            }
            for (let i = 0; i < buttons.length; i++) {
                let buttonText = options.buttonText[i];
                if (buttonText) {
                    buttons[i].text = buttonText;
                }
            }
        }
        return buttons;
    }

    protected getDefaultDialogButtons(
        type: OpenType,
        click: boolean | string | ((yes: boolean, close: () => void) => boolean | undefined) | ((close: () => void) => boolean | undefined),
        theme?: string): ButtonOptions[] {
        if (click !== false) {
            if (type === 'none') {
                return [];
            } else if (type === 'confirm') {
                return [{
                    text: '确定',
                    type: theme || 'primary',
                    click(close: () => void) {
                        if (typeof click === 'string') {
                            click = this[click];
                        }
                        if (typeof click === 'function') {
                            return click.call(this, true, close);
                        }
                    }
                }, {
                    text: '取消',
                    click(close: () => void) {
                        if (typeof click === 'string') {
                            click = this[click];
                        }
                        if (typeof click === 'function') {
                            return click.call(this, false, close);
                        }
                    }
                }];
            } else if (type === 'close') {
                return [{
                    text: '关闭',
                    type: theme,
                    click(close: () => void) {
                        if (typeof click === 'string') {
                            click = this[click];
                        }
                        if (typeof click === 'function') {
                            return click.call(this, close);
                        }
                    }
                }];
            } else {
                return [{
                    text: '确定',
                    type: theme || 'primary',
                    click(close: () => void) {
                        if (typeof click === 'string') {
                            click = this[click];
                        }
                        if (typeof click === 'function') {
                            return click.call(this, close);
                        }
                    }
                }];
            }
        }
        return [];
    }

    closeDialog(all?: boolean): void {
        // 默认不实现，由UI框架扩展层实现
        throw new Error('Unsupported function');
    }

    drawer(content: string | Vue.Component,
           title?: string,
           options: DrawerOptions = {},
           contentProps: Record<string, any> = {}) {
        const buttons = this.getOptionsButtons(options);
        // 默认不实现，由UI框架扩展层实现
        throw new Error('Unsupported function');
    }

    closeDrawer(all?: boolean): void {
        // 默认不实现，由UI框架扩展层实现
        throw new Error('Unsupported function');
    }

    showLoading(message?: string): Promise<void> {
        // 默认不实现，由UI框架扩展层实现
        throw new Error('Unsupported function');
    }

    closeLoading(): void {
        // 默认不实现，由UI框架扩展层实现
        throw new Error('Unsupported function');
    }

    hideLoading(): void {
        this.closeLoading();
    }

    open(component: Vue.Component, props?: Record<string, any>, options: OpenOptions = {}) {
        const c = component as any;
        if (typeof c.openOptions === 'function') {
            options = Object.assign({}, c.openOptions(props), options);
        } else if (c.openOptions) {
            options = Object.assign({}, c.openOptions, options);
        }

        let mode = options.mode;
        delete options.mode;
        const title = options.title;
        delete options.title;
        if (mode === 'drawer') {
            return this.drawer(component, title, options, props);
        }
        return this.dialog(component, title, options, props);
    }

}
