/**
 * Settings — tabbed modal (General / Backup / MQTT) + full-screen Log overlay.
 */

import { apiKey } from '../core/state.ts';
import { API_BASE, fetchWithAuth } from '../core/api.ts';
import { Modal } from '../core/modal.ts';
import { showToast, showConfirm } from '../core/ui.ts';
import { t } from '../core/i18n.ts';
import { ICON_UNDO, ICON_DOWNLOAD } from '../core/icons.ts';
import { IconSelect } from '../core/icon-select.ts';

// ─── External URL (used by other modules for user-visible URLs) ──

let _externalUrl = '';

/** Get the configured external base URL (empty string = not set). */
export function getExternalUrl(): string {
    return _externalUrl;
}

/**
 * Return the base origin for user-visible URLs (webhook, WS).
 * If an external URL is configured, use that; otherwise fall back to window.location.origin.
 */
export function getBaseOrigin(): string {
    return _externalUrl || window.location.origin;
}

export async function loadExternalUrl(): Promise<void> {
    try {
        const resp = await fetchWithAuth('/system/external-url');
        if (!resp.ok) return;
        const data = await resp.json();
        _externalUrl = data.external_url || '';
        const input = document.getElementById('settings-external-url') as HTMLInputElement | null;
        if (input) input.value = _externalUrl;
    } catch (err) {
        console.error('Failed to load external URL:', err);
    }
}

export async function saveExternalUrl(): Promise<void> {
    const input = document.getElementById('settings-external-url') as HTMLInputElement | null;
    if (!input) return;
    const url = input.value.trim().replace(/\/+$/, '');
    try {
        const resp = await fetchWithAuth('/system/external-url', {
            method: 'PUT',
            body: JSON.stringify({ external_url: url }),
        });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        const data = await resp.json();
        _externalUrl = data.external_url || '';
        input.value = _externalUrl;
        showToast(t('settings.external_url.saved'), 'success');
    } catch (err) {
        console.error('Failed to save external URL:', err);
        showToast(t('settings.external_url.save_error') + ': ' + err.message, 'error');
    }
}

// ─── Settings-modal tab switching ───────────────────────────

export function switchSettingsTab(tabId: string): void {
    document.querySelectorAll('.settings-tab-btn').forEach(btn => {
        btn.classList.toggle('active', (btn as HTMLElement).dataset.settingsTab === tabId);
    });
    document.querySelectorAll('.settings-panel').forEach(panel => {
        panel.classList.toggle('active', panel.id === `settings-panel-${tabId}`);
    });
    // Lazy-render the appearance tab content
    if (tabId === 'appearance' && typeof window.renderAppearanceTab === 'function') {
        window.renderAppearanceTab();
    }
    // Lazy-load update settings
    if (tabId === 'updates' && typeof (window as any).loadUpdateSettings === 'function') {
        (window as any).initUpdateSettingsPanel();
        (window as any).loadUpdateSettings();
    }
    // Lazy-render the about panel content
    if (tabId === 'about' && typeof (window as any).renderAboutPanel === 'function') {
        (window as any).renderAboutPanel();
    }
}

// ─── Log Viewer ────────────────────────────────────────────

/** @type {WebSocket|null} */
let _logWs: WebSocket | null = null;

/** Level ordering for filter comparisons */
const _LOG_LEVELS = { DEBUG: 10, INFO: 20, WARNING: 30, ERROR: 40, CRITICAL: 50 };

function _detectLevel(line: string): string {
    for (const lvl of ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG']) {
        if (line.includes(lvl)) return lvl;
    }
    return 'DEBUG';
}

function _levelClass(level: string): string {
    if (level === 'ERROR' || level === 'CRITICAL') return 'log-line-error';
    if (level === 'WARNING') return 'log-line-warning';
    if (level === 'DEBUG') return 'log-line-debug';
    return '';
}

function _filterLevel(): string {
    const sel = document.getElementById('log-viewer-filter') as HTMLSelectElement | null;
    return sel ? sel.value : 'all';
}

