import "./css/popup.scss";
import { r as lang } from "../utility/lgres";
import { nullOrEmpty } from "../utility/strings";
import { global } from "../utility";
import { createElement } from "../functions";
import { createIcon, changeIcon } from "./icon";
import { requestAnimationFrame } from "../ui";

const ResizeMods = {
    right: 1,
    bottom: 2,
    left: 4,
    top: 8,
    bottomRight: 2 | 1,
    bottomLeft: 2 | 4,
    topRight: 8 | 1,
    topLeft: 8 | 4
}

const IconStyle = 'fa-light';

// const Cursors = {
//     [ResizeMods.right]: 'ew-resize',
//     [ResizeMods.bottom]: 'ns-resize',
//     [ResizeMods.bottomRight]: 'nwse-resize',
//     [ResizeMods.left]: 'ew-resize',
//     [ResizeMods.bottomLeft]: 'nesw-resize',
//     [ResizeMods.top]: 'ns-resize',
//     [ResizeMods.topRight]: 'nesw-resize',
//     [ResizeMods.topLeft]: 'nwse-resize'
// }

function trimPx(px) {
    if (typeof px !== 'string') {
        return px;
    }
    if (px.endsWith('px')) {
        const size = Number(px.substring(0, px.length - 2));
        return isNaN(size) ? px : size;
    }
    return px;
}

export class Popup {
    _var = {};
    // _var.mask;
    // _var.option;
    // _var.bounds;
    // _var.cursor;

    constructor(opts = {}) {
        this._var.option = opts;
    }

    get container() { return this._var.mask.querySelector('.ui-popup-container') }

    get title() { return this._var.option.title }
    set title(title) {
        const element = this._var.mask?.querySelector('.ui-popup-container .ui-popup-header .ui-popup-header-title');
        if (element != null) {
            element.innerText = title;
        }
        this._var.option.title = title;
    }

