/**
 * Home Assistant Sources — CRUD, test, cards.
 */

import {
    _cachedHASources, haSourcesCache,
    _haEntityNamesCache, setHAEntityNames,
} from '../core/state.ts';
import { fetchWithAuth, escapeHtml } from '../core/api.ts';
import { t } from '../core/i18n.ts';
import { Modal } from '../core/modal.ts';
import { showToast, showConfirm } from '../core/ui.ts';
import { ICON_CLONE, ICON_EDIT, ICON_REFRESH } from '../core/icons.ts';
import * as P from '../core/icon-paths.ts';
import { wrapCard } from '../core/card-colors.ts';
import type { ModCardOpts, ModChipOpts, LedState } from '../core/mod-card.ts';
import { TagInput, renderTagChips } from '../core/tag-input.ts';
import { makeCardIconFields } from '../core/card-icon.ts';
import { registerIconEntityType, makeSimpleIconAdapter } from './icon-picker.ts';
import type { HomeAssistantSource } from '../types.ts';

registerIconEntityType('ha_source', makeSimpleIconAdapter<HomeAssistantSource>({
    cache: haSourcesCache,
    endpointPrefix: '/home-assistant/sources',
    reload: async () => {
        if (typeof window.loadIntegrations === 'function') {
            await window.loadIntegrations();
        }
    },
    typeLabelKey: 'device.icon.entity.ha_source',
    typeLabelFallback: 'Home Assistant source',
    cardSelectors: (id) => [
        `[data-card-section="ha-sources"] [data-id="${CSS.escape(id)}"]`,
    ],
}));

const ICON_HA = `<svg class="icon" viewBox="0 0 24 24">${P.home}</svg>`;

// ── Modal ──

let _haTagsInput: TagInput | null = null;

class HASourceModal extends Modal {
    constructor() { super('ha-source-modal'); }

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

    snapshotValues() {
        return {
            name: (document.getElementById('ha-source-name') as HTMLInputElement).value,
            host: (document.getElementById('ha-source-host') as HTMLInputElement).value,
            token: (document.getElementById('ha-source-token') as HTMLInputElement).value,
            use_ssl: (document.getElementById('ha-source-ssl') as HTMLInputElement).checked,
            entity_filters: (document.getElementById('ha-source-filters') as HTMLInputElement).value,
            description: (document.getElementById('ha-source-description') as HTMLInputElement).value,
            tags: JSON.stringify(_haTagsInput ? _haTagsInput.getValue() : []),
        };
    }
}

const haSourceModal = new HASourceModal();

// ── Entity friendly-name cache ──────────────────────────────────
//
// Fetching entities calls the live HA instance, so we only refresh
// the cache when the user actively interacts with a source. The
// Streams tab triggers `prefetchHAEntities()` for sources referenced
// by ha_entity value sources on initial load; subsequent edits in
// the value-source editor refresh via `_fetchVSHAEntities`.

/** Fetch entities for one HA source and write friendly names into the
 *  shared cache. Failures are silent — callers fall back to entity_id. */
export async function fetchHAEntities(haSourceId: string): Promise<void> {
    if (!haSourceId) return;
    try {
        const resp = await fetchWithAuth(`/home-assistant/sources/${haSourceId}/entities`);
        if (!resp.ok) return;
        const data = await resp.json();
        setHAEntityNames(haSourceId, data.entities || []);
    } catch {
        // Leave any existing cache entry intact.
    }
}

/** Prefetch entities for the given HA source IDs in parallel,
 *  skipping any already cached this session. */
export async function prefetchHAEntities(haSourceIds: ReadonlyArray<string>): Promise<void> {
    const unique = [...new Set(haSourceIds.filter(id => id && !_haEntityNamesCache[id]))];
    if (unique.length === 0) return;
    await Promise.all(unique.map(id => fetchHAEntities(id)));
}

// ── Show / Close ──

