/**
 * Color Strip Sources — Composite layer helpers.
 * Extracted from color-strips.ts to reduce file size.
 */

import { escapeHtml } from '../core/api.ts';
import { _cachedValueSources, _cachedCSPTemplates } from '../core/state.ts';
import { t } from '../core/i18n.ts';
import {
    getColorStripIcon, getValueSourceIcon,
    ICON_SPARKLES,
} from '../core/icons.ts';
import * as P from '../core/icon-paths.ts';
import { IconSelect } from '../core/icon-select.ts';
import { EntitySelect } from '../core/entity-palette.ts';

const _icon = (d: any) => `<svg class="icon" viewBox="0 0 24 24">${d}</svg>`;

/* ── Composite layer state ────────────────────────────────────── */

let _compositeLayers: any[] = [];
let _compositeAvailableSources: any[] = [];  // non-composite sources for layer dropdowns
let _compositeSourceEntitySelects: any[] = [];
let _compositeBrightnessEntitySelects: any[] = [];
let _compositeBlendIconSelects: any[] = [];
let _compositeCSPTEntitySelects: any[] = [];

/** Return current composite layers array (for dirty-check snapshot). */
export function compositeGetRawLayers() {
    return _compositeLayers;
}

export function compositeSetAvailableSources(sources: any[]) {
    _compositeAvailableSources = sources;
}

export function compositeGetAvailableSources() {
    return _compositeAvailableSources;
}

export function compositeDestroyEntitySelects() {
    _compositeSourceEntitySelects.forEach(es => es.destroy());
    _compositeSourceEntitySelects = [];
    _compositeBrightnessEntitySelects.forEach(es => es.destroy());
    _compositeBrightnessEntitySelects = [];
    _compositeBlendIconSelects.forEach(is => is.destroy());
    _compositeBlendIconSelects = [];
    _compositeCSPTEntitySelects.forEach(es => es.destroy());
    _compositeCSPTEntitySelects = [];
}

function _getCompositeBlendItems() {
    return [
        { value: 'normal',     icon: _icon(P.square),      label: t('color_strip.composite.blend_mode.normal'),     desc: t('color_strip.composite.blend_mode.normal.desc') },
        { value: 'add',        icon: _icon(P.sun),          label: t('color_strip.composite.blend_mode.add'),        desc: t('color_strip.composite.blend_mode.add.desc') },
        { value: 'multiply',   icon: _icon(P.moon),         label: t('color_strip.composite.blend_mode.multiply'),   desc: t('color_strip.composite.blend_mode.multiply.desc') },
        { value: 'screen',     icon: _icon(P.monitor),      label: t('color_strip.composite.blend_mode.screen'),     desc: t('color_strip.composite.blend_mode.screen.desc') },
        { value: 'overlay',    icon: _icon(P.copy),          label: t('color_strip.composite.blend_mode.overlay'),    desc: t('color_strip.composite.blend_mode.overlay.desc') },
        { value: 'soft_light', icon: _icon(P.sunDim),       label: t('color_strip.composite.blend_mode.soft_light'), desc: t('color_strip.composite.blend_mode.soft_light.desc') },
        { value: 'hard_light', icon: _icon(P.zap),          label: t('color_strip.composite.blend_mode.hard_light'), desc: t('color_strip.composite.blend_mode.hard_light.desc') },
        { value: 'difference', icon: _icon(P.activity),     label: t('color_strip.composite.blend_mode.difference'), desc: t('color_strip.composite.blend_mode.difference.desc') },
        { value: 'exclusion',  icon: _icon(P.circleOff),    label: t('color_strip.composite.blend_mode.exclusion'),  desc: t('color_strip.composite.blend_mode.exclusion.desc') },
        { value: 'override',   icon: _icon(P.eye),          label: t('color_strip.composite.blend_mode.override'),   desc: t('color_strip.composite.blend_mode.override.desc') },
    ];
}

function _getCompositeSourceItems() {
    return _compositeAvailableSources.map(s => ({
        value: s.id,
        label: s.name,
        icon: getColorStripIcon(s.source_type),
    }));
}

function _getCompositeBrightnessItems() {
    return (_cachedValueSources || []).map(v => ({
        value: v.id,
        label: v.name,
        icon: getValueSourceIcon(v.source_type),
    }));
}

function _getCompositeCSPTItems() {
    return (_cachedCSPTemplates || []).map(tmpl => ({
        value: tmpl.id,
        label: tmpl.name,
        icon: ICON_SPARKLES,
    }));
}

