// KL-69 / KL-116: Lumi personality picker — lives inside the director panel.
//
// Compact chips at the bottom of the director panel. Hovering a chip
// reveals a small fingerprint popover (4 mini-bars: Strobe / Movement /
// Color shifts / Restraint) positioned just above the hovered chip —
// preview without committing. Click a chip to commit; live-applies via
// /api/personalities/activate.
//
// KL-116: chip rendering is Alpine.js-driven. Reactive state lives in
// Alpine.store('personalities'); the chip <template x-for> binds to it.
// The fingerprint popover stays imperative because positioning needs
// getBoundingClientRect math that Alpine doesn't simplify.

// KL-167: 8 dimensions matching the AI Director's actual knob set.
// First four are the original behavioral tendencies; last four were
// added to expose the optical params (zoom_pulse_amount, iris_response,
// frost_amount, cct_warm_cool_bias) that landed in KL-136. Now the
// picker honestly describes Lumi's full surface area.
const FINGERPRINT_DIMENSIONS = [
    {key: 'strobe', label: 'Strobe'},
    {key: 'movement', label: 'Movement'},
    {key: 'color_shifts', label: 'Color'},
    {key: 'restraint', label: 'Restraint'},
    {key: 'zoom', label: 'Zoom'},
    {key: 'iris', label: 'Iris'},
    {key: 'frost', label: 'Frost'},
    {key: 'warmth', label: 'Warmth'},
];

function escapeHtml(s) {
    return String(s).replace(/[&<>"']/g, c => ({
        '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
    }[c]));
}

function storeDefinition() {
    return {
        byId: {},
        items: [],
        activeId: null,
        gated: false,
        initialized: false,
        isLocked(id) {
            return this.gated && id !== 'default';
        },
        async setActive(id) {
            if (id === this.activeId) return;
            if (this.isLocked(id)) return;
            const prev = this.activeId;
            this.activeId = id;
            try {
                const r = await fetch('/api/personalities/activate', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({id}),
                });
                if (!r.ok) this.activeId = prev;
            } catch (e) {
                this.activeId = prev;
            }
        },
        syncFromState(activeId) {
            if (!activeId || activeId === this.activeId) return;
            if (!this.byId[activeId]) return;
            this.activeId = activeId;
        },
    };
}

// Register the store. Alpine may load before or after this module;
// handle both cases so chip bindings see the store regardless of order.
function ensureStore() {
    if (!window.Alpine) return null;
    if (!window.Alpine.store('personalities')) {
        window.Alpine.store('personalities', storeDefinition());
    }
    return window.Alpine.store('personalities');
}

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

// Imperative fingerprint popover. Alpine doesn't help with the
// getBoundingClientRect anchor math.
function showFingerprintPopover(pid, anchorEl) {
    const popover = document.getElementById('personality-fingerprint-popover');
    if (!popover) return;
    const store = window.Alpine?.store('personalities');
    const p = store?.byId[pid];
    if (!p) {
        popover.hidden = true;
        return;
    }
    const fp = p.fingerprint || {};
    const bars = FINGERPRINT_DIMENSIONS.map(({key, label}) => {
        const v = Math.max(0, Math.min(1, Number(fp[key] || 0)));
        const pct = Math.round(v * 100);
        return `
            <div class="fp-row">
                <span class="fp-label">${escapeHtml(label)}</span>
                <span class="fp-bar"><span class="fp-bar-fill" style="width: ${pct}%; background: ${escapeHtml(p.accent)}"></span></span>
            </div>
        `;
    }).join('');
    popover.innerHTML = `<div class="fp-bars">${bars}</div>`;
    popover.style.borderColor = p.accent;
    popover.hidden = false;

    const picker = document.getElementById('personality-picker');
    if (!picker || !anchorEl) return;
    const pickerRect = picker.getBoundingClientRect();
    const anchorRect = anchorEl.getBoundingClientRect();
    const popRect = popover.getBoundingClientRect();
    const left = (anchorRect.left - pickerRect.left)
                 + (anchorRect.width / 2)
                 - (popRect.width / 2);
    const top = (anchorRect.top - pickerRect.top) - popRect.height - 8;
    popover.style.left = `${Math.max(8, left)}px`;
    popover.style.top = `${top}px`;
}

function hideFingerprintPopover() {
    const popover = document.getElementById('personality-fingerprint-popover');
    if (popover) popover.hidden = true;
}

// Listen for popover events that Alpine x-on dispatches from chip
// buttons. CustomEvents bubble up to the picker section.
function attachPopoverListeners(picker) {
    if (picker._popoverListenersAttached) return;
    picker._popoverListenersAttached = true;
    picker.addEventListener('show-fingerprint', e => {
        showFingerprintPopover(e.detail.id, e.detail.anchor);
    });
    picker.addEventListener('hide-fingerprint', () => hideFingerprintPopover());
}

