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

import { fetchWithAuth, escapeHtml } from '../core/api.ts';
import { t } from '../core/i18n.ts';
import { showToast } from '../core/ui.ts';
import {
    ICON_SEARCH, ICON_CLONE, getAssetTypeIcon,
} 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';
import { attachNotificationAppPicker, NotificationAppPalette } from '../core/process-picker.ts';
import { _cachedAssets, assetsCache } from '../core/state.ts';
import { getBaseOrigin } from './settings.ts';

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

/* ── Notification state ───────────────────────────────────────── */

interface AppOverride {
    app: string;
    color: string;
    sound_asset_id: string | null;
    volume: number; // 0–100
}

let _notificationAppOverrides: AppOverride[] = [];

/** Return current overrides array (for dirty-check snapshot). */
export function notificationGetRawAppOverrides() {
    return _notificationAppOverrides;
}

let _notificationEffectIconSelect: any = null;
let _notificationFilterModeIconSelect: any = null;

export function ensureNotificationEffectIconSelect() {
    const sel = document.getElementById('css-editor-notification-effect') as HTMLSelectElement | null;
    if (!sel) return;
    const items = [
        { value: 'flash',          icon: _icon(P.zap),         label: t('color_strip.notification.effect.flash'),          desc: t('color_strip.notification.effect.flash.desc') },
        { value: 'pulse',          icon: _icon(P.activity),     label: t('color_strip.notification.effect.pulse'),          desc: t('color_strip.notification.effect.pulse.desc') },
        { value: 'sweep',          icon: _icon(P.fastForward),  label: t('color_strip.notification.effect.sweep'),          desc: t('color_strip.notification.effect.sweep.desc') },
        { value: 'chase',          icon: _icon(P.rocket),       label: t('color_strip.notification.effect.chase'),          desc: t('color_strip.notification.effect.chase.desc') },
        { value: 'gradient_flash', icon: _icon(P.rainbow),      label: t('color_strip.notification.effect.gradient_flash'), desc: t('color_strip.notification.effect.gradient_flash.desc') },
    ];
    if (_notificationEffectIconSelect) { _notificationEffectIconSelect.updateItems(items); return; }
    _notificationEffectIconSelect = new IconSelect({ target: sel, items, columns: 2 });
}

export function ensureNotificationFilterModeIconSelect() {
    const sel = document.getElementById('css-editor-notification-filter-mode') as HTMLSelectElement | null;
    if (!sel) return;
    const items = [
        { value: 'off',       icon: _icon(P.globe),    label: t('color_strip.notification.filter_mode.off'),       desc: t('color_strip.notification.filter_mode.off.desc') },
        { value: 'whitelist', icon: _icon(P.circleCheck), label: t('color_strip.notification.filter_mode.whitelist'), desc: t('color_strip.notification.filter_mode.whitelist.desc') },
        { value: 'blacklist', icon: _icon(P.eyeOff),   label: t('color_strip.notification.filter_mode.blacklist'), desc: t('color_strip.notification.filter_mode.blacklist.desc') },
    ];
    if (_notificationFilterModeIconSelect) { _notificationFilterModeIconSelect.updateItems(items); return; }
    _notificationFilterModeIconSelect = new IconSelect({ target: sel, items, columns: 3 });
}

export function onNotificationFilterModeChange() {
    const mode = (document.getElementById('css-editor-notification-filter-mode') as HTMLInputElement).value;
    (document.getElementById('css-editor-notification-filter-list-group') as HTMLElement).style.display = mode === 'off' ? 'none' : '';
}

/* ── Unified per-app overrides (color + sound) ────────────────── */

let _overrideEntitySelects: EntitySelect[] = [];

function _getSoundAssetItems() {
    return _cachedAssets
        .filter(a => a.asset_type === 'sound')
        .map(a => ({ value: a.id, label: a.name, icon: getAssetTypeIcon('sound'), desc: a.filename }));
}

function _overridesSyncFromDom() {
    const list = document.getElementById('notification-app-overrides-list') as HTMLElement | null;
    if (!list) return;
    const names = list.querySelectorAll<HTMLInputElement>('.notif-override-name');
    const colors = list.querySelectorAll<HTMLInputElement>('.notif-override-color');
    const sounds = list.querySelectorAll<HTMLSelectElement>('.notif-override-sound');
    const volumes = list.querySelectorAll<HTMLInputElement>('.notif-override-volume');
    if (names.length === _notificationAppOverrides.length) {
        for (let i = 0; i < names.length; i++) {
            _notificationAppOverrides[i].app = names[i].value;
            _notificationAppOverrides[i].color = colors[i].value;
            _notificationAppOverrides[i].sound_asset_id = sounds[i].value || null;
            _notificationAppOverrides[i].volume = parseInt(volumes[i].value);
        }
    }
}

