// DMX tab — protocol dropdown + optional Art-Net nodes list (Alpine
// conversion KL-137). The nodes list is deliberately sparse: most DJ
// rigs have one Art-Net box and leave this empty (broadcast). Unicast
// only matters for venues with split networks per universe.

function storeDefinition() {
    return {
        // --- Server snapshot ---
        enabled: true,
        enabledStatusText: 'Loading…',
        protocol: 'artnet',
        nodes: [],

        // --- Add/Edit modal ---
        modalShown: false,
        editingIndex: null,  // null = Add, number = Edit
        form: {ip: '', universe: 0},
        formError: '',
        get modalTitle() {
            return this.editingIndex === null ? 'Add Art-Net node' : 'Edit Art-Net node';
        },

        // --- Computed visibility ---
        get showEmpty() { return this.nodes.length === 0; },

        async refresh() {
            try {
                const res = await fetch('/api/dmx');
                const data = await res.json();
                this.enabled = data.enabled !== false;
                // KL-138: senders run live so the steady-state copy is just
                // "Active." / "Disabled." — the legacy "on restart" wording
                // only surfaces when a hot-reload couldn't take.
                this.enabledStatusText = this.enabled ? 'Active' : 'Disabled';
                this.protocol = data.protocol || 'artnet';
                this.nodes = data.nodes || [];
            } catch (e) {
                this.nodes = [];
                this.enabledStatusText = 'Failed to load';
            }
        },

        async setEnabled(enabled) {
            try {
                const r = await fetch('/api/dmx/enabled', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({enabled}),
                });
                const data = await r.json().catch(() => ({}));
                // KL-138: server hot-reloads on toggle. Falls back to
                // "after restart" wording only when the live reload
                // couldn't take (e.g. socket bind failed).
                if (data.live ?? true) {
                    this.enabledStatusText = enabled ? 'Active' : 'Disabled';
                } else {
                    this.enabledStatusText = enabled
                        ? 'Will be active after restart'
                        : 'Will be disabled after restart';
                }
            } catch (_) { /* noop */ }
        },

        async saveProtocol() {
            try {
                await fetch('/api/dmx/protocol', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({protocol: this.protocol}),
                });
            } catch (_) { /* noop */ }
        },

        openAdd() {
            this.editingIndex = null;
            this.form = {ip: '', universe: 0};
            this.formError = '';
            this.modalShown = true;
            setTimeout(() => document.getElementById('dmx-node-ip')?.focus(), 0);
        },

        openEdit(index) {
            const node = this.nodes[index];
            if (!node) return;
            this.editingIndex = index;
            this.form = {
                ip: node.ip || '',
                universe: node.universe ?? 0,
            };
            this.formError = '';
            this.modalShown = true;
            setTimeout(() => document.getElementById('dmx-node-ip')?.focus(), 0);
        },

        closeModal() {
            this.modalShown = false;
            this.editingIndex = null;
        },

        async saveNode() {
            const ip = (this.form.ip || '').trim();
            const universe = parseInt(this.form.universe, 10);
            if (!ip) {
                this.formError = 'IP address is required.';
                return;
            }
            if (!Number.isFinite(universe) || universe < 0 || universe > 32767) {
                this.formError = 'Universe must be 0–32767.';
                return;
            }
            const url = this.editingIndex === null
                ? '/api/dmx/nodes'
                : `/api/dmx/nodes/${this.editingIndex}`;
            const method = this.editingIndex === null ? 'POST' : 'PUT';
            try {
                const res = await fetch(url, {
                    method,
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ip, universe}),
                });
                if (!res.ok) {
                    const data = await res.json().catch(() => ({}));
                    this.formError = data.error || 'Save failed.';
                    return;
                }
                this.closeModal();
                await this.refresh();
            } catch (e) {
                this.formError = 'Network error.';
            }
        },

        async deleteNode(index) {
            if (!confirm('Remove this Art-Net node?')) return;
            try {
                await fetch(`/api/dmx/nodes/${index}`, {method: 'DELETE'});
                await this.refresh();
            } catch (_) { /* noop */ }
        },
    };
}

function ensureStore() {
    if (!window.Alpine) return null;
    if (!window.Alpine.store('dmx')) {
        window.Alpine.store('dmx', storeDefinition());
    }
    return window.Alpine.store('dmx');
}

if (window.Alpine) {
    ensureStore();
} else {
    document.addEventListener('alpine:init', ensureStore);
}

export function initDmx() {
    let store = ensureStore();
    if (!store) {
        document.addEventListener('alpine:init', () => {
            ensureStore()?.refresh();
        }, {once: true});
        return;
    }
    store.refresh();
}
