/**
 * Automations — automation cards, editor, rule builder, process picker, scene selector.
 */

import { apiKey, _automationsLoading, set_automationsLoading, automationsCacheObj, scenePresetsCache, _cachedHASources } from '../core/state.ts';
import { getHAEntityIcon } from '../core/icons.ts';
import { fetchWithAuth, escapeHtml } from '../core/api.ts';
import { t } from '../core/i18n.ts';
import { showToast, showConfirm, setTabRefreshing } from '../core/ui.ts';
import { Modal } from '../core/modal.ts';
import { CardSection } from '../core/card-sections.ts';
import { updateTabBadge, updateSubTabHash } from './tabs.ts';
import { ICON_SETTINGS, ICON_START, ICON_PAUSE, ICON_CLOCK, ICON_AUTOMATION, ICON_HELP, ICON_OK, ICON_TIMER, ICON_MONITOR, ICON_RADIO, ICON_SCENE, ICON_CLONE, ICON_TRASH, ICON_CIRCLE_OFF, ICON_UNDO, ICON_WEB, ICON_SEARCH } from '../core/icons.ts';
import * as P from '../core/icon-paths.ts';
import { wrapCard } from '../core/card-colors.ts';
import { TagInput, renderTagChips } from '../core/tag-input.ts';
import { getBaseOrigin } from './settings.ts';
import { IconSelect } from '../core/icon-select.ts';
import { EntitySelect } from '../core/entity-palette.ts';
import { attachProcessPicker } from '../core/process-picker.ts';
import { TreeNav } from '../core/tree-nav.ts';
import { csScenes, createSceneCard, initScenePresetDelegation } from './scene-presets.ts';
import type { Automation } from '../types.ts';

// ── HA rule entity cache ──
let _haRuleEntities: any[] = [];

async function _loadHAEntitiesForRule(haSourceId: string, container: HTMLElement): Promise<void> {
    if (!haSourceId) { _haRuleEntities = []; return; }
    try {
        const resp = await fetchWithAuth(`/home-assistant/sources/${haSourceId}/entities`);
        if (!resp.ok) { _haRuleEntities = []; return; }
        const data = await resp.json();
        _haRuleEntities = data.entities || [];
    } catch {
        _haRuleEntities = [];
    }
    // Rebuild entity select options
    const entitySelect = container.querySelector('.rule-ha-entity-id') as HTMLSelectElement;
    if (entitySelect) {
        const currentVal = entitySelect.value;
        entitySelect.innerHTML = `<option value="">—</option>` +
            _haRuleEntities.map((e: any) =>
                `<option value="${e.entity_id}" ${e.entity_id === currentVal ? 'selected' : ''}>${escapeHtml(e.friendly_name || e.entity_id)}</option>`
            ).join('');
        if (currentVal && !_haRuleEntities.some((e: any) => e.entity_id === currentVal)) {
            entitySelect.innerHTML += `<option value="${escapeHtml(currentVal)}" selected>${escapeHtml(currentVal)}</option>`;
        }
        // Refresh the EntitySelect wrapper so the trigger shows the friendly name
        const es = (entitySelect as any)._entitySelect as EntitySelect | undefined;
        if (es) es.refresh();
    }
}

let _automationTagsInput: any = null;

// ── Auto-name ──

let _autoNameManuallyEdited = false;

function _autoGenerateAutomationName() {
    if (_autoNameManuallyEdited) return;
    if ((document.getElementById('automation-editor-id') as HTMLInputElement).value) return;
    const sceneSel = document.getElementById('automation-scene-id') as HTMLSelectElement | null;
    const sceneName = sceneSel?.selectedOptions[0]?.textContent?.trim() || '';
    const logic = (document.getElementById('automation-editor-logic') as HTMLSelectElement).value;
    const ruleCount = document.querySelectorAll('#automation-rules-list .rule-row').length;
    let name = '';
    if (sceneName) name = sceneName;
    if (ruleCount > 0) {
        const logicLabel = logic === 'and' ? 'AND' : 'OR';
        const suffix = `${ruleCount} ${logicLabel}`;
        name = name ? `${name} · ${suffix}` : suffix;
    }
    (document.getElementById('automation-editor-name') as HTMLInputElement).value = name || t('automations.add');
}

class AutomationEditorModal extends Modal {
    constructor() { super('automation-editor-modal'); }

    onForceClose() {
        if (_automationTagsInput) { _automationTagsInput.destroy(); _automationTagsInput = null; }
    }

    snapshotValues() {
        return {
            name: (document.getElementById('automation-editor-name') as HTMLInputElement).value,
            enabled: (document.getElementById('automation-editor-enabled') as HTMLInputElement).checked.toString(),
            logic: (document.getElementById('automation-editor-logic') as HTMLSelectElement).value,
            rules: JSON.stringify(getAutomationEditorRules()),
            scenePresetId: (document.getElementById('automation-scene-id') as HTMLSelectElement).value,
            deactivationMode: (document.getElementById('automation-deactivation-mode') as HTMLSelectElement).value,
            deactivationScenePresetId: (document.getElementById('automation-fallback-scene-id') as HTMLSelectElement).value,
            tags: JSON.stringify(_automationTagsInput ? _automationTagsInput.getValue() : []),
        };
    }
}

const automationModal = new AutomationEditorModal();

// ── Bulk action handlers ──
async function _bulkEnableAutomations(ids: any) {
    const results = await Promise.allSettled(ids.map(id =>
        fetchWithAuth(`/automations/${id}/enable`, { method: 'POST' })
    ));
    const failed = results.filter(r => r.status === 'rejected' || ((r as any).value && !(r as any).value.ok)).length;
    if (failed) showToast(`${ids.length - failed}/${ids.length} enabled`, 'warning');
    else showToast(t('automations.updated'), 'success');
    automationsCacheObj.invalidate();
    loadAutomations();
}

async function _bulkDisableAutomations(ids: any) {
    const results = await Promise.allSettled(ids.map(id =>
        fetchWithAuth(`/automations/${id}/disable`, { method: 'POST' })
    ));
    const failed = results.filter(r => r.status === 'rejected' || ((r as any).value && !(r as any).value.ok)).length;
    if (failed) showToast(`${ids.length - failed}/${ids.length} disabled`, 'warning');
    else showToast(t('automations.updated'), 'success');
    automationsCacheObj.invalidate();
    loadAutomations();
}