function _overridesRenderList() {
    const list = document.getElementById('notification-app-overrides-list') as HTMLElement | null;
    if (!list) return;

    _overrideEntitySelects.forEach(es => es.destroy());
    _overrideEntitySelects = [];

    const soundAssets = _cachedAssets.filter(a => a.asset_type === 'sound');

    list.innerHTML = _notificationAppOverrides.map((entry, i) => {
        const soundOpts = `<option value="">${t('color_strip.notification.sound.none')}</option>` +
            soundAssets.map(a =>
                `<option value="${a.id}"${a.id === entry.sound_asset_id ? ' selected' : ''}>${escapeHtml(a.name)}</option>`
            ).join('');
        const volPct = entry.volume ?? 100;
        return `
        <div class="notif-override-row">
            <input type="text" class="notif-override-name" data-idx="${i}" value="${escapeHtml(entry.app)}"
                   placeholder="${t('color_strip.notification.app_overrides.app_placeholder') || 'App name'}">
            <button type="button" class="btn btn-icon btn-secondary notif-override-browse" data-idx="${i}"
                    title="${t('automations.condition.application.browse')}">${ICON_SEARCH}</button>
            <input type="color" class="notif-override-color" data-idx="${i}" value="${entry.color}">
            <button type="button" class="btn btn-icon btn-secondary"
                    onclick="notificationRemoveAppOverride(${i})">&#x2715;</button>
            <select class="notif-override-sound" data-idx="${i}">${soundOpts}</select>
            <input type="range" class="notif-override-volume" data-idx="${i}" min="0" max="100" step="5" value="${volPct}"
                   title="${volPct}%"
                   oninput="this.title = this.value + '%'">
        </div>`;
    }).join('');

    // Wire browse buttons
    list.querySelectorAll<HTMLButtonElement>('.notif-override-browse').forEach(btn => {
        btn.addEventListener('click', async () => {
            const idx = parseInt(btn.dataset.idx!);
            const nameInput = list.querySelector<HTMLInputElement>(`.notif-override-name[data-idx="${idx}"]`);
            if (!nameInput) return;
            const picked = await NotificationAppPalette.pick({
                current: nameInput.value,
                placeholder: t('color_strip.notification.search_apps') || 'Search notification apps…',
            });
            if (picked !== undefined) {
                nameInput.value = picked;
                _overridesSyncFromDom();
            }
        });
    });

    // Wire EntitySelects for sound dropdowns
    list.querySelectorAll<HTMLSelectElement>('.notif-override-sound').forEach(sel => {
        const items = _getSoundAssetItems();
        if (items.length > 0) {
            const es = new EntitySelect({
                target: sel,
                getItems: () => _getSoundAssetItems(),
                placeholder: t('color_strip.notification.sound.search') || 'Search sounds…',
            });
            _overrideEntitySelects.push(es);
        }
    });
}

export function notificationAddAppOverride() {
    _overridesSyncFromDom();
    _notificationAppOverrides.push({ app: '', color: '#ffffff', sound_asset_id: null, volume: 100 });
    _overridesRenderList();
}

export function notificationRemoveAppOverride(i: number) {
    _overridesSyncFromDom();
    _notificationAppOverrides.splice(i, 1);
    _overridesRenderList();
}

/** Split overrides into app_colors dict for the API. */
export function notificationGetAppColorsDict() {
    _overridesSyncFromDom();
    const dict: Record<string, string> = {};
    for (const entry of _notificationAppOverrides) {
        if (entry.app.trim()) dict[entry.app.trim()] = entry.color;
    }
    return dict;
}

/** Split overrides into app_sounds dict for the API. */
export function notificationGetAppSoundsDict() {
    _overridesSyncFromDom();
    const dict: Record<string, any> = {};
    for (const entry of _notificationAppOverrides) {
        if (!entry.app.trim()) continue;
        if (entry.sound_asset_id || entry.volume !== 100) {
            dict[entry.app.trim()] = {
                sound_asset_id: entry.sound_asset_id || null,
                volume: (entry.volume ?? 100) / 100,
            };
        }
    }
    return dict;
}

/* ── Notification sound — global EntitySelect ─────────────────── */

let _notifSoundEntitySelect: EntitySelect | null = null;

function _populateSoundOptions(sel: HTMLSelectElement, selectedId?: string | null) {
    const sounds = _cachedAssets.filter(a => a.asset_type === 'sound');
    sel.innerHTML = `<option value="">${t('color_strip.notification.sound.none')}</option>` +
        sounds.map(a =>
            `<option value="${a.id}"${a.id === selectedId ? ' selected' : ''}>${escapeHtml(a.name)}</option>`
        ).join('');
}