function _linePassesFilter(line: string): boolean {
    const filter = _filterLevel();
    if (filter === 'all') return true;
    const lineLvl = _detectLevel(line);
    return (_LOG_LEVELS[lineLvl] ?? 10) >= (_LOG_LEVELS[filter] ?? 0);
}

function _appendLine(line: string): void {
    // Skip keepalive empty pings
    if (!line) return;
    if (!_linePassesFilter(line)) return;

    const output = document.getElementById('log-viewer-output');
    if (!output) return;

    const level = _detectLevel(line);
    const cls = _levelClass(level);

    const span = document.createElement('span');
    if (cls) span.className = cls;
    span.textContent = line + '\n';
    output.appendChild(span);

    // Auto-scroll to bottom
    output.scrollTop = output.scrollHeight;
}

export function connectLogViewer(): void {
    const btn = document.getElementById('log-viewer-connect-btn');

    if (_logWs && (_logWs.readyState === WebSocket.OPEN || _logWs.readyState === WebSocket.CONNECTING)) {
        // Disconnect
        _logWs.close();
        _logWs = null;
        if (btn) { btn.textContent = t('settings.logs.connect'); btn.dataset.i18n = 'settings.logs.connect'; }
        return;
    }

    const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
    const url = `${proto}//${location.host}/api/v1/system/logs/ws?token=${encodeURIComponent(apiKey ?? '')}`;

    _logWs = new WebSocket(url);

    _logWs.onopen = () => {
        if (btn) { btn.textContent = t('settings.logs.disconnect'); btn.dataset.i18n = 'settings.logs.disconnect'; }
    };

    _logWs.onmessage = (evt) => {
        _appendLine(evt.data);
    };

    _logWs.onerror = () => {
        showToast(t('settings.logs.error'), 'error');
    };

    _logWs.onclose = () => {
        _logWs = null;
        if (btn) { btn.textContent = t('settings.logs.connect'); btn.dataset.i18n = 'settings.logs.connect'; }
    };
}

export function disconnectLogViewer(): void {
    if (_logWs) {
        _logWs.close();
        _logWs = null;
    }
    const btn = document.getElementById('log-viewer-connect-btn');
    if (btn) { btn.textContent = t('settings.logs.connect'); btn.dataset.i18n = 'settings.logs.connect'; }
}

export function clearLogViewer(): void {
    const output = document.getElementById('log-viewer-output');
    if (output) output.innerHTML = '';
}

/** Re-render the log output according to the current filter selection. */
export function applyLogFilter(): void {
    const output = document.getElementById('log-viewer-output');
    if (!output) return;
    const filter = _filterLevel();
    for (const span of Array.from(output.children)) {
        const line = span.textContent;
        const lineLvl = _detectLevel(line);
        const passes = filter === 'all' || (_LOG_LEVELS[lineLvl] ?? 10) >= (_LOG_LEVELS[filter] ?? 0);
        (span as HTMLElement).style.display = passes ? '' : 'none';
    }
}

// ─── Log Overlay (full-screen) ──────────────────────────────

let _logFilterIconSelect: IconSelect | null = null;

/** Build filter items lazily so t() has locale data loaded. */
function _getLogFilterItems(): { value: string; icon: string; label: string; desc: string }[] {
    return [
        { value: 'all',     icon: '<span style="color:#9e9e9e;font-weight:700">*</span>', label: t('settings.logs.filter.all'),     desc: t('settings.logs.filter.all_desc') },
        { value: 'INFO',    icon: '<span style="color:#4fc3f7;font-weight:700">I</span>', label: t('settings.logs.filter.info'),    desc: t('settings.logs.filter.info_desc') },
        { value: 'WARNING', icon: '<span style="color:#ffb74d;font-weight:700">W</span>', label: t('settings.logs.filter.warning'), desc: t('settings.logs.filter.warning_desc') },
        { value: 'ERROR',   icon: '<span style="color:#ef5350;font-weight:700">E</span>', label: t('settings.logs.filter.error'),   desc: t('settings.logs.filter.error_desc') },
    ];
}