async function _bulkDeleteAutomations(ids: any) {
    const results = await Promise.allSettled(ids.map(id =>
        fetchWithAuth(`/automations/${id}`, { method: 'DELETE' })
    ));
    const failed = results.filter(r => r.status === 'rejected' || ((r as any).value && !(r as any).value.ok)).length;
    if (failed) showToast(`${ids.length - failed}/${ids.length} deleted`, 'warning');
    else showToast(t('automations.deleted'), 'success');
    automationsCacheObj.invalidate();
    loadAutomations();
}

const csAutomations = new CardSection('automations', { titleKey: 'automations.title', gridClass: 'devices-grid', addCardOnclick: "openAutomationEditor()", keyAttr: 'data-automation-id', emptyKey: 'section.empty.automations', bulkActions: [
    { key: 'enable', labelKey: 'bulk.enable', icon: ICON_OK, handler: _bulkEnableAutomations },
    { key: 'disable', labelKey: 'bulk.disable', icon: ICON_CIRCLE_OFF, handler: _bulkDisableAutomations },
    { key: 'delete', labelKey: 'bulk.delete', icon: ICON_TRASH, style: 'danger', confirm: 'bulk.confirm_delete', handler: _bulkDeleteAutomations },
] } as any);

// ── Tree navigation ──

let _automationsTreeTriggered = false;

const _automationsTree = new TreeNav('automations-tree-nav', {
    onSelect: (key: string) => {
        _automationsTreeTriggered = true;
        switchAutomationTab(key);
        _automationsTreeTriggered = false;
    }
});

export function switchAutomationTab(tabKey: string) {
    document.querySelectorAll('.automation-sub-tab-panel').forEach(panel =>
        (panel as HTMLElement).classList.toggle('active', panel.id === `automation-tab-${tabKey}`)
    );
    localStorage.setItem('activeAutomationTab', tabKey);
    updateSubTabHash('automations', tabKey);
    if (!_automationsTreeTriggered) {
        _automationsTree.setActive(tabKey);
    }
}

/* ── Condition logic IconSelect ───────────────────────────────── */

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

let _ruleLogicIconSelect: any = null;

function _ensureRuleLogicIconSelect() {
    const sel = document.getElementById('automation-editor-logic');
    if (!sel) return;
    const items = [
        { value: 'or',  icon: _icon(P.zap),       label: t('automations.rule_logic.or'),  desc: t('automations.rule_logic.or.desc') },
        { value: 'and', icon: _icon(P.link),       label: t('automations.rule_logic.and'), desc: t('automations.rule_logic.and.desc') },
    ];
    if (_ruleLogicIconSelect) { _ruleLogicIconSelect.updateItems(items); return; }
    _ruleLogicIconSelect = new IconSelect({ target: sel, items, columns: 2 } as any);
}

// Re-render automations when language changes (only if tab is active)
document.addEventListener('languageChanged', () => {
    if (apiKey && (localStorage.getItem('activeTab') || 'dashboard') === 'automations') loadAutomations();
});

// React to real-time automation state changes from global events WS
document.addEventListener('server:automation_state_changed', () => {
    if (apiKey && (localStorage.getItem('activeTab') || 'dashboard') === 'automations') {
        loadAutomations();
    }
});

export async function loadAutomations() {
    if (_automationsLoading) return;
    set_automationsLoading(true);
    const container = document.getElementById('automations-content');
    if (!container) { set_automationsLoading(false); return; }
    if (!csAutomations.isMounted()) setTabRefreshing('automations-content', true);

    try {
        const [automations, scenes] = await Promise.all([
            automationsCacheObj.fetch(),
            scenePresetsCache.fetch(),
        ]);

        const sceneMap = new Map(scenes.map(s => [s.id, s]));
        const activeCount = automations.filter(a => a.is_active).length;
        updateTabBadge('automations', activeCount);
        renderAutomations(automations, sceneMap);
    } catch (error: any) {
        if (error.isAuth) return;
        console.error('Failed to load automations:', error);
        container.innerHTML = `<p class="error-message">${escapeHtml(error.message)}</p>`;
    } finally {
        set_automationsLoading(false);
        setTabRefreshing('automations-content', false);
    }
}

function renderAutomations(automations: any, sceneMap: any) {
    const container = document.getElementById('automations-content');

    const autoItems = csAutomations.applySortOrder(automations.map(a => ({ key: a.id, html: createAutomationCard(a, sceneMap) })));
    const sceneItems = csScenes.applySortOrder(scenePresetsCache.data.map(s => ({ key: s.id, html: createSceneCard(s) })));

    const activeTab = localStorage.getItem('activeAutomationTab') || 'automations';

    const treeItems = [
        { key: 'automations', icon: ICON_AUTOMATION, titleKey: 'automations.title', count: automations.length },
        { key: 'scenes', icon: ICON_SCENE, titleKey: 'scenes.title', count: scenePresetsCache.data.length },
    ];

    if (csAutomations.isMounted()) {
        _automationsTree.updateCounts({
            automations: automations.length,
            scenes: scenePresetsCache.data.length,
        });
        csAutomations.reconcile(autoItems);
        csScenes.reconcile(sceneItems);
    } else {
        const panels = [
            { key: 'automations', html: csAutomations.render(autoItems) },
            { key: 'scenes', html: csScenes.render(sceneItems) },
        ].map(p => `<div class="automation-sub-tab-panel stream-tab-panel${p.key === activeTab ? ' active' : ''}" id="automation-tab-${p.key}">${p.html}</div>`).join('');

        container!.innerHTML = panels;
        CardSection.bindAll([csAutomations, csScenes]);

        // Event delegation for scene preset card actions
        initScenePresetDelegation(container!);

        _automationsTree.setExtraHtml(`<button class="tutorial-trigger-btn" onclick="startAutomationsTutorial()" data-i18n-title="tour.restart" title="${t('tour.restart')}">${ICON_HELP}</button>`);
        _automationsTree.update(treeItems, activeTab);
        _automationsTree.observeSections('automations-content', {
            'automations': 'automations',
            'scenes': 'scenes',
        });
    }
}

type RulePillRenderer = (c: any) => string;

