import { global } from "../../utility";
// import { nullOrEmpty } from "../../utility/strings";
import { createElement } from "../../functions";
import { createIcon } from "../icon";
import { createCheckbox } from "../checkbox";
// import { setTooltip } from "../tooltip";
import { Dropdown } from "../dropdown";
import { convertCssStyle } from "../extension";

export class GridColumn {
    static create() {
        return createElement('span');
    }

    static setValue(element, val) {
        element.innerText = val;
    }

    static setStyle(element, style) {
        // for (let css of Object.entries(style)) {
        //     element.style.setProperty(css[0], css[1]);
        // }
        element.style.cssText = convertCssStyle(style);
    }

    static setEnabled(element, enabled) {
        const tooltip = element.querySelector('.ui-tooltip-wrapper');
        if (tooltip != null) {
            tooltip.style.display = enabled === false ? 'none' : '';
        }
    }

    static toString() { return '[object Column]' }
}

export class GridInputColumn extends GridColumn {
    static get editing() { return true };

    static createEdit(trigger, col, _wrapper, vals) {
        const input = createElement('input');
        input.setAttribute('type', 'text');
        if (typeof trigger === 'function') {
            input.addEventListener('change', trigger);
        }
        input.addEventListener('input', () => {
            if (vals.__editing == null) {
                vals.__editing = {
                    [col.key]: true
                }
            } else {
                vals.__editing[col.key] = true;
            }
        });
        return input;
    }

    static setValue(element, val) {
        if (element.tagName !== 'INPUT') {
            super.setValue(element, val);
        } else {
            element.value = val;
        }
    }

    static getValue(e) { return e.target.value }

    static setEnabled(element, enabled) {
        super.setEnabled(element, enabled);
        element.disabled = enabled === false;
    }
}

export class GridTextColumn extends GridInputColumn {
    static createEdit(trigger, col, _wrapper, vals) {
        const input = createElement('textarea');
        if (typeof trigger === 'function') {
            input.addEventListener('change', trigger);
        }
        input.addEventListener('input', () => {
            if (vals.__editing == null) {
                vals.__editing = {
                    [col.key]: true
                }
            } else {
                vals.__editing[col.key] = true;
            }
        });
        return input;
    }

    static setValue(element, val, _item, _col, grid) {
        if (element.tagName !== 'TEXTAREA') {
            super.setValue(element, val);
        } else {
            element.value = val;
            if (val != null) {
                const lines = String(val).split('\n').length;
                element.style.height = `${lines * grid.lineHeight + 12}px`;
            }
            // TODO: bad performance
        }
    }
}

const SymbolDropdown = Symbol.for('ui-dropdown');

export class GridDropdownColumn extends GridColumn {
    static createEdit(trigger, col, container, it) {
        const drop = new Dropdown({
            ...col.dropOptions,
            wrapper: container.parentElement
        });
        drop.onSelected = trigger;
        drop.onExpanded = function () {
            if (it.__editing == null) {
                it.__editing = {
                    [col.key]: true
                }
            } else {
                it.__editing[col.key] = true;
            }
            if (typeof col.onDropExpanded === 'function') {
                col.onDropExpanded.call(col, it.values, drop);
            }
        };
        return drop.create();
    }

    static _getDrop(element) {
        const dropGlobal = global[SymbolDropdown];
        if (dropGlobal == null) {
            return null;
        }
        const dropId = element.dataset.dropId;
        const drop = dropGlobal[dropId];
        if (drop == null) {
            return null;
        }
        return drop;
    }

    static _getSource(item, col) {
        let source;
        if (col.sourceCache !== false) {
            source = item.source?.[col.key];
            if (source != null) {
                return source;
            }
        }
        source = col.source;
        if (typeof source === 'function') {
            source = source(item.values);
        }
        if (col.sourceCache !== false) {
            if (item.source == null) {
                item.source = { [col.key]: source };
            } else {
                item.source[col.key] = source;
            }
        }
        return source;
    }

    static _setValue(source, element, val) {
        const data = source?.find(v => v.value === val);
        if (data != null) {
            val = data.text;
        }
        super.setValue(element, val);
    }

    static setValue(element, val, item, col) {
        if (element.tagName !== 'DIV') {
            let source = this._getSource(item, col);
            if (source instanceof Promise) {
                source.then(s => this._setValue(s, element, val));
            } else {
                this._setValue(source, element, val);
            }
            return;
        }
        const drop = this._getDrop(element);
        if (drop == null) {
            return;
        }
        if (drop.source == null || drop.source.length === 0) {
            let source = this._getSource(item, col);
            if (source instanceof Promise) {
                source.then(s => {
                    drop.source = s;
                    drop.select(val, true);
                })
                return;
            } else if (source != null) {
                drop.source = source;
            }
        }
        drop.select(val, true);
    }