export function openLogOverlay(): void {
    const overlay = document.getElementById('log-overlay');
    if (overlay) {
        overlay.style.display = 'flex';

        // Initialize log filter icon select (once)
        if (!_logFilterIconSelect) {
            const filterSel = document.getElementById('log-viewer-filter') as HTMLSelectElement | null;
            if (filterSel) {
                _logFilterIconSelect = new IconSelect({
                    target: filterSel,
                    items: _getLogFilterItems(),
                    columns: 2,
                    onChange: () => applyLogFilter(),
                });
            }
        }

        // Auto-connect when opening
        if (!_logWs || _logWs.readyState !== WebSocket.OPEN) {
            connectLogViewer();
        }
    }
}

export function closeLogOverlay(): void {
    const overlay = document.getElementById('log-overlay');
    if (overlay) overlay.style.display = 'none';
    disconnectLogViewer();
}

// ─── Settings Modal ─────────────────────────────────────────

// Simple modal (no form / no dirty check needed)
const settingsModal = new Modal('settings-modal');

let _logLevelIconSelect: IconSelect | null = null;

/** Build log-level items lazily so t() has locale data loaded. */
function _getLogLevelItems(): { value: string; icon: string; label: string; desc: string }[] {
    return [
        { value: 'DEBUG',    icon: '<span style="color:#9e9e9e;font-weight:700">D</span>', label: 'DEBUG',    desc: t('settings.log_level.desc.debug') },
        { value: 'INFO',     icon: '<span style="color:#4fc3f7;font-weight:700">I</span>', label: 'INFO',     desc: t('settings.log_level.desc.info') },
        { value: 'WARNING',  icon: '<span style="color:#ffb74d;font-weight:700">W</span>', label: 'WARNING',  desc: t('settings.log_level.desc.warning') },
        { value: 'ERROR',    icon: '<span style="color:#ef5350;font-weight:700">E</span>', label: 'ERROR',    desc: t('settings.log_level.desc.error') },
        { value: 'CRITICAL', icon: '<span style="color:#ff1744;font-weight:700">!</span>', label: 'CRITICAL', desc: t('settings.log_level.desc.critical') },
    ];
}

export function openSettingsModal(): void {
    (document.getElementById('settings-error') as HTMLElement).style.display = 'none';

    // Reset to first tab
    switchSettingsTab('general');

    settingsModal.open();

    // Initialize log level icon select
    if (!_logLevelIconSelect) {
        const levelSel = document.getElementById('settings-log-level') as HTMLSelectElement | null;
        if (levelSel) {
            _logLevelIconSelect = new IconSelect({
                target: levelSel,
                items: _getLogLevelItems(),
                columns: 3,
                onChange: () => setLogLevel(),
            });
        }
    }

    loadApiKeysList();
    loadExternalUrl();
    loadAutoBackupSettings();
    loadBackupList();
    loadMqttSettings();
    loadLogLevel();
}

export function closeSettingsModal(): void {
    settingsModal.forceClose();
}

// ─── Backup ────────────────────────────────────────────────

export async function downloadBackup(): Promise<void> {
    try {
        const resp = await fetchWithAuth('/system/backup', { timeout: 30000 });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        const blob = await resp.blob();
        const disposition = resp.headers.get('Content-Disposition') || '';
        const match = disposition.match(/filename="(.+?)"/);
        const filename = match ? match[1] : 'ledgrab-backup.json';

        const a = document.createElement('a');
        a.href = URL.createObjectURL(blob);
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        a.remove();
        URL.revokeObjectURL(a.href);

        showToast(t('settings.backup.success'), 'success');
    } catch (err) {
        console.error('Backup download failed:', err);
        showToast(t('settings.backup.error') + ': ' + err.message, 'error');
    }
}

// ─── Restore ───────────────────────────────────────────────

export async function handleRestoreFileSelected(input: HTMLInputElement): Promise<void> {
    const file = input.files![0];
    input.value = '';
    if (!file) return;

    const confirmed = await showConfirm(t('settings.restore.confirm'));
    if (!confirmed) return;

    try {
        const formData = new FormData();
        formData.append('file', file);

        const resp = await fetch(`${API_BASE}/system/restore`, {
            method: 'POST',
            headers: { 'Authorization': `Bearer ${apiKey}` },
            body: formData,
        });

        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }

        const data = await resp.json();
        showToast(data.message || t('settings.restore.success'), 'success');
        settingsModal.forceClose();

    } catch (err) {
        console.error('Restore failed:', err);
        showToast(t('settings.restore.error') + ': ' + err.message, 'error');
    }
}