export function compositeRenderList() {
    const list = document.getElementById('composite-layers-list') as HTMLElement | null;
    if (!list) return;
    compositeDestroyEntitySelects();
    const vsList = _cachedValueSources || [];
    list.innerHTML = _compositeLayers.map((layer, i) => {
        const srcOptions = _compositeAvailableSources.map(s =>
            `<option value="${s.id}"${layer.source_id === s.id ? ' selected' : ''}>${escapeHtml(s.name)}</option>`
        ).join('');
        const vsOptions = `<option value="">${t('color_strip.composite.brightness.none')}</option>` +
            vsList.map(v =>
                `<option value="${v.id}"${layer.brightness_source_id === v.id ? ' selected' : ''}>${escapeHtml(v.name)}</option>`
            ).join('');
        const csptList = _cachedCSPTemplates || [];
        const csptOptions = `<option value="">${t('common.none_no_cspt')}</option>` +
            csptList.map(tmpl =>
                `<option value="${tmpl.id}"${layer.processing_template_id === tmpl.id ? ' selected' : ''}>${escapeHtml(tmpl.name)}</option>`
            ).join('');
        const canRemove = _compositeLayers.length > 1;
        const srcName = _compositeAvailableSources.find(s => s.id === layer.source_id)?.name || '—';
        const blendLabel = t(`color_strip.composite.blend_mode.${layer.blend_mode}`) || layer.blend_mode;
        return `
            <div class="composite-layer-item" data-layer-index="${i}">
                <div class="composite-layer-header">
                    <span class="composite-layer-drag-handle" title="${t('filters.drag_to_reorder')}">&#x2807;</span>
                    <span class="composite-layer-expand-btn">&#x25B6;</span>
                    <span class="composite-layer-summary">
                        <span class="composite-layer-summary-name">${escapeHtml(srcName)}</span>
                        <span class="composite-layer-summary-blend">${escapeHtml(blendLabel)}</span>
                    </span>
                    <label class="settings-toggle composite-layer-toggle">
                        <input type="checkbox" class="composite-layer-enabled" data-idx="${i}"${layer.enabled ? ' checked' : ''}>
                        <span class="settings-toggle-slider"></span>
                    </label>
                    ${canRemove
                        ? `<button type="button" class="composite-layer-remove-btn"
                                   onclick="compositeRemoveLayer(${i})" title="${t('common.delete')}">&#x2715;</button>`
                        : ''}
                </div>
                <div class="composite-layer-body-wrapper">
                <div class="composite-layer-body">
                    <div class="composite-layer-row">
                        <select class="composite-layer-source" data-idx="${i}">${srcOptions}</select>
                        <select class="composite-layer-blend" data-idx="${i}">
                            ${_getCompositeBlendItems().map(b =>
                                `<option value="${b.value}"${layer.blend_mode === b.value ? ' selected' : ''}>${b.label}</option>`
                            ).join('')}
                        </select>
                    </div>
                    <div class="composite-layer-row">
                        <label class="composite-layer-opacity-label">
                            <span>${t('color_strip.composite.opacity')}:</span>
                            <span class="composite-opacity-val">${parseFloat(layer.opacity).toFixed(2)}</span>
                        </label>
                        <input type="range" class="composite-layer-opacity" data-idx="${i}"
                               min="0" max="1" step="0.05" value="${layer.opacity}">
                    </div>
                    <div class="composite-layer-row">
                        <label class="composite-layer-brightness-label">
                            <span>${t('color_strip.composite.brightness')}:</span>
                        </label>
                        <select class="composite-layer-brightness" data-idx="${i}">${vsOptions}</select>
                    </div>
                    <div class="composite-layer-row">
                        <label class="composite-layer-brightness-label">
                            <span>${t('color_strip.composite.processing')}:</span>
                        </label>
                        <select class="composite-layer-cspt" data-idx="${i}">${csptOptions}</select>
                    </div>
                    <div class="composite-layer-row composite-layer-range-row">
                        <label class="composite-layer-range-toggle-label">
                            <input type="checkbox" class="composite-layer-range-toggle" data-idx="${i}"${(layer.start > 0 || layer.end > 0) ? ' checked' : ''}>
                            <span>${t('color_strip.composite.range')}</span>
                        </label>
                        <div class="composite-layer-range-fields${(layer.start > 0 || layer.end > 0) ? '' : ' composite-layer-range-disabled'}">
                            <label>${t('color_strip.composite.range_start')}</label>
                            <input type="number" class="composite-layer-start" data-idx="${i}"
                                   min="0" value="${layer.start || 0}"${(layer.start > 0 || layer.end > 0) ? '' : ' disabled'}>
                            <label>${t('color_strip.composite.range_end')}</label>
                            <input type="number" class="composite-layer-end" data-idx="${i}"
                                   min="0" value="${layer.end || 0}"${(layer.start > 0 || layer.end > 0) ? '' : ' disabled'}>
                        </div>
                        <label class="composite-layer-reverse-label">
                            <input type="checkbox" class="composite-layer-reverse" data-idx="${i}"${layer.reverse ? ' checked' : ''}>
                            <span>${t('color_strip.composite.reverse')}</span>
                        </label>
                    </div>
                </div>
                </div>
            </div>
        `;
    }).join('');

    // Wire up expand/collapse
    list.querySelectorAll('.composite-layer-header').forEach(header => {
        const item = header.closest('.composite-layer-item') as HTMLElement;

        header.addEventListener('click', (e: Event) => {
            const target = e.target as HTMLElement;
            if (target.closest('.settings-toggle, .composite-layer-remove-btn, .composite-layer-drag-handle')) return;
            item.classList.toggle('composite-layer-expanded');
        });
    });

    // Wire up source/blend change to update summary text
    list.querySelectorAll<HTMLSelectElement>('.composite-layer-source').forEach(sel => {
        sel.addEventListener('change', () => {
            const item = sel.closest('.composite-layer-item') as HTMLElement;
            const nameEl = item.querySelector('.composite-layer-summary-name') as HTMLElement;
            const selectedOpt = sel.options[sel.selectedIndex];
            if (nameEl && selectedOpt) nameEl.textContent = selectedOpt.text;
        });
    });
    list.querySelectorAll<HTMLSelectElement>('.composite-layer-blend').forEach(sel => {
        sel.addEventListener('change', () => {
            const item = sel.closest('.composite-layer-item') as HTMLElement;
            const blendEl = item.querySelector('.composite-layer-summary-blend') as HTMLElement;
            const selectedOpt = sel.options[sel.selectedIndex];
            if (blendEl && selectedOpt) blendEl.textContent = selectedOpt.text;
        });
    });

    // Wire up range toggle: enable/disable fields, clear values when unchecked
    list.querySelectorAll<HTMLInputElement>('.composite-layer-range-toggle').forEach(el => {
        el.addEventListener('change', () => {
            const row = el.closest('.composite-layer-range-row')!;
            const fields = row.querySelector('.composite-layer-range-fields') as HTMLElement;
            const startInput = fields.querySelector('.composite-layer-start') as HTMLInputElement;
            const endInput = fields.querySelector('.composite-layer-end') as HTMLInputElement;
            if (el.checked) {
                fields.classList.remove('composite-layer-range-disabled');
                if (startInput) startInput.disabled = false;
                if (endInput) endInput.disabled = false;
            } else {
                fields.classList.add('composite-layer-range-disabled');
                if (startInput) { startInput.value = '0'; startInput.disabled = true; }
                if (endInput) { endInput.value = '0'; endInput.disabled = true; }
            }
        });
    });

    // Wire up live opacity display
    list.querySelectorAll<HTMLInputElement>('.composite-layer-opacity').forEach(el => {
        el.addEventListener('input', () => {
            const val = parseFloat(el.value);
            (el.closest('.composite-layer-row')!.querySelector('.composite-opacity-val') as HTMLElement).textContent = val.toFixed(2);
        });
    });

    // Attach IconSelect to each layer's blend mode dropdown
    const blendItems = _getCompositeBlendItems();
    list.querySelectorAll<HTMLSelectElement>('.composite-layer-blend').forEach(sel => {
        const is = new IconSelect({ target: sel, items: blendItems, columns: 2 });
        _compositeBlendIconSelects.push(is);
    });

    // Attach EntitySelect to each layer's source dropdown
    list.querySelectorAll<HTMLSelectElement>('.composite-layer-source').forEach(sel => {
        _compositeSourceEntitySelects.push(new EntitySelect({
            target: sel,
            getItems: _getCompositeSourceItems,
            placeholder: t('palette.search'),
        }));
    });

    // Attach EntitySelect to each layer's brightness dropdown
    list.querySelectorAll<HTMLSelectElement>('.composite-layer-brightness').forEach(sel => {
        _compositeBrightnessEntitySelects.push(new EntitySelect({
            target: sel,
            getItems: _getCompositeBrightnessItems,
            placeholder: t('palette.search'),
            allowNone: true,
            noneLabel: t('color_strip.composite.brightness.none'),
        }));
    });

    // Attach EntitySelect to each layer's CSPT dropdown
    list.querySelectorAll<HTMLSelectElement>('.composite-layer-cspt').forEach(sel => {
        _compositeCSPTEntitySelects.push(new EntitySelect({
            target: sel,
            getItems: _getCompositeCSPTItems,
            placeholder: t('palette.search'),
            allowNone: true,
            noneLabel: t('common.none_no_cspt'),
        }));
    });

    _initCompositeLayerDrag(list);
}

export function compositeAddLayer() {
    _compositeLayersSyncFromDom();
    _compositeLayers.push({
        source_id: _compositeAvailableSources.length > 0 ? _compositeAvailableSources[0].id : '',
        blend_mode: 'normal',
        opacity: 1.0,
        enabled: true,
        brightness_source_id: null,
        processing_template_id: null,
        start: 0,
        end: 0,
        reverse: false,
    });
    compositeRenderList();
}

export function compositeRemoveLayer(i: number) {
    _compositeLayersSyncFromDom();
    if (_compositeLayers.length <= 1) return;
    _compositeLayers.splice(i, 1);
    compositeRenderList();
}

function _compositeLayersSyncFromDom() {
    const list = document.getElementById('composite-layers-list') as HTMLElement | null;
    if (!list) return;
    const srcs = list.querySelectorAll<HTMLSelectElement>('.composite-layer-source');
    const blends = list.querySelectorAll<HTMLSelectElement>('.composite-layer-blend');
    const opacities = list.querySelectorAll<HTMLInputElement>('.composite-layer-opacity');
    const enableds = list.querySelectorAll<HTMLInputElement>('.composite-layer-enabled');
    const briSrcs = list.querySelectorAll<HTMLSelectElement>('.composite-layer-brightness');
    const csptSels = list.querySelectorAll<HTMLSelectElement>('.composite-layer-cspt');
    const starts = list.querySelectorAll<HTMLInputElement>('.composite-layer-start');
    const ends = list.querySelectorAll<HTMLInputElement>('.composite-layer-end');
    const reverses = list.querySelectorAll<HTMLInputElement>('.composite-layer-reverse');
    if (srcs.length === _compositeLayers.length) {
        for (let i = 0; i < srcs.length; i++) {
            _compositeLayers[i].source_id = srcs[i].value;
            _compositeLayers[i].blend_mode = blends[i].value;
            _compositeLayers[i].opacity = parseFloat(opacities[i].value);
            _compositeLayers[i].enabled = enableds[i].checked;
            _compositeLayers[i].brightness_source_id = briSrcs[i] ? (briSrcs[i].value || null) : null;
            _compositeLayers[i].processing_template_id = csptSels[i] ? (csptSels[i].value || null) : null;
            _compositeLayers[i].start = starts[i] ? (parseInt(starts[i].value) || 0) : 0;
            _compositeLayers[i].end = ends[i] ? (parseInt(ends[i].value) || 0) : 0;
            _compositeLayers[i].reverse = reverses[i] ? reverses[i].checked : false;
        }
    }
}

/* ── Composite layer drag-to-reorder ── */

const _COMPOSITE_DRAG_THRESHOLD = 5;
let _compositeLayerDragState: any = null;

function _initCompositeLayerDrag(list: any) {
    // Guard against stacking listeners across re-renders (the list DOM node persists).
    if (list._compositeDragBound) return;
    list._compositeDragBound = true;

    list.addEventListener('pointerdown', (e: any) => {
        const handle = e.target.closest('.composite-layer-drag-handle');
        if (!handle) return;
        const item = handle.closest('.composite-layer-item');
        if (!item) return;
        e.preventDefault();
        e.stopPropagation();

        const fromIndex = parseInt(item.dataset.layerIndex, 10);
        _compositeLayerDragState = {
            item,
            list,
            startY: e.clientY,
            started: false,
            clone: null,
            placeholder: null,
            offsetY: 0,
            fromIndex,
            scrollRaf: null,
        };

        const onMove = (ev: any) => _onCompositeLayerDragMove(ev);
        const cleanup = () => {
            document.removeEventListener('pointermove', onMove);
            document.removeEventListener('pointerup', cleanup);
            document.removeEventListener('pointercancel', cleanup);
            _onCompositeLayerDragEnd();
        };
        document.addEventListener('pointermove', onMove);
        document.addEventListener('pointerup', cleanup);
        document.addEventListener('pointercancel', cleanup);
    }, { capture: false });
}

function _onCompositeLayerDragMove(e: any) {
    const ds = _compositeLayerDragState;
    if (!ds) return;

    if (!ds.started) {
        if (Math.abs(e.clientY - ds.startY) < _COMPOSITE_DRAG_THRESHOLD) return;
        _startCompositeLayerDrag(ds, e);
    }

    ds.clone.style.top = (e.clientY - ds.offsetY) + 'px';

    const items = ds.list.querySelectorAll('.composite-layer-item');
    for (const it of items) {
        if (it.style.display === 'none') continue;
        const r = it.getBoundingClientRect();
        if (e.clientY >= r.top && e.clientY <= r.bottom) {
            const before = e.clientY < r.top + r.height / 2;
            if (it === ds.lastTarget && before === ds.lastBefore) break;
            ds.lastTarget = it;
            ds.lastBefore = before;
            if (before) {
                ds.list.insertBefore(ds.placeholder, it);
            } else {
                ds.list.insertBefore(ds.placeholder, it.nextSibling);
            }
            break;
        }
    }

    // Auto-scroll near modal edges
    if (ds.scrollRaf) cancelAnimationFrame(ds.scrollRaf);
    const modal = ds.list.closest('.modal-body');
    if (modal) {
        const EDGE = 60, SPEED = 12;
        const mr = modal.getBoundingClientRect();
        let speed = 0;
        if (e.clientY < mr.top + EDGE) speed = -SPEED;
        else if (e.clientY > mr.bottom - EDGE) speed = SPEED;
        if (speed !== 0) {
            const scroll = () => { modal.scrollTop += speed; ds.scrollRaf = requestAnimationFrame(scroll); };
            ds.scrollRaf = requestAnimationFrame(scroll);
        }
    }
}

function _startCompositeLayerDrag(ds: any, e: any) {
    ds.started = true;
    const rect = ds.item.getBoundingClientRect();

    const clone = ds.item.cloneNode(true);
    clone.className = ds.item.className + ' composite-layer-drag-clone';
    clone.style.width = rect.width + 'px';
    clone.style.left = rect.left + 'px';
    clone.style.top = rect.top + 'px';
    document.body.appendChild(clone);
    ds.clone = clone;
    ds.offsetY = e.clientY - rect.top;

    const placeholder = document.createElement('div');
    placeholder.className = 'composite-layer-drag-placeholder';
    placeholder.style.height = rect.height + 'px';
    ds.item.parentNode.insertBefore(placeholder, ds.item);
    ds.placeholder = placeholder;

    ds.item.style.display = 'none';
    document.body.classList.add('composite-layer-dragging');
}

function _onCompositeLayerDragEnd() {
    const ds = _compositeLayerDragState;
    _compositeLayerDragState = null;
    if (!ds || !ds.started) return;

    if (ds.scrollRaf) cancelAnimationFrame(ds.scrollRaf);

    // Determine new index from placeholder position
    let toIndex = 0;
    for (const child of ds.list.children) {
        if (child === ds.placeholder) break;
        if (child.classList.contains('composite-layer-item') && child.style.display !== 'none') {
            toIndex++;
        }
    }

    // Cleanup DOM
    ds.item.style.display = '';
    ds.placeholder.remove();
    ds.clone.remove();
    document.body.classList.remove('composite-layer-dragging');

    // Sync current DOM values before reordering
    _compositeLayersSyncFromDom();

    // Reorder array and re-render
    if (toIndex !== ds.fromIndex) {
        const [moved] = _compositeLayers.splice(ds.fromIndex, 1);
        _compositeLayers.splice(toIndex, 0, moved);
        compositeRenderList();
    }
}

export function compositeGetLayers() {
    _compositeLayersSyncFromDom();
    return _compositeLayers.map(l => {
        const layer: any = {
            source_id: l.source_id,
            blend_mode: l.blend_mode,
            opacity: l.opacity,
            enabled: l.enabled,
        };
        if (l.brightness_source_id) layer.brightness_source_id = l.brightness_source_id;
        if (l.processing_template_id) layer.processing_template_id = l.processing_template_id;
        if (l.start) layer.start = l.start;
        if (l.end) layer.end = l.end;
        if (l.reverse) layer.reverse = l.reverse;
        return layer;
    });
}

export function loadCompositeState(css: any) {
    const raw = css && css.layers;
    _compositeLayers = (raw && raw.length > 0)
        ? raw.map((l: any) => ({
            source_id: l.source_id || '',
            blend_mode: l.blend_mode || 'normal',
            opacity: l.opacity != null ? l.opacity : 1.0,
            enabled: l.enabled != null ? l.enabled : true,
            brightness_source_id: l.brightness_source_id || null,
            processing_template_id: l.processing_template_id || null,
            start: l.start || 0,
            end: l.end || 0,
            reverse: l.reverse || false,
        }))
        : [{ source_id: '', blend_mode: 'normal', opacity: 1.0, enabled: true, brightness_source_id: null, processing_template_id: null, start: 0, end: 0, reverse: false }];
    compositeRenderList();
}
