/**
 * FilterListManager — reusable class that encapsulates filter list DOM
 * manipulation (add, remove, move, toggle-expand, update option, render,
 * populate select, collect, auto-name).
 *
 * Both the PP template modal and the CSPT modal create an instance,
 * parameterised by which filter array, filter definitions, DOM IDs, etc.
 */

import { t } from './i18n.ts';
import { escapeHtml } from './api.ts';
import { IconSelect } from './icon-select.ts';
import * as P from './icon-paths.ts';

const _FILTER_ICONS = {
    brightness:           P.sunDim,
    saturation:           P.palette,
    gamma:                P.sun,
    downscaler:           P.monitor,
    pixelate:             P.layoutDashboard,
    auto_crop:            P.target,
    flip:                 P.rotateCw,
    color_correction:     P.palette,
    filter_template:      P.fileText,
    frame_interpolation:  P.fastForward,
    noise_gate:           P.volume2,
    palette_quantization: P.sparkles,
    css_filter_template:  P.fileText,
    hsl_shift:            P.rainbow,
    contrast:             P.slidersHorizontal,
    temporal_blur:        P.timer,
};

export { _FILTER_ICONS };

export interface FilterListOpts {
    getFilters: () => any[];
    getFilterDefs: () => any[];
    getFilterName: (filterId: string) => string;
    selectId: string;
    containerId: string;
    prefix: string;
    editingIdInputId: string;
    selfRefFilterId: string;
    autoNameFn?: () => void;
    initDrag?: (containerId: string, filtersArr: any[], rerenderFn: () => void) => void;
    initPaletteGrids?: (containerEl: HTMLElement) => void;
}

export class FilterListManager {
    _getFilters: () => any[];
    _getFilterDefs: () => any[];
    _getFilterName: (filterId: string) => string;
    _selectId: string;
    _containerId: string;
    _prefix: string;
    _editingIdInputId: string;
    _selfRefFilterId: string;
    _autoNameFn: (() => void) | null;
    _initDrag: ((containerId: string, filtersArr: any[], rerenderFn: () => void) => void) | null;
    _initPaletteGrids: ((containerEl: HTMLElement) => void) | null;
    _iconSelect: IconSelect | null;

    constructor(opts: FilterListOpts) {
        this._getFilters = opts.getFilters;
        this._getFilterDefs = opts.getFilterDefs;
        this._getFilterName = opts.getFilterName;
        this._selectId = opts.selectId;
        this._containerId = opts.containerId;
        this._prefix = opts.prefix;
        this._editingIdInputId = opts.editingIdInputId;
        this._selfRefFilterId = opts.selfRefFilterId;
        this._autoNameFn = opts.autoNameFn || null;
        this._initDrag = opts.initDrag || null;
        this._initPaletteGrids = opts.initPaletteGrids || null;
        this._iconSelect = null;
    }

    /** Get the current IconSelect instance (for external access if needed). */
    get iconSelect() { return this._iconSelect; }

    /** Populate the filter <select> and attach/update IconSelect grid. */
    populateSelect(onChangeCallback: (value: string) => void) {
        const select = document.getElementById(this._selectId) as HTMLSelectElement;
        const filterDefs = this._getFilterDefs();
        select.innerHTML = `<option value="">${t('filters.select_type')}</option>`;
        const items: { value: string; icon: string; label: string; desc: string }[] = [];
        for (const f of filterDefs) {
            const name = this._getFilterName(f.filter_id);
            select.innerHTML += `<option value="${f.filter_id}">${name}</option>`;
            const pathData = _FILTER_ICONS[f.filter_id] || P.wrench;
            items.push({
                value: f.filter_id,
                icon: `<svg class="icon" viewBox="0 0 24 24">${pathData}</svg>`,
                label: name,
                desc: t(`filters.${f.filter_id}.desc`),
            });
        }
        if (this._iconSelect) {
            this._iconSelect.updateItems(items);
        } else if (items.length > 0) {
            this._iconSelect = new IconSelect({
                target: select,
                items,
                columns: 3,
                placeholder: t('filters.select_type'),
                onChange: onChangeCallback,
            });
        }
    }