// ─── Server restart ────────────────────────────────────────

export async function restartServer(): Promise<void> {
    const confirmed = await showConfirm(t('settings.restart_confirm'));
    if (!confirmed) return;

    try {
        const resp = await fetchWithAuth('/system/restart', { method: 'POST' });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        settingsModal.forceClose();
    } catch (err) {
        console.error('Server restart failed:', err);
        showToast(t('settings.restore.error') + ': ' + err.message, 'error');
    }
}

// ─── Auto-Backup settings ─────────────────────────────────

export async function loadAutoBackupSettings(): Promise<void> {
    try {
        const resp = await fetchWithAuth('/system/auto-backup/settings');
        if (!resp.ok) return;
        const data = await resp.json();

        (document.getElementById('auto-backup-enabled') as HTMLInputElement).checked = data.enabled;
        (document.getElementById('auto-backup-interval') as HTMLInputElement).value = String(data.interval_hours);
        (document.getElementById('auto-backup-max') as HTMLInputElement).value = data.max_backups;

        const statusEl = document.getElementById('auto-backup-status')!;
        if (data.last_backup_time) {
            const d = new Date(data.last_backup_time);
            statusEl.textContent = t('settings.auto_backup.last_backup') + ': ' + d.toLocaleString();
        } else {
            statusEl.textContent = t('settings.auto_backup.last_backup') + ': ' + t('settings.auto_backup.never');
        }
    } catch (err) {
        console.error('Failed to load auto-backup settings:', err);
    }
}

export async function saveAutoBackupSettings(): Promise<void> {
    const enabled = (document.getElementById('auto-backup-enabled') as HTMLInputElement).checked;
    const interval_hours = parseFloat((document.getElementById('auto-backup-interval') as HTMLInputElement).value);
    const max_backups = parseInt((document.getElementById('auto-backup-max') as HTMLInputElement).value, 10);

    try {
        const resp = await fetchWithAuth('/system/auto-backup/settings', {
            method: 'PUT',
            body: JSON.stringify({ enabled, interval_hours, max_backups }),
        });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        showToast(t('settings.auto_backup.saved'), 'success');
        loadAutoBackupSettings();
        loadBackupList();
    } catch (err) {
        console.error('Failed to save auto-backup settings:', err);
        showToast(t('settings.auto_backup.save_error') + ': ' + err.message, 'error');
    }
}

export async function triggerBackupNow(): Promise<void> {
    try {
        const resp = await fetchWithAuth('/system/auto-backup/trigger', { method: 'POST' });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        showToast(t('settings.auto_backup.backup_created'), 'success');
        loadBackupList();
        loadAutoBackupSettings();
    } catch (err) {
        console.error('Backup failed:', err);
        showToast(t('settings.auto_backup.backup_error') + ': ' + err.message, 'error');
    }
}

// ─── Saved backup list ────────────────────────────────────