    static getValue(e, col) {
        return e[col.dropOptions?.valueKey ?? 'value'];
    }

    static setEnabled(element, enabled) {
        super.setEnabled(element, enabled);
        const drop = this._getDrop(element);
        if (drop == null) {
            return;
        }
        drop.disabled = enabled === false;
    }

    static leaveEdit(element, container) {
        container.parentElement.querySelectorAll('.ui-drop-box.active').forEach(e => {
            if (e != null) {
                e.classList.remove('active');
            }
        });
        const drop = this._getDrop(element);
        if (drop == null) {
            return;
        }
        if (drop?.multiSelect && typeof drop.onCollapsed === 'function') {
            drop.onCollapsed();
        }
    }
}

export class GridCheckboxColumn extends GridColumn {
    static createEdit(trigger) {
        const check = createCheckbox({
            onchange: typeof trigger === 'function' ? trigger : null
        });
        return check;
    }

    static setValue(element, val) {
        element.querySelector('input').checked = val;
    }

    static getValue(e) { return e.target.checked }

    static setEnabled(element, enabled) {
        super.setEnabled(element, enabled);
        element.querySelector('input').disabled = enabled === false;
    }
}

export class GridIconColumn extends GridColumn {
    static create() { return createElement('span', 'col-icon') }

    static setValue(element, val, item, col) {
        let className = col.iconClassName;
        if (typeof className === 'function') {
            className = className.call(col, item.values);
        }
        if (className == null) {
            element.className = 'col-icon';
        } else {
            element.className = `col-icon ${className}`;
        }
        let type = col.iconType;
        if (typeof type === 'function') {
            type = type.call(col, item.values);
        }
        type ??= 'fa-light';
        if (element.dataset.type !== type || element.dataset.icon !== val) {
            const icon = createIcon(type, val);
            // const layer = element.children[0];
            element.replaceChildren(icon);
            // !nullOrEmpty(col.tooltip) && setTooltip(element, col.tooltip, false, grid.element);
            element.dataset.type = type;
            element.dataset.icon = val;
        }
    }

    static setEnabled(element, enabled) {
        super.setEnabled(element, enabled);
        if (enabled === false) {
            element.classList.add('disabled');
        } else {
            element.classList.remove('disabled');
        }
    }
}

export class GridDateColumn extends GridColumn {
    static createEdit(trigger, col, _container, vals) {
        let enabled = col.enabled;
        if (typeof enabled === 'string') {
            enabled = vals.values[enabled];
        } else if (typeof enabled === 'function') {
            enabled = col.enabled(vals.values);
        }
        if (enabled === false) {
            return super.create();
        }
        const date = createElement('input', 'ui-grid-date-cell');
        date.required = true;
        date.type = 'date';
        if (col.dateMin != null) {
            date.min = col.dateMin;
        }
        if (col.dateMax != null) {
            date.max = col.dateMax;
        }
        // date.addEventListener('change', trigger);
        date.addEventListener('blur', trigger);
        return date;
    }

    static setValue(element, val) {
        if (element.tagName === 'INPUT') {
            if (isNaN(val) || /^\d{4}-\d{2}-\d{2}$/.test(val)) {
                element.value = val;
            } else {
                val = new Date((val - 621355968e9) / 1e4);
                const month = String(val.getMonth() + 1).padStart(2, '0');
                const date = String(val.getDate()).padStart(2, '0');
                element.value = `${val.getFullYear()}-${month}-${date}`;
            }
        } else {
            element.innerText = this.formatDate(val);
        }
    }

    static getValue(e) {
        const date = e.target?.valueAsDate;
        if (date instanceof Date && !isNaN(date)) {
            const year = date.getFullYear();
            if (year < 1900 || year > 9999) {
                return '';
            }
            return String(date.getTime() * 1e4 + 621355968e9);
        }
        return '';
    }

    static setEnabled(element, enabled) {
        element.disabled = enabled === false;
    }

    static _resolveDate(s) {
        if (s instanceof Date) {
            return s;
        }
        const ticks = Number(s);
        if (!isNaN(ticks) && ticks > 0) {
            return new Date((ticks - 621355968e9) / 1e4);
        }
        return new Date(s);
    }

    static formatDate(date) {
        date = this._resolveDate(date);
        if (date instanceof Date && !isNaN(date)) {
            return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`;
        }
        return '';
    }
}