export function ensureNotifSoundEntitySelect() {
    const sel = document.getElementById('css-editor-notification-sound') as HTMLSelectElement | null;
    if (!sel) return;
    _populateSoundOptions(sel);
    if (_notifSoundEntitySelect) _notifSoundEntitySelect.destroy();
    const items = _getSoundAssetItems();
    if (items.length > 0) {
        _notifSoundEntitySelect = new EntitySelect({
            target: sel,
            getItems: () => _getSoundAssetItems(),
            placeholder: t('color_strip.notification.sound.search') || 'Search sounds…',
        });
    }
}

/* ── Test notification ────────────────────────────────────────── */

export async function testNotification(sourceId: string) {
    try {
        const resp = (await fetchWithAuth(`/color-strip-sources/${sourceId}/notify`, { method: 'POST' }))!;
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            showToast(err.detail || t('color_strip.notification.test.error'), 'error');
            return;
        }
        const data = await resp.json();
        if (data.streams_notified > 0) {
            showToast(t('color_strip.notification.test.ok'), 'success');
        } else {
            showToast(t('color_strip.notification.test.no_streams'), 'warning');
        }
    } catch {
        showToast(t('color_strip.notification.test.error'), 'error');
    }
}

// ── OS Notification History Modal ─────────────────────────────────────────

export function showNotificationHistory() {
    const modal = document.getElementById('notification-history-modal') as HTMLElement | null;
    if (!modal) return;
    modal.style.display = 'flex';
    modal.onclick = (e) => { if (e.target === modal) closeNotificationHistory(); };
    _loadNotificationHistory();
}

export function closeNotificationHistory() {
    const modal = document.getElementById('notification-history-modal') as HTMLElement | null;
    if (modal) modal.style.display = 'none';
}

export async function refreshNotificationHistory() {
    await _loadNotificationHistory();
}

async function _loadNotificationHistory() {
    const list = document.getElementById('notification-history-list') as HTMLElement | null;
    const status = document.getElementById('notification-history-status') as HTMLElement | null;
    if (!list) return;

    try {
        const resp = (await fetchWithAuth('/color-strip-sources/os-notifications/history'))!;
        if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
        const data = await resp.json();

        if (!data.available) {
            list.innerHTML = '';
            if (status) {
                status.textContent = t('color_strip.notification.history.unavailable');
                status.style.display = '';
            }
            return;
        }

        if (status) status.style.display = 'none';

        const history = data.history || [];
        if (history.length === 0) {
            list.innerHTML = `<div class="notif-history-empty">${t('color_strip.notification.history.empty')}</div>`;
            return;
        }

        list.innerHTML = history.map((entry: any) => {
            const appName = entry.app || t('color_strip.notification.history.unknown_app');
            const timeStr = new Date(entry.time * 1000).toLocaleString();
            const fired = entry.fired ?? 0;
            const filtered = entry.filtered ?? 0;
            const firedBadge = fired > 0
                ? `<span class="notif-history-badge notif-history-badge--fired" title="${t('color_strip.notification.history.fired')}">${fired}</span>`
                : '';
            const filteredBadge = filtered > 0
                ? `<span class="notif-history-badge notif-history-badge--filtered" title="${t('color_strip.notification.history.filtered')}">${filtered}</span>`
                : '';
            return `<div class="notif-history-row">
                <div class="notif-history-app" title="${escapeHtml(appName)}">${escapeHtml(appName)}</div>
                <div class="notif-history-time">${timeStr}</div>
                <div class="notif-history-badges">${firedBadge}${filteredBadge}</div>
            </div>`;
        }).join('');
    } catch (err: any) {
        console.error('Failed to load notification history:', err);
        if (status) {
            status.textContent = t('color_strip.notification.history.error');
            status.style.display = '';
        }
        list.innerHTML = '';
    }
}

/* ── Load / Reset state ───────────────────────────────────────── */

/**
 * Merge app_colors and app_sounds dicts into unified overrides list.
 * app_colors: {app: color}
 * app_sounds: {app: {sound_asset_id, volume}}
 */
function _mergeOverrides(appColors: Record<string, string>, appSounds: Record<string, any>): AppOverride[] {
    const appNames = new Set([...Object.keys(appColors), ...Object.keys(appSounds)]);
    return [...appNames].map(app => ({
        app,
        color: appColors[app] || '#ffffff',
        sound_asset_id: appSounds[app]?.sound_asset_id || null,
        volume: Math.round((appSounds[app]?.volume ?? 1.0) * 100),
    }));
}