export async function loadBackupList(): Promise<void> {
    const container = document.getElementById('saved-backups-list')!;
    try {
        const resp = await fetchWithAuth('/system/backups');
        if (!resp.ok) return;
        const data = await resp.json();

        if (data.count === 0) {
            container.innerHTML = `<div style="color:var(--text-muted);font-size:0.85rem;">${t('settings.saved_backups.empty')}</div>`;
            return;
        }

        container.innerHTML = data.backups.map(b => {
            const sizeBytes = b.size_bytes || 0;
            const sizeStr = sizeBytes >= 1024 * 1024
                ? (sizeBytes / (1024 * 1024)).toFixed(1) + ' MB'
                : (sizeBytes / 1024).toFixed(1) + ' KB';
            const date = new Date(b.created_at).toLocaleString();
            const isAuto = b.filename.startsWith('ledgrab-autobackup-');
            const typeBadge = isAuto
                ? `<span style="display:inline-block;padding:1px 5px;border-radius:3px;font-size:0.7rem;background:var(--border-color);color:var(--text-muted);white-space:nowrap;">${t('settings.saved_backups.type.auto')}</span>`
                : `<span style="display:inline-block;padding:1px 5px;border-radius:3px;font-size:0.7rem;background:var(--primary-color);color:#fff;white-space:nowrap;">${t('settings.saved_backups.type.manual')}</span>`;
            return `<div style="display:flex;align-items:center;gap:0.5rem;padding:0.3rem 0;border-bottom:1px solid var(--border-color);font-size:0.82rem;">
                ${typeBadge}
                <div style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${b.filename}">
                    <span>${date}</span>
                    <span style="color:var(--text-muted);margin-left:0.3rem;">${sizeStr}</span>
                </div>
                <button class="btn btn-icon btn-secondary" onclick="restoreSavedBackup('${b.filename}')" title="${t('settings.saved_backups.restore')}" style="padding:2px 6px;font-size:0.8rem;">${ICON_UNDO}</button>
                <button class="btn btn-icon btn-secondary" onclick="downloadSavedBackup('${b.filename}')" title="${t('settings.saved_backups.download')}" style="padding:2px 6px;font-size:0.8rem;">${ICON_DOWNLOAD}</button>
                <button class="btn btn-icon btn-secondary" onclick="deleteSavedBackup('${b.filename}')" title="${t('settings.saved_backups.delete')}" style="padding:2px 6px;font-size:0.8rem;">&#x2715;</button>
            </div>`;
        }).join('');
    } catch (err) {
        console.error('Failed to load backup list:', err);
        container.innerHTML = '';
    }
}

export async function downloadSavedBackup(filename: string): Promise<void> {
    try {
        const resp = await fetchWithAuth(`/system/backups/${encodeURIComponent(filename)}`, { timeout: 30000 });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        const blob = await resp.blob();
        const a = document.createElement('a');
        a.href = URL.createObjectURL(blob);
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        a.remove();
        URL.revokeObjectURL(a.href);
    } catch (err) {
        console.error('Backup download failed:', err);
        showToast(t('settings.backup.error') + ': ' + err.message, 'error');
    }
}