const RULE_PILL_RENDERERS: Record<string, RulePillRenderer> = {
    startup: (c) => `<span class="stream-card-prop">${ICON_START} ${t('automations.rule.startup')}</span>`,
    application: (c) => {
        const apps = (c.apps || []).join(', ');
        const matchLabel = t('automations.rule.application.match_type.' + (c.match_type || 'running'));
        return `<span class="stream-card-prop stream-card-prop-full">${t('automations.rule.application')}: ${apps} (${matchLabel})</span>`;
    },
    time_of_day: (c) => `<span class="stream-card-prop">${ICON_CLOCK} ${t('automations.rule.time_of_day')}: ${c.start_time || '00:00'} – ${c.end_time || '23:59'}</span>`,
    system_idle: (c) => {
        const mode = c.when_idle !== false ? t('automations.rule.system_idle.when_idle') : t('automations.rule.system_idle.when_active');
        return `<span class="stream-card-prop">${ICON_TIMER} ${c.idle_minutes || 5}m (${mode})</span>`;
    },
    display_state: (c) => {
        const stateLabel = t('automations.rule.display_state.' + (c.state || 'on'));
        return `<span class="stream-card-prop">${ICON_MONITOR} ${t('automations.rule.display_state')}: ${stateLabel}</span>`;
    },
    mqtt: (c) => `<span class="stream-card-prop stream-card-prop-full">${ICON_RADIO} ${t('automations.rule.mqtt')}: ${escapeHtml(c.topic || '')} = ${escapeHtml(c.payload || '*')}</span>`,
    webhook: (c) => `<span class="stream-card-prop">${ICON_WEB} ${t('automations.rule.webhook')}</span>`,
    home_assistant: (c) => `<span class="stream-card-prop stream-card-prop-full">${_icon(P.home)} ${t('automations.rule.home_assistant')}: ${escapeHtml(c.entity_id || '')} = ${escapeHtml(c.state || '*')}</span>`,
};

function createAutomationCard(automation: Automation, sceneMap = new Map()) {
    const statusClass = !automation.enabled ? 'disabled' : automation.is_active ? 'active' : 'inactive';
    const statusText = !automation.enabled ? t('automations.status.disabled') : automation.is_active ? t('automations.status.active') : t('automations.status.inactive');

    let rulePills = '';
    if (automation.rules.length === 0) {
        rulePills = `<span class="stream-card-prop">${t('automations.rules.empty')}</span>`;
    } else {
        const parts = automation.rules.map(c => {
            const renderer = RULE_PILL_RENDERERS[c.rule_type];
            return renderer ? renderer(c) : `<span class="stream-card-prop">${c.rule_type}</span>`;
        });
        const logicLabel = automation.rule_logic === 'and' ? t('automations.logic.and') : t('automations.logic.or');
        rulePills = parts.join(`<span class="automation-logic-label">${logicLabel}</span>`);
    }

    // Scene info
    const scene = automation.scene_preset_id ? sceneMap.get(automation.scene_preset_id) : null;
    const sceneName = scene ? escapeHtml(scene.name) : t('automations.scene.none_selected');
    const sceneColor = scene ? scene.color || '#4fc3f7' : '#888';

    // Deactivation mode label
    let deactivationMeta = '';
    if (automation.deactivation_mode === 'revert') {
        deactivationMeta = `<span class="card-meta">${ICON_UNDO} ${t('automations.deactivation_mode.revert')}</span>`;
    } else if (automation.deactivation_mode === 'fallback_scene') {
        const fallback = automation.deactivation_scene_preset_id ? sceneMap.get(automation.deactivation_scene_preset_id) : null;
        if (fallback) {
            const fbColor = fallback.color || '#4fc3f7';
            deactivationMeta = `<span class="card-meta stream-card-link" onclick="event.stopPropagation(); navigateToCard('automations',null,'scenes','data-scene-id','${automation.deactivation_scene_preset_id}')">${ICON_UNDO} <span style="color:${fbColor}">&#x25CF;</span> ${escapeHtml(fallback.name)}</span>`;
        } else {
            deactivationMeta = `<span class="card-meta">${ICON_UNDO} ${t('automations.deactivation_mode.fallback_scene')}</span>`;
        }
    }

    let lastActivityMeta = '';
    if (automation.last_activated_at) {
        const ts = new Date(automation.last_activated_at);
        lastActivityMeta = `<span class="card-meta" title="${t('automations.last_activated')}">${ICON_CLOCK} ${ts.toLocaleString()}</span>`;
    }

    return wrapCard({
        dataAttr: 'data-automation-id',
        id: automation.id,
        classes: !automation.enabled ? 'automation-status-disabled' : '',
        removeOnclick: `deleteAutomation('${automation.id}', '${escapeHtml(automation.name)}')`,
        removeTitle: t('common.delete'),
        content: `
            <div class="card-header">
                <div class="card-title" title="${escapeHtml(automation.name)}">
                    <span class="card-title-text">${escapeHtml(automation.name)}</span>
                    <span class="badge badge-automation-${statusClass}">${statusText}</span>
                </div>
            </div>
            <div class="card-subtitle">
                <span class="card-meta">${rulePills}</span>
                <span class="card-meta${scene ? ' stream-card-link' : ''}"${scene ? ` onclick="event.stopPropagation(); navigateToCard('automations',null,'scenes','data-scene-id','${automation.scene_preset_id}')"` : ''}>${ICON_SCENE} <span style="color:${sceneColor}">&#x25CF;</span> ${sceneName}</span>
                ${deactivationMeta}
            </div>
            ${renderTagChips(automation.tags)}`,
        actions: `
                <button class="btn btn-icon btn-secondary" onclick="cloneAutomation('${automation.id}')" title="${t('common.clone')}">${ICON_CLONE}</button>
                <button class="btn btn-icon btn-secondary" onclick="openAutomationEditor('${automation.id}')" title="${t('automations.edit')}">${ICON_SETTINGS}</button>
                <button class="btn btn-icon ${automation.enabled ? 'btn-warning' : 'btn-success'}" onclick="toggleAutomationEnabled('${automation.id}', ${!automation.enabled})" title="${automation.enabled ? t('automations.action.disable') : t('automations.status.active')}">
                    ${automation.enabled ? ICON_PAUSE : ICON_START}
                </button>`,
    });
}

