/**
 * BindableScalarWidget — a slider that can optionally bind to a value source.
 *
 * Renders a slider (range input) with a small toggle button. When toggled to
 * "bound" mode, shows an EntitySelect value source picker instead of the slider.
 * Emits a BindableFloat value: plain number (static) or {value, source_id} (bound).
 *
 * Usage:
 *   const widget = new BindableScalarWidget({
 *       container: document.getElementById('my-container'),
 *       label: 'Smoothing',
 *       min: 0, max: 1, step: 0.05, default: 0.3,
 *       valueSources: () => cachedValueSources,
 *       onChange: (bf) => { … },
 *   });
 *   widget.setValue({ value: 0.3, source_id: 'vs_abc' });
 *   const bf = widget.getValue(); // BindableFloat
 */

import type { BindableFloat } from '../types.ts';
import { bindableValue, bindableSourceId } from '../types.ts';
import { EntitySelect } from './entity-palette.ts';
import { getValueSourceIcon } from './icons.ts';
import { t } from './i18n.ts';

export interface BindableScalarOpts {
    container: HTMLElement;
    label?: string;
    min: number;
    max: number;
    step: number;
    default: number;
    /** Format the display value (default: 2 decimal places) */
    format?: (v: number) => string;
    valueSources: () => Array<{ id: string; name: string; source_type: string }>;
    onChange?: (value: BindableFloat) => void;
    /** HTML id prefix for generated elements */
    idPrefix?: string;
    /** Label for the "no binding" option (default: generic "None (static value)") */
    noneLabel?: string;
}

let _widgetCounter = 0;

export class BindableScalarWidget {
    private _container: HTMLElement;
    private _opts: BindableScalarOpts;
    private _id: string;
    private _bound: boolean = false;
    private _staticValue: number;
    private _sourceId: string = '';
    private _entitySelect: EntitySelect | null = null;

    // DOM elements
    private _sliderRow!: HTMLElement;
    private _vsRow!: HTMLElement;
    private _slider!: HTMLInputElement;
    private _display!: HTMLElement;
    private _toggleBtn!: HTMLButtonElement;
    private _select!: HTMLSelectElement;

    constructor(opts: BindableScalarOpts) {
        this._opts = opts;
        this._container = opts.container;
        this._staticValue = opts.default;
        this._id = opts.idPrefix || `bsw-${++_widgetCounter}`;
        this._render();
    }

    private _format(v: number): string {
        return this._opts.format ? this._opts.format(v) : v.toFixed(2);
    }

    private _render(): void {
        const { min, max, step } = this._opts;
        const id = this._id;

        // Toggle button (link icon for binding)
        const toggleHtml = `<button type="button" class="bindable-toggle" id="${id}-toggle" title="${t('bindable.toggle')}" aria-label="Toggle value source binding">
            <svg class="icon icon-xs" viewBox="0 0 24 24"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></svg>
        </button>`;

        // Slider row (static mode)
        const sliderHtml = `<div class="bindable-slider-row" id="${id}-slider-row">
            <input type="range" id="${id}-slider" min="${min}" max="${max}" step="${step}" value="${this._staticValue}">
            <span class="bindable-value" id="${id}-display">${this._format(this._staticValue)}</span>
            ${toggleHtml}
        </div>`;

        // VS picker row (bound mode)
        const vsHtml = `<div class="bindable-vs-row" id="${id}-vs-row" style="display:none">
            <select id="${id}-select"></select>
            <button type="button" class="bindable-toggle bindable-toggle--active" id="${id}-untoggle" title="${t('bindable.toggle')}" aria-label="Switch to static value">
                <svg class="icon icon-xs" viewBox="0 0 24 24"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></svg>
            </button>
        </div>`;

        this._container.innerHTML = sliderHtml + vsHtml;

        // Cache DOM refs
        this._sliderRow = document.getElementById(`${id}-slider-row`)!;
        this._vsRow = document.getElementById(`${id}-vs-row`)!;
        this._slider = document.getElementById(`${id}-slider`) as HTMLInputElement;
        this._display = document.getElementById(`${id}-display`)!;
        this._select = document.getElementById(`${id}-select`) as HTMLSelectElement;
        this._toggleBtn = document.getElementById(`${id}-toggle`) as HTMLButtonElement;

        // Slider input handler
        this._slider.addEventListener('input', () => {
            this._staticValue = parseFloat(this._slider.value);
            this._display.textContent = this._format(this._staticValue);
            this._fireChange();
        });

        // Toggle to bound mode
        this._toggleBtn.addEventListener('click', () => this._setMode(true));
        document.getElementById(`${id}-untoggle`)!.addEventListener('click', () => this._setMode(false));
    }

    private _setMode(bound: boolean): void {
        this._bound = bound;
        this._sliderRow.style.display = bound ? 'none' : '';
        this._vsRow.style.display = bound ? '' : 'none';

        if (bound) {
            this._populateVsSelect();
        } else {
            this._sourceId = '';
            if (this._entitySelect) {
                this._entitySelect.destroy();
                this._entitySelect = null;
            }
        }
        this._fireChange();
    }

    private _populateVsSelect(): void {
        const sources = this._opts.valueSources();
        const id = this._id;

        this._select.innerHTML = `<option value="">${this._opts.noneLabel || t('bindable.none')}</option>` +
            sources.map(vs =>
                `<option value="${vs.id}"${vs.id === this._sourceId ? ' selected' : ''}>${vs.name}</option>`
            ).join('');

        // Wrap with EntitySelect
        if (this._entitySelect) this._entitySelect.destroy();
        this._entitySelect = new EntitySelect({
            target: this._select,
            getItems: () => sources.map(vs => ({
                value: vs.id,
                label: vs.name,
                icon: getValueSourceIcon(vs.source_type),
                desc: vs.source_type,
            })),
            placeholder: t('palette.search'),
            allowNone: true,
            noneLabel: this._opts.noneLabel || t('bindable.none'),
            onChange: (value: string) => {
                this._sourceId = value;
                this._fireChange();
            },
        });
    }

    private _fireChange(): void {
        if (this._opts.onChange) {
            this._opts.onChange(this.getValue());
        }
    }

    // ── Public API ──

    getValue(): BindableFloat {
        if (this._bound && this._sourceId) {
            return { value: this._staticValue, source_id: this._sourceId };
        }
        return this._staticValue;
    }

    setValue(bf: BindableFloat | undefined): void {
        this._staticValue = bindableValue(bf, this._opts.default);
        this._sourceId = bindableSourceId(bf);
        this._bound = !!this._sourceId;

        this._slider.value = String(this._staticValue);
        this._display.textContent = this._format(this._staticValue);

        this._sliderRow.style.display = this._bound ? 'none' : '';
        this._vsRow.style.display = this._bound ? '' : 'none';

        if (this._bound) {
            this._populateVsSelect();
        }
    }

    /** Refresh the VS dropdown after cache updates. */
    refresh(): void {
        if (this._bound) {
            this._populateVsSelect();
        }
    }

    destroy(): void {
        if (this._entitySelect) {
            this._entitySelect.destroy();
            this._entitySelect = null;
        }
        this._container.innerHTML = '';
    }
}