export async function restoreSavedBackup(filename: string): Promise<void> {
    const confirmed = await showConfirm(t('settings.restore.confirm'));
    if (!confirmed) return;

    try {
        // Download the backup file from the server
        const dlResp = await fetchWithAuth(`/system/backups/${encodeURIComponent(filename)}`, { timeout: 30000 });
        if (!dlResp.ok) {
            const err = await dlResp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${dlResp.status}`);
        }
        const blob = await dlResp.blob();

        // POST it to the restore endpoint
        const formData = new FormData();
        formData.append('file', blob, filename);

        const resp = await fetch(`${API_BASE}/system/restore`, {
            method: 'POST',
            headers: { 'Authorization': `Bearer ${apiKey}` },
            body: formData,
        });

        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }

        const data = await resp.json();
        showToast(data.message || t('settings.restore.success'), 'success');
        settingsModal.forceClose();

    } catch (err) {
        console.error('Restore from saved backup failed:', err);
        showToast(t('settings.restore.error') + ': ' + err.message, 'error');
    }
}

export async function deleteSavedBackup(filename: string): Promise<void> {
    const confirmed = await showConfirm(t('settings.saved_backups.delete_confirm'));
    if (!confirmed) return;

    try {
        const resp = await fetchWithAuth(`/system/backups/${encodeURIComponent(filename)}`, {
            method: 'DELETE',
        });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        loadBackupList();
    } catch (err) {
        console.error('Backup delete failed:', err);
        showToast(t('settings.saved_backups.delete_error') + ': ' + err.message, 'error');
    }
}

// ─── API Keys (read-only display) ─────────────────────────────

export async function loadApiKeysList(): Promise<void> {
    const container = document.getElementById('settings-api-keys-list');
    if (!container) return;
    try {
        const resp = await fetchWithAuth('/system/api-keys');
        if (!resp.ok) {
            container.innerHTML = `<div style="color:var(--text-muted);">${t('settings.api_keys.load_error')}</div>`;
            return;
        }
        const data = await resp.json();
        if (data.count === 0) {
            container.innerHTML = `<div style="color:var(--text-muted);">${t('settings.api_keys.empty')}</div>`;
            return;
        }
        container.innerHTML = data.keys.map(k =>
            `<div style="display:flex;align-items:center;gap:0.5rem;padding:0.25rem 0;border-bottom:1px solid var(--border-color);">
                <span style="font-weight:600;min-width:80px;">${k.label}</span>
                <code style="flex:1;color:var(--text-muted);font-size:0.8rem;">${k.masked}</code>
            </div>`
        ).join('');
    } catch (err) {
        console.error('Failed to load API keys:', err);
        if (container) container.innerHTML = '';
    }
}

// ─── Log Level ────────────────────────────────────────────────

export async function loadLogLevel(): Promise<void> {
    try {
        const resp = await fetchWithAuth('/system/log-level');
        if (!resp.ok) return;
        const data = await resp.json();
        if (_logLevelIconSelect) {
            _logLevelIconSelect.setValue(data.level);
        } else {
            const select = document.getElementById('settings-log-level') as HTMLSelectElement | null;
            if (select) select.value = data.level;
        }
    } catch (err) {
        console.error('Failed to load log level:', err);
    }
}

export async function setLogLevel(): Promise<void> {
    const select = document.getElementById('settings-log-level') as HTMLSelectElement | null;
    if (!select) return;
    const level = select.value;
    try {
        const resp = await fetchWithAuth('/system/log-level', {
            method: 'PUT',
            body: JSON.stringify({ level }),
        });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        showToast(t('settings.log_level.saved'), 'success');
    } catch (err) {
        console.error('Failed to set log level:', err);
        showToast(t('settings.log_level.save_error') + ': ' + err.message, 'error');
    }
}

// ─── MQTT settings ────────────────────────────────────────────

export async function loadMqttSettings(): Promise<void> {
    try {
        const resp = await fetchWithAuth('/system/mqtt/settings');
        if (!resp.ok) return;
        const data = await resp.json();

        (document.getElementById('mqtt-enabled') as HTMLInputElement).checked = data.enabled;
        (document.getElementById('mqtt-host') as HTMLInputElement).value = data.broker_host;
        (document.getElementById('mqtt-port') as HTMLInputElement).value = data.broker_port;
        (document.getElementById('mqtt-username') as HTMLInputElement).value = data.username;
        (document.getElementById('mqtt-password') as HTMLInputElement).value = '';
        (document.getElementById('mqtt-client-id') as HTMLInputElement).value = data.client_id;
        (document.getElementById('mqtt-base-topic') as HTMLInputElement).value = data.base_topic;

        const hint = document.getElementById('mqtt-password-hint');
        if (hint) hint.style.display = data.password_set ? '' : 'none';
    } catch (err) {
        console.error('Failed to load MQTT settings:', err);
    }
}

export async function saveMqttSettings(): Promise<void> {
    const enabled = (document.getElementById('mqtt-enabled') as HTMLInputElement).checked;
    const broker_host = (document.getElementById('mqtt-host') as HTMLInputElement).value.trim();
    const broker_port = parseInt((document.getElementById('mqtt-port') as HTMLInputElement).value, 10);
    const username = (document.getElementById('mqtt-username') as HTMLInputElement).value;
    const password = (document.getElementById('mqtt-password') as HTMLInputElement).value;
    const client_id = (document.getElementById('mqtt-client-id') as HTMLInputElement).value.trim();
    const base_topic = (document.getElementById('mqtt-base-topic') as HTMLInputElement).value.trim();

    if (!broker_host) {
        showToast(t('settings.mqtt.error_host_required'), 'error');
        return;
    }

    try {
        const resp = await fetchWithAuth('/system/mqtt/settings', {
            method: 'PUT',
            body: JSON.stringify({ enabled, broker_host, broker_port, username, password, client_id, base_topic }),
        });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        showToast(t('settings.mqtt.saved'), 'success');
        loadMqttSettings();
    } catch (err) {
        console.error('Failed to save MQTT settings:', err);
        showToast(t('settings.mqtt.save_error') + ': ' + err.message, 'error');
    }
}