export async function openAutomationEditor(automationId?: any, cloneData?: any) {
    const modal = document.getElementById('automation-editor-modal');
    const titleEl = document.getElementById('automation-editor-title');
    const idInput = document.getElementById('automation-editor-id') as HTMLInputElement;
    const nameInput = document.getElementById('automation-editor-name') as HTMLInputElement;
    const enabledInput = document.getElementById('automation-editor-enabled') as HTMLInputElement;
    const logicSelect = document.getElementById('automation-editor-logic') as HTMLSelectElement;
    const ruleList = document.getElementById('automation-rules-list');
    const errorEl = document.getElementById('automation-editor-error') as HTMLElement;

    errorEl.style.display = 'none';
    ruleList!.innerHTML = '';

    _ensureRuleLogicIconSelect();
    _ensureDeactivationModeIconSelect();

    // Fetch scenes for selector
    try {
        await scenePresetsCache.fetch();
    } catch { /* use cached */ }

    // Reset deactivation mode
    (document.getElementById('automation-deactivation-mode') as HTMLSelectElement).value = 'none';
    if (_deactivationModeIconSelect) _deactivationModeIconSelect.setValue('none');
    (document.getElementById('automation-fallback-scene-group') as HTMLElement).style.display = 'none';

    let _editorTags: any[] = [];

    if (automationId) {
        titleEl!.innerHTML = `${ICON_AUTOMATION} ${t('automations.edit')}`;
        try {
            const resp = await fetchWithAuth(`/automations/${automationId}`);
            if (!resp.ok) throw new Error('Failed to load automation');
            const automation = await resp.json();

            idInput.value = automation.id;
            nameInput.value = automation.name;
            enabledInput.checked = automation.enabled;
            logicSelect.value = automation.rule_logic;
            if (_ruleLogicIconSelect) _ruleLogicIconSelect.setValue(automation.rule_logic);

            for (const c of automation.rules) {
                addAutomationRuleRow(c);
            }

            // Scene selector
            _initSceneSelector('automation-scene-id', automation.scene_preset_id);

            // Deactivation mode
            const deactMode = automation.deactivation_mode || 'none';
            (document.getElementById('automation-deactivation-mode') as HTMLSelectElement).value = deactMode;
            if (_deactivationModeIconSelect) _deactivationModeIconSelect.setValue(deactMode);
            _onDeactivationModeChange();
            _initSceneSelector('automation-fallback-scene-id', automation.deactivation_scene_preset_id);
            _editorTags = automation.tags || [];
        } catch (e: any) {
            showToast(e.message, 'error');
            return;
        }
    } else if (cloneData) {
        // Clone mode — create with prefilled data
        titleEl!.innerHTML = `${ICON_AUTOMATION} ${t('automations.add')}`;
        idInput.value = '';
        nameInput.value = (cloneData.name || '') + ' (Copy)';
        enabledInput.checked = cloneData.enabled !== false;
        logicSelect.value = cloneData.rule_logic || 'or';
        if (_ruleLogicIconSelect) _ruleLogicIconSelect.setValue(cloneData.rule_logic || 'or');

        // Clone rules (strip webhook tokens — they must be unique)
        for (const c of (cloneData.rules || [])) {
            const clonedCond = { ...c };
            if (clonedCond.rule_type === 'webhook') delete clonedCond.token;
            addAutomationRuleRow(clonedCond);
        }

        _initSceneSelector('automation-scene-id', cloneData.scene_preset_id);

        const cloneDeactMode = cloneData.deactivation_mode || 'none';
        (document.getElementById('automation-deactivation-mode') as HTMLSelectElement).value = cloneDeactMode;
        if (_deactivationModeIconSelect) _deactivationModeIconSelect.setValue(cloneDeactMode);
        _onDeactivationModeChange();
        _initSceneSelector('automation-fallback-scene-id', cloneData.deactivation_scene_preset_id);
        _editorTags = cloneData.tags || [];
    } else {
        titleEl!.innerHTML = `${ICON_AUTOMATION} ${t('automations.add')}`;
        idInput.value = '';
        nameInput.value = '';
        enabledInput.checked = true;
        logicSelect.value = 'or';
        if (_ruleLogicIconSelect) _ruleLogicIconSelect.setValue('or');
        _initSceneSelector('automation-scene-id', null);
        _initSceneSelector('automation-fallback-scene-id', null);
    }

    // Wire up deactivation mode change
    (document.getElementById('automation-deactivation-mode') as HTMLSelectElement).onchange = _onDeactivationModeChange;

    // Auto-name wiring
    _autoNameManuallyEdited = !!(automationId || cloneData);
    nameInput.oninput = () => { _autoNameManuallyEdited = true; };
    (window as any)._autoGenerateAutomationName = _autoGenerateAutomationName;
    if (!automationId && !cloneData) _autoGenerateAutomationName();

    automationModal.open();
    modal!.querySelectorAll('[data-i18n]').forEach(el => {
        el.textContent = t(el.getAttribute('data-i18n')!);
    });
    modal!.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
        (el as HTMLInputElement).placeholder = t(el.getAttribute('data-i18n-placeholder')!);
    });

    // Tags
    if (_automationTagsInput) { _automationTagsInput.destroy(); _automationTagsInput = null; }
    _automationTagsInput = new TagInput(document.getElementById('automation-tags-container'), { placeholder: t('tags.placeholder') });
    _automationTagsInput.setValue(_editorTags);

    automationModal.snapshot();
}

function _onDeactivationModeChange() {
    const mode = (document.getElementById('automation-deactivation-mode') as HTMLSelectElement).value;
    (document.getElementById('automation-fallback-scene-group') as HTMLElement).style.display = mode === 'fallback_scene' ? '' : 'none';
}

export async function closeAutomationEditorModal() {
    await automationModal.close();
}

// ===== Scene selector (EntitySelect) =====

let _sceneEntitySelect: any = null;
let _fallbackSceneEntitySelect: any = null;

function _getSceneItems() {
    return (scenePresetsCache.data || []).map(s => ({
        value: s.id,
        label: s.name,
        icon: `<span class="scene-color-dot" style="background:${s.color || '#4fc3f7'}"></span>`,
    }));
}