    get loading() { return this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' }
    set loading(flag) {
        let loading = this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading');
        if (loading == null) {
            return;
        }
        if (flag === false) {
            loading.style.visibility = 'hidden';
            loading.style.opacity = 0;
        } else {
            loading.style.visibility = 'visible';
            loading.style.opacity = 1;
        }
    }

    get rect() {
        const container = this.container;
        if (container == null) {
            return null;
        }
        const style = global.getComputedStyle(container);
        const collapsed = container.classList.contains('ui-popup-collapse');
        const bounds = this._var.bounds;
        return {
            collapsed,
            left: trimPx(style.left),
            top: trimPx(style.top),
            width: collapsed === true && bounds != null ? bounds.width : trimPx(style.width),
            height: collapsed === true && bounds != null ? bounds.height : trimPx(style.height)
        };
    }
    set rect(r) {
        const container = this.container;
        if (container == null) {
            return;
        }
        const css = [];
        if (!isNaN(r.left)) {
            css.push(`left: ${r.left}px`);
        }
        if (!isNaN(r.top)) {
            css.push(`top: ${r.top}px`);
        }
        const collapse = container.querySelector('.ui-popup-header-icons>.icon-expand');
        if (r.collapsed === true) {
            css.push('width: 160px', 'height: 40px');
            this._var.bounds = r;
            container.classList.add('ui-popup-collapse');
            if (collapse != null) {
                changeIcon(collapse, IconStyle, 'expand-alt');
            }
        } else {
            if (!isNaN(r.width) && r.width > 0) {
                css.push(`width: ${r.width}px`);
            }
            if (!isNaN(r.height) && r.height > 0) {
                css.push(`height: ${r.height}px`);
            }
            container.classList.remove('ui-popup-collapse');
            this._var.bounds = null;
            if (collapse != null) {
                changeIcon(collapse, IconStyle, 'compress-alt');
            }
        }
        if (css.length > 0) {
            container.style.cssText += css.join('; ');
        }
    }

    close(result = null, animation = true) {
        const option = this._var.option;
        const mask = this._var.mask;
        const doClose = () => {
            if (option.persistent) {
                mask.style.display = 'none';
            } else {
                mask.remove();
                this._var.mask = null;
            }
        }
        if (animation) {
            mask.classList.add('ui-popup-active');
            mask.style.opacity = 0;
            setTimeout(() => { doClose(); }, 120);
        } else {
            doClose();
        }
        if (typeof option.onMasking === 'function') {
            option.onMasking.call(this, false);
        }
        if (typeof option.resolve === 'function') {
            option.resolve.call(this, {
                result,
                popup: this
            });
        }
    }

    /**
     * 创建 Popup 面板
     * @returns {HTMLDivElement} 返回遮罩元素（顶层元素）
     */
    create() {
        const mask = createElement('div', 'ui-popup-mask ui-popup-active');
        const option = this._var.option;
        if (option.mask === false) {
            mask.classList.add('ui-popup-transparent');
        } else if (typeof option.onMasking === 'function') {
            option.onMasking.call(this, true);
        }
        if (!isNaN(option.zIndex)) {
            mask.style.zIndex = String(option.zIndex);
        }
        const container = createElement('div', 'ui-popup-container');
        if (option.changeZIndex === true) {
            container.addEventListener('mousedown', () => {
                const masks = [...this._var.mask.parentElement.children].filter(e => e.classList.contains('ui-popup-mask'));
                let max = 200;
                masks.forEach(m => {
                    let index;
                    if (m.dataset.zindex != null) {
                        index = parseInt(m.dataset.zindex);
                        m.style.zIndex = isNaN(index) ? '' : String(index);
                        delete m.dataset.zindex;
                    } else {
                        index = parseInt(m.style.zIndex);
                    }
                    if (index > max) {
                        max = index;
                    }
                });
                mask.dataset.zindex = mask.style.zIndex;
                mask.style.zIndex = max + 1;
            });
        } else {

        }
        let tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0));
        if (tabIndex < 0) {
            tabIndex = 0;
        }
        container.tabIndex = tabIndex + 1;
        let content = option.content;
        if (!(content instanceof HTMLElement)) {
            content = createElement('div', d => d.innerText = content);
        }
        container.append(
            createElement('div', header => {
                header.className = 'ui-popup-header';
                let title = option.title;
                if (!(title instanceof HTMLElement)) {
                    title = createElement('div', t => {
                        if (option.movable === false) {
                            t.className = 'ui-popup-header-title no-move';
                        } else {
                            t.className = 'ui-popup-header-title';
                        }
                        t.innerText = title;
                    });
                }
                header.appendChild(title);
                if (option.movable !== false) {
                    const move = header; // title.querySelector('.ui-popup-move') ?? title;
                    move.addEventListener('mousedown', e => {
                        if (['svg', 'use'].includes(e.target?.tagName)) {
                            return;
                        }
                        if (e.buttons !== 1) {
                            return;
                        }
                        const parent = option.mask === false ? mask.parentElement : mask;
                        const x = e.clientX - container.offsetLeft;
                        const y = e.clientY - container.offsetTop;
                        let moved;
                        const move = e => {
                            if (e.buttons === 1) {
                                container.style.left = `${e.clientX - x}px`;
                                container.style.top = `${e.clientY - y}px`;
                                moved = true;
                            } else {
                                parent.dispatchEvent(new MouseEvent('mouseup'));
                            }
                        };
                        parent.addEventListener('mousemove', move, { passive: false });
                        const up = () => {
                            parent.removeEventListener('mousemove', move, { passive: false });
                            parent.removeEventListener('mouseup', up);
                            if (moved === true && typeof option.onMoveEnded === 'function') {
                                option.onMoveEnded.call(this);
                            }
                            moved = false;
                        };
                        parent.addEventListener('mouseup', up);
                    });
                }
                const icons = createElement('div', icons => {
                    icons.className = 'ui-popup-header-icons';
                    if (option.collapsable === true) {
                        const collapse = createIcon(IconStyle, 'compress-alt');
                        collapse.tabIndex = tabIndex + 2;
                        collapse.classList.add('icon-expand');
                        collapse.addEventListener('keypress', e => {
                            if (e.key === ' ' || e.key === 'Enter') {
                                collapse.dispatchEvent(new MouseEvent('click'));
                            }
                        });
                        collapse.addEventListener('click', () => {
                            if (container.classList.contains('ui-popup-collapse')) {
                                const bounds = this._var.bounds;
                                if (bounds != null) {
                                    container.style.cssText += `width: ${bounds.width}px; height: ${bounds.height}px`;
                                    this._var.bounds = null;
                                }
                                container.classList.remove('ui-popup-collapse');
                                changeIcon(collapse, IconStyle, 'compress-alt');
                            } else {
                                const rect = this.rect;
                                this._var.bounds = rect;
                                container.style.cssText += `width: 160px; height: 40px`;
                                container.classList.add('ui-popup-collapse');
                                changeIcon(collapse, IconStyle, 'expand-alt');
                            }
                            if (typeof option.onResizeEnded === 'function') {
                                option.onResizeEnded.call(this);
                            }
                        });
                        icons.appendChild(collapse);
                    }
                    if (option.closable !== false) {
                        const cancel = createIcon(IconStyle, 'times');
                        cancel.tabIndex = tabIndex + 3;
                        cancel.addEventListener('keypress', e => {
                            if (e.key === ' ' || e.key === 'Enter') {
                                this.close();
                            }
                        });
                        cancel.addEventListener('click', () => this.close());
                        icons.appendChild(cancel);
                    }
                });
                header.appendChild(icons);
            }),
            createElement('div', 'ui-popup-body', content, createElement('div', 'ui-popup-loading',
                createElement('div', null, createIcon(IconStyle, 'spinner-third'))
            ))
        );
        if (Array.isArray(option.buttons) && option.buttons.length > 0) {
            tabIndex = Math.max.apply(null, [...container.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0));
            container.appendChild(
                createElement('div', 'ui-popup-footer', ...option.buttons.map((b, i) => {
                    const button = createElement('button', 'ui-popup-button');
                    if (b.className != null) {
                        button.classList.add(b.className);
                    }
                    if (b.isPrimary) {
                        button.classList.add('primary');
                    }
                    if (b.tabIndex > 0) {
                        button.tabIndex = b.tabIndex;
                    } else {
                        button.tabIndex = tabIndex + i + 1;
                    }
                    button.innerText = b.text;
                    button.addEventListener('click', () => {
                        if (typeof b.trigger === 'function') {
                            const result = b.trigger(this);
                            if (typeof result?.then === 'function') {
                                result.then(r => {
                                    if (r !== false) {
                                        this.close(r);
                                    }
                                }).catch(reason => console.warn(reason));
                            } else if (result !== false) {
                                this.close(result);
                            }
                        } else {
                            this.close(b.key ?? i);
                        }
                    });
                    return button;
                }))
            );
            const tabs = [...container.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0);
            const tabMin = Math.min.apply(null, tabs);
            const tabMax = Math.max.apply(null, tabs);
            const last = container.querySelector(`[tabindex="${tabMax}"]`);
            if (last != null) {
                last.addEventListener('keydown', e => {
                    if (e.key === 'Tab') {
                        const first = container.querySelector(`[tabindex="${tabMin}"]`);
                        first?.focus();
                        e.preventDefault();
                    }
                });
            }
        } else {
            container.querySelector('.ui-popup-body>.ui-popup-loading').classList.add('ui-popup-loading-content');
        }
        // resizable
        if (option.resizable === true) {
            container.append(
                createElement('layer', layer => {
                    layer.className = 'ui-popup-border ui-popup-border-right';
                    layer.addEventListener('mousedown', e => this._resize(ResizeMods.right, e));
                }),
                createElement('layer', layer => {
                    layer.className = 'ui-popup-border ui-popup-border-bottom';
                    layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottom, e));
                }),
                createElement('layer', layer => {
                    layer.className = 'ui-popup-border ui-popup-border-left';
                    layer.addEventListener('mousedown', e => this._resize(ResizeMods.left, e));
                }),
                createElement('layer', layer => {
                    layer.className = 'ui-popup-border ui-popup-border-top';
                    layer.addEventListener('mousedown', e => this._resize(ResizeMods.top, e));
                }),
                createElement('layer', layer => {
                    layer.className = 'ui-popup-border ui-popup-border-bottom-right';
                    layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottomRight, e));
                }),
                createElement('layer', layer => {
                    layer.className = 'ui-popup-border ui-popup-border-bottom-left';
                    layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottomLeft, e));
                }),
                createElement('layer', layer => {
                    layer.className = 'ui-popup-border ui-popup-border-top-left';
                    layer.addEventListener('mousedown', e => this._resize(ResizeMods.topLeft, e));
                }),
                createElement('layer', layer => {
                    layer.className = 'ui-popup-border ui-popup-border-top-right';
                    layer.addEventListener('mousedown', e => this._resize(ResizeMods.topRight, e));
                })
            )
        }
        mask.appendChild(container);
        this._var.mask = mask;
        return mask;
    }

    show(parent = document.body, hidden = false) {
        if (parent == null) {
            return;
        }
        let mask = this._var.mask;
        if (mask == null) {
            mask = this._var.mask = this.create();
        }
        if (mask.parentElement == null) {
            // const exists = [...parent.children].filter(e => e.classList.contains('ui-popup-mask'));
            const exists = parent.querySelectorAll('.ui-popup-mask');
            let zindex = 0;
            for (let ex of exists) {
                let z = parseInt(global.getComputedStyle(ex).zIndex);
                if (!isNaN(z) && z > zindex) {
                    zindex = z;
                }
            }
            if (zindex > 0) {
                mask.style.zIndex = String(zindex + 1);
            }
            parent.appendChild(mask);
            if (hidden === true) {
                mask.style.display = 'none';
                return Promise.resolve(mask);
            }
        }
        if (this._var.option.mask === false) {
            // calculator position
            const container = this.container;
            container.style.left = String((parent.offsetWidth - container.offsetWidth) / 2) + 'px';
            container.style.top = String((parent.offsetHeight - container.offsetHeight) / 2) + 'px';
        }
        return new Promise(resolve => {
            mask.style.display = '';
            requestAnimationFrame(() => {
                mask.classList.remove('ui-popup-active');
                mask.style.opacity = 1;
                this.container.focus();
                setTimeout(() => resolve(mask), 120);
            });
        });
    }

    _resize(mod, e) {
        if (e.buttons !== 1) {
            return;
        }
        const container = this.container;
        const option = this._var.option;
        if (typeof option.onResizeStarted === 'function') {
            option.onResizeStarted.call(this);
        }
        const mask = this._var.mask;
        // this._var.cursor = mask.style.cursor;
        // mask.style.cursor = Cursors[mod];
        const originalX = e.clientX;
        const originalY = e.clientY;
        const original = {
            width: container.offsetWidth,
            height: container.offsetHeight,
            left: container.offsetLeft,
            top: container.offsetTop
        };
        const minWidth = option.minWidth ?? 200;
        const minHeight = option.minHeight ?? 200;
        let resized;
        const parent = option.mask === false ? mask.parentElement : mask;
        const move = e => {
            if (e.buttons !== 1) {
                parent.dispatchEvent(new MouseEvent('mouseup'));
                return;
            }
            const offsetX = e.clientX - originalX;
            const offsetY = e.clientY - originalY;
            let width = original.width;
            let height = original.height;
            let x = original.left;
            let y = original.top;
            if ((mod & ResizeMods.right) === ResizeMods.right) {
                width += offsetX;
                if (width < minWidth) {
                    width = minWidth;
                }
            }
            if ((mod & ResizeMods.bottom) === ResizeMods.bottom) {
                height += offsetY;
                if (height < minHeight) {
                    height = minHeight;
                }
            }
            if ((mod & ResizeMods.left) === ResizeMods.left) {
                width -= offsetX;
                if (width < minWidth) {
                    width = minWidth;
                    x = originalX + original.width - minWidth;
                } else {
                    x += offsetX;
                }
            }
            if ((mod & ResizeMods.top) === ResizeMods.top) {
                height -= offsetY;
                if (height < minHeight) {
                    height = minHeight;
                    y = originalY + original.height - minHeight;
                } else {
                    y += offsetY;
                }
            }
            if (typeof option.onResizing === 'function') {
                option.onResizing.call(this, x, y, width, height);
            } else {
                container.style.cssText += `left: ${x}px; top: ${y}px; width: ${width}px; height: ${height}px`;
            }
            resized = true;
        }
        parent.addEventListener('mousemove', move, { passive: false });
        const up = () => {
            parent.removeEventListener('mousemove', move, { passive: false });
            parent.removeEventListener('mouseup', up);
            // mask.style.cursor = this._var.cursor;
            if (resized === true && typeof option.onResizeEnded === 'function') {
                option.onResizeEnded.call(this);
            }
            resized = false;
        };
        parent.addEventListener('mouseup', up);
    }
}