export async function loadNotificationState(css: any) {
    (document.getElementById('css-editor-notification-os-listener') as HTMLInputElement).checked = !!css.os_listener;
    (document.getElementById('css-editor-notification-effect') as HTMLInputElement).value = css.notification_effect || 'flash';
    if (_notificationEffectIconSelect) _notificationEffectIconSelect.setValue(css.notification_effect || 'flash');
    const dur = css.duration_ms ?? 1500;
    (document.getElementById('css-editor-notification-duration') as HTMLInputElement).value = dur;
    (document.getElementById('css-editor-notification-duration-val') as HTMLElement).textContent = dur;
    (document.getElementById('css-editor-notification-default-color') as HTMLInputElement).value = css.default_color || '#ffffff';
    (document.getElementById('css-editor-notification-filter-mode') as HTMLInputElement).value = css.app_filter_mode || 'off';
    if (_notificationFilterModeIconSelect) _notificationFilterModeIconSelect.setValue(css.app_filter_mode || 'off');
    (document.getElementById('css-editor-notification-filter-list') as HTMLInputElement).value = (css.app_filter_list || []).join('\n');
    onNotificationFilterModeChange();
    _attachNotificationProcessPicker();

    // Ensure assets are loaded before populating sound dropdowns
    await assetsCache.fetch();

    // Sound (global)
    const soundSel = document.getElementById('css-editor-notification-sound') as HTMLSelectElement;
    _populateSoundOptions(soundSel, css.sound_asset_id);
    if (soundSel) soundSel.value = css.sound_asset_id || '';
    ensureNotifSoundEntitySelect();
    if (_notifSoundEntitySelect && css.sound_asset_id) _notifSoundEntitySelect.setValue(css.sound_asset_id);
    const volPct = Math.round((css.sound_volume ?? 1.0) * 100);
    (document.getElementById('css-editor-notification-volume') as HTMLInputElement).value = volPct as any;
    (document.getElementById('css-editor-notification-volume-val') as HTMLElement).textContent = `${volPct}%`;

    // Unified per-app overrides (merge app_colors + app_sounds)
    _notificationAppOverrides = _mergeOverrides(css.app_colors || {}, css.app_sounds || {});
    _overridesRenderList();

    showNotificationEndpoint(css.id);
}

export async function resetNotificationState() {
    (document.getElementById('css-editor-notification-os-listener') as HTMLInputElement).checked = true;
    (document.getElementById('css-editor-notification-effect') as HTMLInputElement).value = 'flash';
    if (_notificationEffectIconSelect) _notificationEffectIconSelect.setValue('flash');
    (document.getElementById('css-editor-notification-duration') as HTMLInputElement).value = 1500 as any;
    (document.getElementById('css-editor-notification-duration-val') as HTMLElement).textContent = '1500';
    (document.getElementById('css-editor-notification-default-color') as HTMLInputElement).value = '#ffffff';
    (document.getElementById('css-editor-notification-filter-mode') as HTMLInputElement).value = 'off';
    if (_notificationFilterModeIconSelect) _notificationFilterModeIconSelect.setValue('off');
    (document.getElementById('css-editor-notification-filter-list') as HTMLInputElement).value = '';
    onNotificationFilterModeChange();
    _attachNotificationProcessPicker();

    // Sound reset
    const soundSel = document.getElementById('css-editor-notification-sound') as HTMLSelectElement;
    _populateSoundOptions(soundSel);
    if (soundSel) soundSel.value = '';
    ensureNotifSoundEntitySelect();
    (document.getElementById('css-editor-notification-volume') as HTMLInputElement).value = 100 as any;
    (document.getElementById('css-editor-notification-volume-val') as HTMLElement).textContent = '100%';

    // Clear overrides
    _notificationAppOverrides = [];
    _overridesRenderList();

    showNotificationEndpoint(null);
}

function _attachNotificationProcessPicker() {
    const container = document.getElementById('css-editor-notification-filter-picker-container') as HTMLElement | null;
    const textarea = document.getElementById('css-editor-notification-filter-list') as HTMLTextAreaElement | null;
    if (container && textarea) attachNotificationAppPicker(container, textarea);
}

export function showNotificationEndpoint(cssId: any) {
    const el = document.getElementById('css-editor-notification-endpoint') as HTMLElement | null;
    if (!el) return;
    if (!cssId) {
        el.innerHTML = `<em data-i18n="color_strip.notification.save_first">${t('color_strip.notification.save_first')}</em>`;
        return;
    }
    const base = `${getBaseOrigin()}/api/v1`;
    const url = `${base}/color-strip-sources/${cssId}/notify`;
    el.innerHTML = `
        <small class="endpoint-label">POST</small>
        <div class="ws-url-row"><input type="text" value="${url}" readonly style="font-size:0.85em"><button type="button" class="btn btn-sm btn-secondary" onclick="copyEndpointUrl(this)" title="Copy">${ICON_CLONE}</button></div>
    `;
}