function _initSceneSelector(selectId: any, selectedId: any) {
    const sel = document.getElementById(selectId) as HTMLSelectElement;
    // Populate <select> with scene options
    sel.innerHTML = (scenePresetsCache.data || []).map(s =>
        `<option value="${s.id}">${escapeHtml(s.name)}</option>`
    ).join('');
    sel.value = selectedId || '';

    // Determine which EntitySelect slot to use
    const isMain = selectId === 'automation-scene-id';
    const existing = isMain ? _sceneEntitySelect : _fallbackSceneEntitySelect;
    if (existing) existing.destroy();

    const es = new EntitySelect({
        target: sel,
        getItems: _getSceneItems,
        placeholder: t('automations.scene.search_placeholder'),
        allowNone: true,
        noneLabel: t('automations.scene.none_selected'),
    } as any);
    if (isMain) {
        _sceneEntitySelect = es;
        sel.onchange = () => _autoGenerateAutomationName();
    } else {
        _fallbackSceneEntitySelect = es;
    }
}

// ===== Deactivation mode IconSelect =====

const DEACT_MODE_KEYS = ['none', 'revert', 'fallback_scene'];
const DEACT_MODE_ICONS = {
    none: P.pause, revert: P.undo2, fallback_scene: P.sparkles,
};
let _deactivationModeIconSelect: any = null;

function _ensureDeactivationModeIconSelect() {
    const sel = document.getElementById('automation-deactivation-mode');
    if (!sel || _deactivationModeIconSelect) return;
    const items = DEACT_MODE_KEYS.map(k => ({
        value: k,
        icon: _icon(DEACT_MODE_ICONS[k]),
        label: t(`automations.deactivation_mode.${k}`),
        desc: t(`automations.deactivation_mode.${k}.desc`),
    }));
    _deactivationModeIconSelect = new IconSelect({ target: sel, items, columns: 3 } as any);
}

// ===== Condition editor =====

export function addAutomationRule() {
    addAutomationRuleRow({ rule_type: 'application', apps: [], match_type: 'running' });
    _autoGenerateAutomationName();
}

const RULE_TYPE_KEYS = ['startup', 'application', 'time_of_day', 'system_idle', 'display_state', 'mqtt', 'webhook', 'home_assistant'];
const RULE_TYPE_ICONS = {
    startup: P.power, application: P.smartphone,
    time_of_day: P.clock, system_idle: P.moon, display_state: P.monitor,
    mqtt: P.radio, webhook: P.globe, home_assistant: P.home,
};

const MATCH_TYPE_KEYS = ['running', 'topmost', 'topmost_fullscreen', 'fullscreen'];
const MATCH_TYPE_ICONS = {
    running: P.play, topmost: P.monitor, topmost_fullscreen: P.tv, fullscreen: P.square,
};

function _buildMatchTypeItems() {
    return MATCH_TYPE_KEYS.map(k => ({
        value: k,
        icon: _icon(MATCH_TYPE_ICONS[k]),
        label: t(`automations.rule.application.match_type.${k}`),
        desc: t(`automations.rule.application.match_type.${k}.desc`),
    }));
}

function _buildRuleTypeItems() {
    return RULE_TYPE_KEYS.map(k => ({
        value: k,
        icon: _icon(RULE_TYPE_ICONS[k]),
        label: t(`automations.rule.${k}`),
        desc: t(`automations.rule.${k}.desc`),
    }));
}

/** Wire up the custom time-range picker inputs → sync to hidden fields. */
function _wireTimeRangePicker(container: HTMLElement) {
    const startH = container.querySelector('.tr-start-h') as HTMLInputElement;
    const startM = container.querySelector('.tr-start-m') as HTMLInputElement;
    const endH = container.querySelector('.tr-end-h') as HTMLInputElement;
    const endM = container.querySelector('.tr-end-m') as HTMLInputElement;
    const hiddenStart = container.querySelector('.rule-start-time') as HTMLInputElement;
    const hiddenEnd = container.querySelector('.rule-end-time') as HTMLInputElement;
    if (!startH || !startM || !endH || !endM) return;

    const pad = (n: number) => String(n).padStart(2, '0');

    function clamp(input: HTMLInputElement, min: number, max: number) {
        let v = parseInt(input.value, 10);
        if (isNaN(v)) v = min;
        if (v < min) v = min;
        if (v > max) v = max;
        input.value = pad(v);
        return v;
    }

    function sync() {
        const sh = clamp(startH, 0, 23);
        const sm = clamp(startM, 0, 59);
        const eh = clamp(endH, 0, 23);
        const em = clamp(endM, 0, 59);
        hiddenStart.value = `${pad(sh)}:${pad(sm)}`;
        hiddenEnd.value = `${pad(eh)}:${pad(em)}`;
    }

    [startH, startM, endH, endM].forEach(inp => {
        inp.addEventListener('focus', () => inp.select());
        inp.addEventListener('input', sync);
        inp.addEventListener('blur', sync);
        inp.addEventListener('keydown', (e) => {
            const isHour = inp.dataset.role === 'hour';
            const max = isHour ? 23 : 59;
            if (e.key === 'ArrowUp') {
                e.preventDefault();
                let v = parseInt(inp.value, 10) || 0;
                inp.value = pad(v >= max ? 0 : v + 1);
                sync();
            } else if (e.key === 'ArrowDown') {
                e.preventDefault();
                let v = parseInt(inp.value, 10) || 0;
                inp.value = pad(v <= 0 ? max : v - 1);
                sync();
            }
        });
    });

    sync();
}