export function createPopup(title, content, ...buttons) {
    const popup = new Popup({
        title,
        content,
        buttons
    });
    return popup;
}

/**
 * 解析对话框元素
 * @param {HTMLElement | string} wrapper - 解析该 `.dialog` 元素
 * @param {Function} [callback] - 关闭对话框时的回调
 * @param {boolean} [removable] - 是否可移除
 * @param {number} [zIndex] - 对话框默认 `z-index`
 * @returns {Popup} 返回弹出框字典
 */
export function resolvePopup(wrapper, callback, removable, zIndex) {
    if (typeof wrapper === 'string') {
        wrapper = document.querySelector(wrapper);
    }
    if (wrapper == null) {
        return null;
    }
    if (!wrapper.classList.contains('dialog')) {
        return null;
    }
    const title = wrapper.querySelector('.dialog-title>.title')?.innerText;
    const content = wrapper.querySelector('.dialog-title+div');
    const buttons = [...wrapper.querySelectorAll('.dialog-func>input[type="button"]')].reverse().map(b => ({
        tabIndex: b.tabIndex,
        text: b.value,
        isPrimary: b.dataset.primary != null,
        trigger: b.onclick == null ? null : (popup => (b.onclick.call(popup), false))
    }));
    const popup = new Popup({
        title,
        content,
        persistent: !removable,
        resolve: typeof callback === 'function' ? (result => callback(result)) : null,
        zIndex: wrapper.zIndex ?? zIndex,
        buttons
    });
    popup.show(document.body, true);
    return popup;
}