export async function showHASourceModal(editData: HomeAssistantSource | null = null): Promise<void> {
    const isEdit = !!editData;
    const titleKey = isEdit ? 'ha_source.edit' : 'ha_source.add';
    document.getElementById('ha-source-modal-title')!.innerHTML = `${ICON_HA} ${t(titleKey)}`;
    (document.getElementById('ha-source-id') as HTMLInputElement).value = editData?.id || '';
    (document.getElementById('ha-source-error') as HTMLElement).style.display = 'none';

    if (isEdit) {
        (document.getElementById('ha-source-name') as HTMLInputElement).value = editData.name || '';
        (document.getElementById('ha-source-host') as HTMLInputElement).value = editData.host || '';
        (document.getElementById('ha-source-token') as HTMLInputElement).value = '';  // never expose token
        (document.getElementById('ha-source-ssl') as HTMLInputElement).checked = editData.use_ssl ?? false;
        (document.getElementById('ha-source-filters') as HTMLInputElement).value = (editData.entity_filters || []).join(', ');
        (document.getElementById('ha-source-description') as HTMLInputElement).value = editData.description || '';
    } else {
        (document.getElementById('ha-source-name') as HTMLInputElement).value = '';
        (document.getElementById('ha-source-host') as HTMLInputElement).value = '';
        (document.getElementById('ha-source-token') as HTMLInputElement).value = '';
        (document.getElementById('ha-source-ssl') as HTMLInputElement).checked = false;
        (document.getElementById('ha-source-filters') as HTMLInputElement).value = '';
        (document.getElementById('ha-source-description') as HTMLInputElement).value = '';
    }

    // Tags
    if (_haTagsInput) { _haTagsInput.destroy(); _haTagsInput = null; }
    _haTagsInput = new TagInput(document.getElementById('ha-source-tags-container'), { placeholder: t('tags.placeholder') });
    _haTagsInput.setValue(isEdit ? (editData.tags || []) : []);

    // Show/hide test button based on edit mode
    const testBtn = document.getElementById('ha-source-test-btn');
    if (testBtn) testBtn.style.display = isEdit ? '' : 'none';

    // Token hint
    const tokenHint = document.getElementById('ha-source-token-hint');
    if (tokenHint) tokenHint.style.display = isEdit ? '' : 'none';

    haSourceModal.open();
    haSourceModal.snapshot();
}

export async function closeHASourceModal(): Promise<void> {
    await haSourceModal.close();
}

// ── Save ──

export async function saveHASource(): Promise<void> {
    const id = (document.getElementById('ha-source-id') as HTMLInputElement).value;
    if (haSourceModal.closeIfPristine(id)) return;

    const name = (document.getElementById('ha-source-name') as HTMLInputElement).value.trim();
    const host = (document.getElementById('ha-source-host') as HTMLInputElement).value.trim();
    const token = (document.getElementById('ha-source-token') as HTMLInputElement).value.trim();
    const use_ssl = (document.getElementById('ha-source-ssl') as HTMLInputElement).checked;
    const filtersRaw = (document.getElementById('ha-source-filters') as HTMLInputElement).value.trim();
    const entity_filters = filtersRaw ? filtersRaw.split(',').map(s => s.trim()).filter(Boolean) : [];
    const description = (document.getElementById('ha-source-description') as HTMLInputElement).value.trim() || null;

    if (!name) {
        haSourceModal.showError(t('ha_source.error.name_required'));
        return;
    }
    if (!id && !host) {
        haSourceModal.showError(t('ha_source.error.host_required'));
        return;
    }
    if (!id && !token) {
        haSourceModal.showError(t('ha_source.error.token_required'));
        return;
    }

    const payload: Record<string, any> = {
        name, use_ssl, entity_filters, description,
        tags: _haTagsInput ? _haTagsInput.getValue() : [],
    };
    // Only send host/token if provided (edit mode may leave token blank)
    if (host) payload.host = host;
    if (token) payload.token = token;

    try {
        const method = id ? 'PUT' : 'POST';
        const url = id ? `/home-assistant/sources/${id}` : '/home-assistant/sources';
        const resp = await fetchWithAuth(url, {
            method,
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload),
        });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        showToast(t(id ? 'ha_source.updated' : 'ha_source.created'), 'success');
        haSourceModal.forceClose();
        haSourcesCache.invalidate();
        if (typeof window.loadIntegrations === 'function') await window.loadIntegrations();
    } catch (e: any) {
        if (e.isAuth) return;
        haSourceModal.showError(e.message);
    }
}

// ── Edit / Clone / Delete ──

export async function editHASource(sourceId: string): Promise<void> {
    try {
        const resp = await fetchWithAuth(`/home-assistant/sources/${sourceId}`);
        if (!resp.ok) throw new Error(t('ha_source.error.load'));
        const data = await resp.json();
        await showHASourceModal(data);
    } catch (e: any) {
        if (e.isAuth) return;
        showToast(e.message, 'error');
    }
}

export async function cloneHASource(sourceId: string): Promise<void> {
    try {
        const resp = await fetchWithAuth(`/home-assistant/sources/${sourceId}`);
        if (!resp.ok) throw new Error(t('ha_source.error.load'));
        const data = await resp.json();
        delete data.id;
        data.name = data.name + ' (copy)';
        await showHASourceModal(data);
    } catch (e: any) {
        if (e.isAuth) return;
        showToast(e.message, 'error');
    }
}

export async function deleteHASource(sourceId: string): Promise<void> {
    const confirmed = await showConfirm(t('ha_source.delete.confirm'));
    if (!confirmed) return;
    try {
        const resp = await fetchWithAuth(`/home-assistant/sources/${sourceId}`, { method: 'DELETE' });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        showToast(t('ha_source.deleted'), 'success');
        haSourcesCache.invalidate();
        if (typeof window.loadIntegrations === 'function') await window.loadIntegrations();
    } catch (e: any) {
        if (e.isAuth) return;
        showToast(e.message, 'error');
    }
}

// ── Test ──