function addAutomationRuleRow(rule: any) {
    const list = document.getElementById('automation-rules-list');
    const row = document.createElement('div');
    row.className = 'automation-rule-row';
    const ruleType = rule.rule_type || 'application';

    row.innerHTML = `
        <div class="rule-header">
            <select class="rule-type-select">
                ${RULE_TYPE_KEYS.map(k => `<option value="${k}" ${ruleType === k ? 'selected' : ''}>${t('automations.rule.' + k)}</option>`).join('')}
            </select>
            <button type="button" class="btn-remove-rule" onclick="this.closest('.automation-rule-row').remove(); if(window._autoGenerateAutomationName) window._autoGenerateAutomationName();" title="Remove">${ICON_TRASH}</button>
        </div>
        <div class="rule-fields-container"></div>
    `;

    const typeSelect = row.querySelector('.rule-type-select') as HTMLSelectElement;
    const container = row.querySelector('.rule-fields-container') as HTMLElement;

    // Attach IconSelect to the rule type dropdown
    const ruleIconSelect = new IconSelect({
        target: typeSelect,
        items: _buildRuleTypeItems(),
        columns: 4,
    } as any);

    function renderFields(type: any, data: any) {
        if (type === 'startup') {
            container.innerHTML = `<small class="rule-hint-desc">${t('automations.rule.startup.hint')}</small>`;
            return;
        }
        if (type === 'time_of_day') {
            const startTime = data.start_time || '00:00';
            const endTime = data.end_time || '23:59';
            const [sh, sm] = startTime.split(':').map(Number);
            const [eh, em] = endTime.split(':').map(Number);
            const pad = (n: number) => String(n).padStart(2, '0');
            container.innerHTML = `
                <div class="rule-fields">
                    <input type="hidden" class="rule-start-time" value="${startTime}">
                    <input type="hidden" class="rule-end-time" value="${endTime}">
                    <div class="time-range-picker">
                        <div class="time-range-slot">
                            <span class="time-range-label">${t('automations.rule.time_of_day.start_time')}</span>
                            <div class="time-range-input-wrap">
                                <input type="number" class="tr-start-h" min="0" max="23" value="${sh}" data-role="hour">
                                <span class="time-range-colon">:</span>
                                <input type="number" class="tr-start-m" min="0" max="59" value="${pad(sm)}" data-role="minute">
                            </div>
                        </div>
                        <div class="time-range-arrow">→</div>
                        <div class="time-range-slot">
                            <span class="time-range-label">${t('automations.rule.time_of_day.end_time')}</span>
                            <div class="time-range-input-wrap">
                                <input type="number" class="tr-end-h" min="0" max="23" value="${eh}" data-role="hour">
                                <span class="time-range-colon">:</span>
                                <input type="number" class="tr-end-m" min="0" max="59" value="${pad(em)}" data-role="minute">
                            </div>
                        </div>
                    </div>
                    <small class="rule-hint-desc">${t('automations.rule.time_of_day.overnight_hint')}</small>
                </div>`;
            _wireTimeRangePicker(container);
            return;
        }
        if (type === 'system_idle') {
            const idleMinutes = data.idle_minutes ?? 5;
            const whenIdle = data.when_idle ?? true;
            container.innerHTML = `
                <div class="rule-fields">
                    <div class="rule-field">
                        <label>${t('automations.rule.system_idle.idle_minutes')}</label>
                        <input type="number" class="rule-idle-minutes" min="1" max="999" value="${idleMinutes}">
                    </div>
                    <div class="rule-field">
                        <label>${t('automations.rule.system_idle.mode')}</label>
                        <select class="rule-when-idle">
                            <option value="true" ${whenIdle ? 'selected' : ''}>${t('automations.rule.system_idle.when_idle')}</option>
                            <option value="false" ${!whenIdle ? 'selected' : ''}>${t('automations.rule.system_idle.when_active')}</option>
                        </select>
                    </div>
                </div>`;
            return;
        }
        if (type === 'display_state') {
            const dState = data.state || 'on';
            container.innerHTML = `
                <div class="rule-fields">
                    <div class="rule-field">
                        <label>${t('automations.rule.display_state.state')}</label>
                        <select class="rule-display-state">
                            <option value="on" ${dState === 'on' ? 'selected' : ''}>${t('automations.rule.display_state.on')}</option>
                            <option value="off" ${dState === 'off' ? 'selected' : ''}>${t('automations.rule.display_state.off')}</option>
                        </select>
                    </div>
                </div>`;
            return;
        }
        if (type === 'mqtt') {
            const topic = data.topic || '';
            const payload = data.payload || '';
            const matchMode = data.match_mode || 'exact';
            container.innerHTML = `
                <div class="rule-fields">
                    <div class="rule-field">
                        <label>${t('automations.rule.mqtt.topic')}</label>
                        <input type="text" class="rule-mqtt-topic" value="${escapeHtml(topic)}" placeholder="home/status/power">
                    </div>
                    <div class="rule-field">
                        <label>${t('automations.rule.mqtt.payload')}</label>
                        <input type="text" class="rule-mqtt-payload" value="${escapeHtml(payload)}" placeholder="ON">
                    </div>
                    <div class="rule-field">
                        <label>${t('automations.rule.mqtt.match_mode')}</label>
                        <select class="rule-mqtt-match-mode">
                            <option value="exact" ${matchMode === 'exact' ? 'selected' : ''}>${t('automations.rule.mqtt.match_mode.exact')}</option>
                            <option value="contains" ${matchMode === 'contains' ? 'selected' : ''}>${t('automations.rule.mqtt.match_mode.contains')}</option>
                            <option value="regex" ${matchMode === 'regex' ? 'selected' : ''}>${t('automations.rule.mqtt.match_mode.regex')}</option>
                        </select>
                    </div>
                </div>`;
            return;
        }
        if (type === 'home_assistant') {
            const haSourceId = data.ha_source_id || '';
            const entityId = data.entity_id || '';
            const haState = data.state || '';
            const matchMode = data.match_mode || 'exact';
            const haOptions = _cachedHASources.map((s: any) =>
                `<option value="${s.id}" ${s.id === haSourceId ? 'selected' : ''}>${escapeHtml(s.name)}</option>`
            ).join('');
            container.innerHTML = `
                <div class="rule-fields">
                    <small class="rule-hint-desc">${t('automations.rule.home_assistant.hint')}</small>
                    <div class="rule-field">
                        <label>${t('automations.rule.home_assistant.ha_source')}</label>
                        <select class="rule-ha-source-id">
                            <option value="">—</option>
                            ${haOptions}
                        </select>
                    </div>
                    <div class="rule-field">
                        <label>${t('automations.rule.home_assistant.entity_id')}</label>
                        <select class="rule-ha-entity-id">
                            ${entityId ? `<option value="${escapeHtml(entityId)}" selected>${escapeHtml(entityId)}</option>` : '<option value="">—</option>'}
                        </select>
                    </div>
                    <div class="rule-field">
                        <label>${t('automations.rule.home_assistant.state')}</label>
                        <input type="text" class="rule-ha-state" value="${escapeHtml(haState)}" placeholder="on">
                    </div>
                    <div class="rule-field">
                        <label>${t('automations.rule.home_assistant.match_mode')}</label>
                        <select class="rule-ha-match-mode">
                            <option value="exact" ${matchMode === 'exact' ? 'selected' : ''}>${t('automations.rule.mqtt.match_mode.exact')}</option>
                            <option value="contains" ${matchMode === 'contains' ? 'selected' : ''}>${t('automations.rule.mqtt.match_mode.contains')}</option>
                            <option value="regex" ${matchMode === 'regex' ? 'selected' : ''}>${t('automations.rule.mqtt.match_mode.regex')}</option>
                        </select>
                    </div>
                </div>`;

            // Wire HA source EntitySelect
            const haSrcSelect = container.querySelector('.rule-ha-source-id') as HTMLSelectElement;
            new EntitySelect({
                target: haSrcSelect,
                getItems: () => _cachedHASources.map((s: any) => ({
                    value: s.id, label: s.name, icon: _icon(P.home),
                    desc: s.connected ? t('ha_source.connected') : t('ha_source.disconnected'),
                })),
                placeholder: t('palette.search'),
                onChange: (newId: string) => _loadHAEntitiesForRule(newId, container),
            });

            // Wire entity EntitySelect
            const entitySelect = container.querySelector('.rule-ha-entity-id') as HTMLSelectElement;
            const entityES = new EntitySelect({
                target: entitySelect,
                getItems: () => _haRuleEntities.map((e: any) => ({
                    value: e.entity_id, label: e.friendly_name || e.entity_id,
                    icon: getHAEntityIcon(e), desc: e.state || '',
                })),
                placeholder: t('ha_light.mapping.search_entity'),
            });
            // Store ref so _loadHAEntitiesForRule can refresh the trigger display
            (entitySelect as any)._entitySelect = entityES;

            // Wire match mode IconSelect
            const matchSelect = container.querySelector('.rule-ha-match-mode') as HTMLSelectElement;
            new IconSelect({
                target: matchSelect,
                items: [
                    { value: 'exact', icon: _icon(P.check), label: t('automations.rule.mqtt.match_mode.exact'), desc: t('automations.rule.ha.match_mode.exact.desc') },
                    { value: 'contains', icon: _icon(P.search), label: t('automations.rule.mqtt.match_mode.contains'), desc: t('automations.rule.ha.match_mode.contains.desc') },
                    { value: 'regex', icon: _icon(P.code), label: t('automations.rule.mqtt.match_mode.regex'), desc: t('automations.rule.ha.match_mode.regex.desc') },
                ],
                columns: 1,
            });

            // Load entities if source is already selected
            if (haSourceId) _loadHAEntitiesForRule(haSourceId, container);

            return;
        }
        if (type === 'webhook') {
            if (data.token) {
                const webhookUrl = getBaseOrigin() + '/api/v1/webhooks/' + data.token;
                container.innerHTML = `
                    <div class="rule-fields">
                        <small class="rule-hint-desc">${t('automations.rule.webhook.hint')}</small>
                        <div class="rule-field">
                            <label>${t('automations.rule.webhook.url')}</label>
                            <div class="webhook-url-row">
                                <input type="text" class="rule-webhook-url" value="${escapeHtml(webhookUrl)}" readonly>
                                <button type="button" class="btn btn-secondary btn-webhook-copy" onclick="copyWebhookUrl(this)">${t('automations.rule.webhook.copy')}</button>
                            </div>
                        </div>
                        <input type="hidden" class="rule-webhook-token" value="${escapeHtml(data.token)}">
                    </div>`;
            } else {
                container.innerHTML = `
                    <div class="rule-fields">
                        <small class="rule-hint-desc">${t('automations.rule.webhook.hint')}</small>
                        <p class="webhook-save-hint">${t('automations.rule.webhook.save_first')}</p>
                    </div>`;
            }
            return;
        }
        const appsValue = (data.apps || []).join('\n');
        const matchType = data.match_type || 'running';
        container.innerHTML = `
            <div class="rule-fields">
                <div class="rule-field">
                    <label>${t('automations.rule.application.match_type')}</label>
                    <select class="rule-match-type">
                        <option value="running" ${matchType === 'running' ? 'selected' : ''}>${t('automations.rule.application.match_type.running')}</option>
                        <option value="topmost" ${matchType === 'topmost' ? 'selected' : ''}>${t('automations.rule.application.match_type.topmost')}</option>
                        <option value="topmost_fullscreen" ${matchType === 'topmost_fullscreen' ? 'selected' : ''}>${t('automations.rule.application.match_type.topmost_fullscreen')}</option>
                        <option value="fullscreen" ${matchType === 'fullscreen' ? 'selected' : ''}>${t('automations.rule.application.match_type.fullscreen')}</option>
                    </select>
                </div>
                <div class="rule-field">
                    <div class="rule-apps-header">
                        <label>${t('automations.rule.application.apps')}</label>
                        <button type="button" class="btn btn-icon btn-secondary btn-browse-apps" title="${t('automations.rule.application.browse')}">${ICON_SEARCH}</button>
                    </div>
                    <textarea class="rule-apps" rows="3" placeholder="firefox.exe&#10;chrome.exe">${escapeHtml(appsValue)}</textarea>
                </div>
            </div>
        `;
        const textarea = container.querySelector('.rule-apps') as HTMLTextAreaElement;
        attachProcessPicker(container, textarea);

        // Attach IconSelect to match type
        const matchSel = container.querySelector('.rule-match-type');
        if (matchSel) {
            new IconSelect({
                target: matchSel,
                items: _buildMatchTypeItems(),
                columns: 2,
            } as any);
        }
    }

    renderFields(ruleType, rule);
    typeSelect.addEventListener('change', () => {
        renderFields(typeSelect.value, {});
    });

    list!.appendChild(row);
}