// KL-69: WKWebView caches HTML aggressively despite cache headers +
// private_mode. If the cached page lacks the picker (or carries the
// pre-Alpine markup that's missing the x-for template), rebuild it as
// Alpine markup. Fresh JS bypasses every HTML caching layer.
function ensurePickerInDom() {
    let picker = document.getElementById('personality-picker');
    if (picker) {
        picker.removeAttribute('hidden');
        if (!picker.querySelector('template[x-for]')) {
            picker.setAttribute('x-data', '');
            picker.innerHTML = pickerInnerHtml();
        }
        attachPopoverListeners(picker);
        return picker;
    }
    picker = document.createElement('section');
    picker.id = 'personality-picker';
    picker.className = 'personality-picker';
    picker.setAttribute('x-data', '');
    picker.innerHTML = pickerInnerHtml();
    attachPopoverListeners(picker);
    const console_ = document.querySelector('.lumi-console');
    if (console_ && console_.parentNode) {
        console_.parentNode.insertBefore(picker, console_);
    } else {
        const showTab = document.querySelector('[data-tab-panel="show"]');
        (showTab || document.body).appendChild(picker);
    }
    return picker;
}

function pickerInnerHtml() {
    return `
        <div class="personality-picker-label">SELECT LUMI'S VIBE</div>
        <div class="personality-chips" id="personality-chips" role="radiogroup" aria-label="Lumi personality">
            <template x-for="p in $store.personalities.items" :key="p.id">
                <button type="button"
                        role="radio"
                        class="personality-chip"
                        :class="{ 'active': p.id === $store.personalities.activeId, 'locked': $store.personalities.isLocked(p.id) }"
                        :aria-checked="p.id === $store.personalities.activeId"
                        :aria-label="p.name + ': ' + p.voice"
                        :title="p.voice"
                        :style="'--chip-accent: ' + p.accent"
                        x-on:click="$store.personalities.setActive(p.id)"
                        x-on:mouseenter="$dispatch('show-fingerprint', { id: p.id, anchor: $el })"
                        x-on:focus="$dispatch('show-fingerprint', { id: p.id, anchor: $el })"
                        x-on:mouseleave="$dispatch('hide-fingerprint')"
                        x-on:blur="$dispatch('hide-fingerprint')">
                    <span class="personality-chip-glyph" x-text="p.glyph"></span>
                    <span class="personality-chip-name" x-text="p.name"></span>
                </button>
            </template>
        </div>
        <div class="personality-fingerprint-popover" id="personality-fingerprint-popover" hidden></div>
    `;
}

// Debug helper: paste `__debugPicker()` in devtools.
window.__debugPicker = async () => {
    console.log('[picker] script loaded');
    const existing = document.getElementById('personality-picker');
    console.log('[picker] section in DOM:', existing);
    console.log('[picker] Alpine store:', window.Alpine?.store('personalities'));
    try {
        const r = await fetch('/api/personalities');
        console.log('[picker] fetch status:', r.status);
        const data = await r.json();
        console.log('[picker] api data:', data);
    } catch (e) {
        console.error('[picker] fetch failed:', e);
    }
    return 'see [picker] log lines above';
};
console.log('[picker] module loaded at', new Date().toISOString());

export async function initPersonalityPicker() {
    console.log('[picker] initPersonalityPicker called');
    let store = ensureStore();
    if (!store) {
        // Alpine hasn't fired alpine:init yet — wait for it.
        await new Promise(resolve => {
            document.addEventListener('alpine:init', resolve, {once: true});
        });
        store = ensureStore();
    }
    if (!store) {
        console.error('[picker] Alpine never initialized; picker disabled');
        return;
    }
    if (store.initialized) return;
    store.initialized = true;
    ensurePickerInDom();
    try {
        const r = await fetch('/api/personalities');
        if (!r.ok) {
            renderFallback('Lumi vibes API failed');
            return;
        }
        const data = await r.json();
        const items = data.personalities || [];
        if (items.length === 0) {
            renderFallback('No vibes loaded');
            return;
        }
        store.byId = Object.fromEntries(items.map(p => [p.id, p]));
        store.items = items;
        store.activeId = data.active || items[0].id;
        store.gated = !!data.gated;
    } catch (e) {
        renderFallback(`Lumi vibes error: ${e.message || e}`);
    }
}

function renderFallback(msg) {
    const chipsEl = document.getElementById('personality-chips');
    if (chipsEl) {
        chipsEl.innerHTML = `<span style="color:#888;font-size:0.75rem;padding:0.4rem 0.85rem">${msg}</span>`;
    }
}

export function syncPersonalityFromState(activeFromState) {
    window.Alpine?.store('personalities')?.syncFromState(activeFromState);
}