const iconTypes = {
    'info': 'info-circle',
    'information': 'info-circle',
    'warn': 'exclamation-triangle',
    'warning': 'exclamation-triangle',
    'question': 'question-circle',
    'error': 'times-circle'
}

export function showAlert(title, message, iconType = 'info', parent = document.body) {
    const r = typeof GetTextByKey === 'function' ? GetTextByKey : lang;
    return new Promise(resolve => {
        const popup = new Popup({
            title,
            content: createElement('div', 'message-wrapper',
                createIcon('fa-solid', iconTypes[iconType] ?? 'info-circle'),
                createElement('span', span => span.innerText = message)
            ),
            resolve,
            buttons: [
                { text: r('ok', 'OK'), isPrimary: true }
            ]
        });
        popup.show(parent).then(mask => {
            const button = mask.querySelector('.ui-popup-container .ui-popup-footer .ui-popup-button');
            button?.focus();
        });
    });
}

export function showInput(title, multiline, message, def, placeholder, buttons, parent = document.body) {
    const r = typeof GetTextByKey === 'function' ? GetTextByKey : lang;
    return new Promise(resolve => {
        let tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0));
        if (tabIndex < 0) {
            tabIndex = 0;
        }
        const input = multiline ?
            createElement('textarea', text => {
                text.className = 'ui-text';
            }) :
            createElement('input', input => {
                input.type = 'text';
                input.className = 'ui-input';
            });
        input.tabIndex = tabIndex + 4;
        if (!nullOrEmpty(placeholder)) {
            input.placeholder = placeholder;
        }
        if (!nullOrEmpty(def)) {
            input.value = def;
        }
        const popup = new Popup({
            title,
            content: createElement('div', 'message-wrapper message-wrapper-input',
                nullOrEmpty(message) ? '' : createElement('span', span => span.innerText = message),
                input
            ),
            resolve: r => resolve(r.result === 'yes' ? input.value : null),
            buttons: buttons ?? [
                { key: 'yes', text: r('yes', 'Yes'), isPrimary: true },
                { key: 'no', text: r('no', 'No') }
            ]
        });
        popup.show(parent).then(() => input.focus());
    });
}