function getAutomationEditorRules() {
    const rows = document.querySelectorAll('#automation-rules-list .automation-rule-row');
    const rules: any[] = [];
    rows.forEach(row => {
        const typeSelect = row.querySelector('.rule-type-select') as HTMLSelectElement;
        const ruleType = typeSelect ? typeSelect.value : 'application';
        if (ruleType === 'startup') {
            rules.push({ rule_type: 'startup' });
        } else if (ruleType === 'time_of_day') {
            rules.push({
                rule_type: 'time_of_day',
                start_time: (row.querySelector('.rule-start-time') as HTMLInputElement).value || '00:00',
                end_time: (row.querySelector('.rule-end-time') as HTMLInputElement).value || '23:59',
            });
        } else if (ruleType === 'system_idle') {
            rules.push({
                rule_type: 'system_idle',
                idle_minutes: parseInt((row.querySelector('.rule-idle-minutes') as HTMLInputElement).value, 10) || 5,
                when_idle: (row.querySelector('.rule-when-idle') as HTMLSelectElement).value === 'true',
            });
        } else if (ruleType === 'display_state') {
            rules.push({
                rule_type: 'display_state',
                state: (row.querySelector('.rule-display-state') as HTMLSelectElement).value || 'on',
            });
        } else if (ruleType === 'mqtt') {
            rules.push({
                rule_type: 'mqtt',
                topic: (row.querySelector('.rule-mqtt-topic') as HTMLInputElement).value.trim(),
                payload: (row.querySelector('.rule-mqtt-payload') as HTMLInputElement).value,
                match_mode: (row.querySelector('.rule-mqtt-match-mode') as HTMLSelectElement).value || 'exact',
            });
        } else if (ruleType === 'webhook') {
            const tokenInput = row.querySelector('.rule-webhook-token') as HTMLInputElement;
            const r: any = { rule_type: 'webhook' };
            if (tokenInput && tokenInput.value) r.token = tokenInput.value;
            rules.push(r);
        } else if (ruleType === 'home_assistant') {
            rules.push({
                rule_type: 'home_assistant',
                ha_source_id: (row.querySelector('.rule-ha-source-id') as HTMLSelectElement).value,
                entity_id: (row.querySelector('.rule-ha-entity-id') as HTMLSelectElement).value.trim(),
                state: (row.querySelector('.rule-ha-state') as HTMLInputElement).value,
                match_mode: (row.querySelector('.rule-ha-match-mode') as HTMLSelectElement).value || 'exact',
            });
        } else {
            const matchType = (row.querySelector('.rule-match-type') as HTMLSelectElement).value;
            const appsText = (row.querySelector('.rule-apps') as HTMLTextAreaElement).value.trim();
            const apps = appsText ? appsText.split('\n').map(a => a.trim()).filter(Boolean) : [];
            rules.push({ rule_type: 'application', apps, match_type: matchType });
        }
    });
    return rules;
}

