import { safeXss } from '@/utils/xss-util/xss-util';
import { VNode } from 'vue';

/**
 * 安全html
 *
 * @export
 * @class SafeHtml
 */
export const SafeHtml: any = {
    /**
     * 指令初始化
     *
     * @param {HTMLDivElement} el
     * @param {*} binding
     * @param {VNode} vNode
     * @param {VNode} oldVNode
     */
    bind(el: HTMLDivElement, binding: any, vNode: VNode, oldVNode: VNode) {
        shc.init(el, binding);
    },

    /**
     * 指令更新
     *
     * @param {HTMLDivElement} el
     * @param {*} binding
     * @param {VNode} vNode
     * @param {VNode} oldVNode
     */
    componentUpdated(el: HTMLDivElement, binding: any, vNode: VNode, oldVNode: VNode) {
        shc.update(el, binding);
    },
};

/**
 * 微标控制器
 *
 * @export
 * @class SafeHtmlController
 */
export class SafeHtmlController {
    /**
     * 唯一实例
     *
     * @private
     * @static
     * @memberof SafeHtmlControllerController
     */
    private static readonly instance = new SafeHtmlController();

    /**
     * 容器
     *
     * @protected
     * @type {HTMLDivElement}
     * @memberof NotificationSignalController
     */
    protected el!: HTMLElement;

    /**
     * Creates an instance of SafeHtmlControllerController.
     * @memberof SafeHtmlControllerController
     */
    private constructor() {
        if (SafeHtmlController.instance) {
            return SafeHtmlController.instance;
        }
    }

    /**
     * 初始化
     *
     * @param {HTMLDivElement}
     * @param {any}
     * @memberof SafeHtmlController
     */
    public init(el: HTMLDivElement, binding: any): void {
        const { value } = binding;
        // 先清空容器
        el.innerHTML = '';
        if (value) {
            const dom = this.parseHtml(value);
            if (dom) {
                el.appendChild(dom);
            } else {
                // 解析失败时使用xss过滤
                el.innerHTML = String(safeXss(value));
            }
        }
    }

    /**
     * 更新
     *
     * @param {HTMLDivElement}
     * @param {any}
     * @memberof SafeHtmlController
     */
    public update(el: HTMLDivElement, binding: any): void {
        const { value } = binding;
        // 先清空容器
        el.innerHTML = '';
        if (value) {
            const dom = this.parseHtml(value);
            if (dom) {
                el.appendChild(dom);
            } else {
                // 解析失败时使用xss过滤
                el.innerHTML = String(safeXss(value));
            }
        }
    }

    /**
     * @description 解析html
     * @param {string} html
     * @returns {*}  {(HTMLElement | null)}
     * @memberof SafeHtmlController
     */
    public parseHtml(html: string): HTMLElement | null {
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'application/xml');
        const parseError = doc.querySelector('parsererror');
        if (parseError) {
            if (parseError.textContent) {
                const msg = parseError.textContent
                            .replace(/\s+/g, ' ')
                            .trim();
                console.error(msg);
            }
            return null;
        }
        const dom = doc.documentElement;
        return dom;
    }

    /**
     * 获取唯一实例
     *
     * @static
     * @returns {SafeHtmlController}
     * @memberof SafeHtmlController
     */
    public static getInstance(): SafeHtmlController {
        return SafeHtmlController.instance;
    }
}

// 导出服务
export const shc: SafeHtmlController = SafeHtmlController.getInstance();