export function showConfirm(title, content, buttons, iconType = 'question', parent = document.body) {
    const r = typeof GetTextByKey === 'function' ? GetTextByKey : lang;
    return new Promise(resolve => {
        const wrapper = createElement('div', 'message-wrapper');
        if (!nullOrEmpty(iconType)) {
            wrapper.appendChild(createIcon('fa-solid', iconTypes[iconType] ?? 'question-circle'));
        }
        wrapper.appendChild(content instanceof HTMLElement ?
            content :
            createElement('span', span => span.innerText = content));
        const popup = new Popup({
            title,
            content: wrapper,
            resolve,
            buttons: buttons?.map((b, i) => {
                return {
                    text: b.text,
                    isPrimary: b.isPrimary,
                    trigger: p => {
                        let result;
                        if (typeof b.trigger === 'function') {
                            result = b.trigger(p, b);
                        } else {
                            result = b.key ?? i;
                        }
                        return result;
                    }
                };
            }) ??
                [
                    { key: 'yes', text: r('yes', 'Yes'), isPrimary: true },
                    { key: 'no', text: r('no', 'No') }
                ]
        });
        popup.show(parent).then(mask => {
            const button = mask.querySelector('.ui-popup-container .ui-popup-footer .ui-popup-button:first-child');
            button?.focus();
        });
    });
}