    /**
     * Render the filter list into the container.
     */
    render() {
        const container = document.getElementById(this._containerId) as HTMLElement;
        const filtersArr = this._getFilters();
        const filterDefs = this._getFilterDefs();

        if (filtersArr.length === 0) {
            container.innerHTML = `<div class="pp-filter-empty">${t('filters.empty')}</div>`;
            return;
        }

        const toggleFn = this._prefix ? `${this._prefix}ToggleFilterExpand` : 'toggleFilterExpand';
        const removeFn = this._prefix ? `${this._prefix}RemoveFilter` : 'removeFilter';
        const updateFn = this._prefix ? `${this._prefix}UpdateFilterOption` : 'updateFilterOption';
        const inputPrefix = this._prefix ? `${this._prefix}-filter` : 'filter';
        const nameFn = this._getFilterName;

        let html = '';
        filtersArr.forEach((fi, index) => {
            const filterDef = filterDefs.find(f => f.filter_id === fi.filter_id);
            const filterName = nameFn(fi.filter_id);
            const isExpanded = fi._expanded === true;

            let summary = '';
            if (filterDef && !isExpanded) {
                summary = filterDef.options_schema.map(opt => {
                    const val = fi.options[opt.key] !== undefined ? fi.options[opt.key] : opt.default;
                    if (opt.type === 'select' && Array.isArray(opt.choices)) {
                        const choice = opt.choices.find(c => c.value === val);
                        if (choice) return choice.label;
                    }
                    return val;
                }).join(', ');
            }

            html += `<div class="pp-filter-card${isExpanded ? ' expanded' : ''}" data-filter-index="${index}">
                <div class="pp-filter-card-header" onclick="${toggleFn}(${index})">
                    <span class="pp-filter-drag-handle" title="${t('filters.drag_to_reorder')}">&#x2807;</span>
                    <span class="pp-filter-card-chevron">${isExpanded ? '&#x25BC;' : '&#x25B6;'}</span>
                    <span class="pp-filter-card-name">${escapeHtml(filterName)}</span>
                    ${summary ? `<span class="pp-filter-card-summary">${escapeHtml(summary)}</span>` : ''}
                    <div class="pp-filter-card-actions" onclick="event.stopPropagation()">
                        <button type="button" class="btn-filter-action btn-filter-remove" onclick="${removeFn}(${index})" title="${t('filters.remove')}">&#x2715;</button>
                    </div>
                </div>
                <div class="pp-filter-card-options"${isExpanded ? '' : ' style="display:none"'}>`;

            if (filterDef) {
                for (const opt of filterDef.options_schema) {
                    const currentVal = fi.options[opt.key] !== undefined ? fi.options[opt.key] : opt.default;
                    const inputId = `${inputPrefix}-${index}-${opt.key}`;
                    if (opt.type === 'bool') {
                        const checked = currentVal === true || currentVal === 'true';
                        html += `<div class="pp-filter-option pp-filter-option-bool">
                            <span class="pp-filter-option-label">${escapeHtml(opt.label)}</span>
                            <label class="settings-toggle" for="${inputId}">
                                <input type="checkbox" id="${inputId}" ${checked ? 'checked' : ''}
                                    onchange="${updateFn}(${index}, '${opt.key}', this.checked)">
                                <span class="settings-toggle-slider"></span>
                            </label>
                        </div>`;
                    } else if (opt.type === 'select' && Array.isArray(opt.choices)) {
                        const editingId = (document.getElementById(this._editingIdInputId) as HTMLInputElement)?.value || '';
                        const filteredChoices = (fi.filter_id === this._selfRefFilterId && opt.key === 'template_id' && editingId)
                            ? opt.choices.filter(c => c.value !== editingId)
                            : opt.choices;
                        let selectVal = currentVal;
                        if (filteredChoices.length > 0 && !filteredChoices.some(c => c.value === selectVal)) {
                            selectVal = filteredChoices[0].value;
                            fi.options[opt.key] = selectVal;
                        }
                        const hasPaletteColors = filteredChoices.some(c => c.colors);
                        const options = filteredChoices.map(c =>
                            `<option value="${escapeHtml(c.value)}"${c.value === selectVal ? ' selected' : ''}>${escapeHtml(c.label)}</option>`
                        ).join('');
                        const gridAttr = hasPaletteColors ? ` data-palette-grid="${escapeHtml(JSON.stringify(filteredChoices))}"` : '';
                        const isTemplateRef = opt.key === 'template_id';
                        const entityAttr = isTemplateRef ? ' data-entity-select="template"' : '';
                        html += `<div class="pp-filter-option">
                            <label for="${inputId}"><span>${escapeHtml(opt.label)}:</span></label>
                            <select id="${inputId}"${gridAttr}${entityAttr}
                                onchange="${updateFn}(${index}, '${opt.key}', this.value)">
                                ${options}
                            </select>
                        </div>`;
                    } else if (opt.type === 'string') {
                        const maxLen = opt.max_length || 500;
                        html += `<div class="pp-filter-option">
                            <label for="${inputId}"><span>${escapeHtml(opt.label)}:</span></label>
                            <input type="text" id="${inputId}" value="${escapeHtml(String(currentVal))}"
                                maxlength="${maxLen}" class="pp-filter-text-input"
                                onchange="${updateFn}(${index}, '${opt.key}', this.value)">
                        </div>`;
                    } else {
                        html += `<div class="pp-filter-option">
                            <label for="${inputId}">
                                <span>${escapeHtml(opt.label)}:</span>
                                <span id="${inputId}-display">${currentVal}</span>
                            </label>
                            <input type="range" id="${inputId}"
                                min="${opt.min_value}" max="${opt.max_value}" step="${opt.step}" value="${currentVal}"
                                oninput="${updateFn}(${index}, '${opt.key}', this.value); document.getElementById('${inputId}-display').textContent = this.value;">
                        </div>`;
                    }
                }
            }

            html += `</div></div>`;
        });

        container.innerHTML = html;
        if (this._initDrag) {
            this._initDrag(this._containerId, filtersArr, () => this.render());
        }
        if (this._initPaletteGrids) {
            this._initPaletteGrids(container);
        }
    }