export async function testHASource(): Promise<void> {
    const id = (document.getElementById('ha-source-id') as HTMLInputElement).value;
    if (!id) return;

    const testBtn = document.getElementById('ha-source-test-btn');
    if (testBtn) testBtn.classList.add('loading');

    try {
        const resp = await fetchWithAuth(`/home-assistant/sources/${id}/test`, { method: 'POST' });
        if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
        const data = await resp.json();
        if (data.success) {
            showToast(`${t('ha_source.test.success')} | HA ${data.ha_version} | ${data.entity_count} entities`, 'success');
        } else {
            showToast(`${t('ha_source.test.failed')}: ${data.error}`, 'error');
        }
    } catch (e: any) {
        if (e.isAuth) return;
        showToast(e.message, 'error');
    } finally {
        if (testBtn) testBtn.classList.remove('loading');
    }
}

// ── Card rendering ──

export function createHASourceCard(source: HomeAssistantSource) {
    const isConnected = !!source.connected;
    const leds: LedState[] = isConnected ? ['on', 'on'] : ['fault'];
    const healthTitle = isConnected
        ? `${t('ha_source.connected')} — ${source.entity_count} entities`
        : t('ha_source.disconnected');

    const chips: ModChipOpts[] = [
        { icon: `<svg class="icon" viewBox="0 0 24 24">${P.wifi}</svg>`, text: source.host, title: source.host },
    ];
    if (isConnected) {
        chips.push({ icon: `<svg class="icon" viewBox="0 0 24 24">${P.listChecks}</svg>`, text: `${source.entity_count} entities` });
    }
    if (source.use_ssl) {
        chips.push({ icon: `<svg class="icon" viewBox="0 0 24 24">${P.lock}</svg>`, text: 'SSL' });
    }

    const mod: ModCardOpts = {
        head: {
            badge: { text: 'HA · BRIDGE' },
            name: source.name,
            metaHtml: escapeHtml(`${source.host}${isConnected ? ` · ${source.entity_count} entities` : ''}`),
            leds,
            ...makeCardIconFields('ha_source', source.id, source),
            menu: {
                duplicateOnclick: `cloneHASource('${source.id}')`,
                hideOnclick: `toggleCardHidden('ha-sources','${source.id}')`,
                deleteOnclick: `deleteHASource('${source.id}')`,
            },
        },
        body: {
            desc: source.description || undefined,
            chips,
        },
        foot: {
            patchState: isConnected ? 'live' : 'offline',
            patchLabel: isConnected ? 'CONNECTED' : 'OFFLINE',
            iconActions: [
                { icon: ICON_REFRESH, onclick: '', title: healthTitle, dataAttrs: { 'data-action': 'test' } },
                { icon: ICON_EDIT, onclick: '', title: t('common.edit'), dataAttrs: { 'data-action': 'edit' } },
            ],
        },
        running: isConnected,
    };
    const cardHtml = wrapCard({ type: 'template-card', dataAttr: 'data-id', id: source.id, mod });
    const tagsHtml = renderTagChips(source.tags);
    return tagsHtml ? cardHtml.replace(/<\/div>\s*$/, `${tagsHtml}</div>`) : cardHtml;
}

// ── Event delegation ──

const _haSourceActions: Record<string, (id: string) => void> = {
    test: (id) => _testHASourceFromCard(id),
    clone: cloneHASource,
    edit: editHASource,
};

async function _testHASourceFromCard(sourceId: string): Promise<void> {
    try {
        const resp = await fetchWithAuth(`/home-assistant/sources/${sourceId}/test`, { method: 'POST' });
        if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
        const data = await resp.json();
        if (data.success) {
            showToast(`HA ${data.ha_version} | ${data.entity_count} entities`, 'success');
        } else {
            showToast(`${t('ha_source.test.failed')}: ${data.error}`, 'error');
        }
    } catch (e: any) {
        if (e.isAuth) return;
        showToast(e.message, 'error');
    }
}

export function initHASourceDelegation(container: HTMLElement): void {
    container.addEventListener('click', (e: MouseEvent) => {
        const btn = (e.target as HTMLElement).closest<HTMLElement>('[data-action]');
        if (!btn) return;

        const section = btn.closest<HTMLElement>('[data-card-section="ha-sources"]');
        if (!section) return;
        const card = btn.closest<HTMLElement>('[data-id]');
        if (!card) return;

        const action = btn.dataset.action;
        const id = card.getAttribute('data-id');
        if (!action || !id) return;

        const handler = _haSourceActions[action];
        if (handler) {
            e.stopPropagation();
            handler(id);
        }
    });
}

// ── Expose to global scope for HTML template onclick handlers ──

window.showHASourceModal = showHASourceModal;
window.closeHASourceModal = closeHASourceModal;
window.saveHASource = saveHASource;
window.editHASource = editHASource;
window.cloneHASource = cloneHASource;
window.deleteHASource = deleteHASource;
window.testHASource = testHASource;