export async function saveAutomationEditor() {
    const idInput = document.getElementById('automation-editor-id') as HTMLInputElement;
    const nameInput = document.getElementById('automation-editor-name') as HTMLInputElement;
    const enabledInput = document.getElementById('automation-editor-enabled') as HTMLInputElement;
    const logicSelect = document.getElementById('automation-editor-logic') as HTMLSelectElement;

    const name = nameInput.value.trim();
    if (!name) {
        automationModal.showError(t('automations.error.name_required'));
        return;
    }

    const body = {
        name,
        enabled: enabledInput.checked,
        rule_logic: logicSelect.value,
        rules: getAutomationEditorRules(),
        scene_preset_id: (document.getElementById('automation-scene-id') as HTMLSelectElement).value || null,
        deactivation_mode: (document.getElementById('automation-deactivation-mode') as HTMLSelectElement).value,
        deactivation_scene_preset_id: (document.getElementById('automation-fallback-scene-id') as HTMLSelectElement).value || null,
        tags: _automationTagsInput ? _automationTagsInput.getValue() : [],
    };

    const automationId = idInput.value;
    const isEdit = !!automationId;

    try {
        const url = isEdit ? `/automations/${automationId}` : '/automations';
        const resp = await fetchWithAuth(url, {
            method: isEdit ? 'PUT' : 'POST',
            body: JSON.stringify(body),
        });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || 'Failed to save automation');
        }

        automationModal.forceClose();
        showToast(isEdit ? t('automations.updated') : t('automations.created'), 'success');
        automationsCacheObj.invalidate();
        loadAutomations();
    } catch (e: any) {
        if (e.isAuth) return;
        automationModal.showError(e.message);
    }
}

export async function toggleAutomationEnabled(automationId: any, enable: any) {
    try {
        const action = enable ? 'enable' : 'disable';
        const resp = await fetchWithAuth(`/automations/${automationId}/${action}`, {
            method: 'POST',
        });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `Failed to ${action} automation`);
        }
        automationsCacheObj.invalidate();
        loadAutomations();
    } catch (e: any) {
        if (e.isAuth) return;
        showToast(e.message, 'error');
    }
}

export function copyWebhookUrl(btn: any) {
    const input = btn.closest('.webhook-url-row').querySelector('.rule-webhook-url') as HTMLInputElement;
    if (!input || !input.value) return;
    const onCopied = () => {
        const orig = btn.textContent;
        btn.textContent = t('automations.rule.webhook.copied');
        setTimeout(() => { btn.textContent = orig; }, 1500);
    };
    if (navigator.clipboard && window.isSecureContext) {
        navigator.clipboard.writeText(input.value).then(onCopied);
    } else {
        input.select();
        document.execCommand('copy');
        onCopied();
    }
}

export async function cloneAutomation(automationId: any) {
    try {
        const resp = await fetchWithAuth(`/automations/${automationId}`);
        if (!resp.ok) throw new Error('Failed to load automation');
        const automation = await resp.json();
        openAutomationEditor(null, automation);
    } catch (e: any) {
        if (e.isAuth) return;
        showToast(t('automations.error.clone_failed'), 'error');
    }
}

export async function deleteAutomation(automationId: any, automationName: any) {
    const msg = t('automations.delete.confirm').replace('{name}', automationName);
    const confirmed = await showConfirm(msg);
    if (!confirmed) return;

    try {
        const resp = await fetchWithAuth(`/automations/${automationId}`, {
            method: 'DELETE',
        });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || 'Failed to delete automation');
        }
        showToast(t('automations.deleted'), 'success');
        automationsCacheObj.invalidate();
        loadAutomations();
    } catch (e: any) {
        if (e.isAuth) return;
        showToast(e.message, 'error');
    }
}