    /**
     * Add a filter from the select element into the filters array.
     */
    addFromSelect() {
        const select = document.getElementById(this._selectId) as HTMLSelectElement;
        const filterId = select.value;
        if (!filterId) return;

        const filterDefs = this._getFilterDefs();
        const filterDef = filterDefs.find(f => f.filter_id === filterId);
        if (!filterDef) return;

        const options = {};
        for (const opt of filterDef.options_schema) {
            if (opt.type === 'select' && !opt.default && Array.isArray(opt.choices) && opt.choices.length > 0) {
                options[opt.key] = opt.choices[0].value;
            } else {
                options[opt.key] = opt.default;
            }
        }

        const filtersArr = this._getFilters();
        filtersArr.push({ filter_id: filterId, options, _expanded: true });
        select.value = '';
        if (this._iconSelect) this._iconSelect.setValue('');
        this.render();
        if (this._autoNameFn) this._autoNameFn();
    }

    /**
     * Toggle expand/collapse of a filter card.
     */
    toggleExpand(index: number) {
        const filtersArr = this._getFilters();
        if (filtersArr[index]) {
            filtersArr[index]._expanded = !filtersArr[index]._expanded;
            this.render();
        }
    }

    /**
     * Remove a filter at the given index.
     */
    remove(index: number) {
        const filtersArr = this._getFilters();
        filtersArr.splice(index, 1);
        this.render();
        if (this._autoNameFn) this._autoNameFn();
    }

    /** Move a filter up or down by swapping with its neighbour. */
    move(index: number, direction: number) {
        const filtersArr = this._getFilters();
        const newIndex = index + direction;
        if (newIndex < 0 || newIndex >= filtersArr.length) return;
        const tmp = filtersArr[index];
        filtersArr[index] = filtersArr[newIndex];
        filtersArr[newIndex] = tmp;
        this.render();
        if (this._autoNameFn) this._autoNameFn();
    }

    /**
     * Update a single option value on a filter.
     */
    updateOption(filterIndex: number, optionKey: string, value: any) {
        const filtersArr = this._getFilters();
        const filterDefs = this._getFilterDefs();
        if (filtersArr[filterIndex]) {
            const fi = filtersArr[filterIndex];
            const filterDef = filterDefs.find(f => f.filter_id === fi.filter_id);
            if (filterDef) {
                const optDef = filterDef.options_schema.find(o => o.key === optionKey);
                if (optDef && optDef.type === 'bool') {
                    fi.options[optionKey] = !!value;
                } else if (optDef && optDef.type === 'select') {
                    fi.options[optionKey] = String(value);
                } else if (optDef && optDef.type === 'string') {
                    fi.options[optionKey] = String(value);
                } else if (optDef && optDef.type === 'int') {
                    fi.options[optionKey] = parseInt(value);
                } else {
                    fi.options[optionKey] = parseFloat(value);
                }
            } else {
                fi.options[optionKey] = parseFloat(value);
            }
        }
    }

    /**
     * Collect the current filters into a clean array (no internal _expanded flag).
     */
    collect() {
        return this._getFilters().map(fi => ({
            filter_id: fi.filter_id,
            options: { ...fi.options },
        }));
    }
}
