// ============================================================
// Display Brightness, Power, Contrast, Input Source, Color Preset,
// Picture Mode Control + Links Management
// ============================================================

import { t, showToast, escapeHtml, closeDialog, showConfirm, resolveMdiIcons, fetchMdiIcon, getAuthHeaders, hasCredentials } from './core.js';
import { IconSelect } from './icon-select.js';

let displayBrightnessTimers = {};
let displayContrastTimers = {};
let _displayIconSelects = [];
const DISPLAY_THROTTLE_MS = 50;

// ─── Icon palette for the tuning IconSelects ───────────────────────────
// All SVGs are 24x24 monochrome — IconSelect's CSS fills them with currentColor.

const ICON_PORT_GENERIC =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M4 8h16v8H4V8zm2 2v4h12v-4H6zm2 1h2v2H8v-2zm4 0h2v2h-2v-2zm-9 6h18v2H3v-2z"/></svg>';
const ICON_PORT_HDMI =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M3 9l2-2h14l2 2v5l-2 2h-3l-1 1H9l-1-1H5l-2-2V9zm2.5.5v4l1 1h2l1 1h7l1-1h2l1-1v-4l-1-.5H6.5l-1 .5z"/></svg>';
const ICON_PORT_DP =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M4 8l2-2h12l2 2v8l-2 2H6l-2-2V8zm2 .5V15l1 1h10l1-1V8.5L17 8H7l-1 .5zM8 10h2v4H8v-4zm6 0h2v4h-2v-4z"/></svg>';
const ICON_PORT_DVI =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M3 8h18v8H3V8zm2 1.5v5h14v-5H5zM7 11h1.5v2H7v-2zm3 0h1.5v2H10v-2zm3 0h1.5v2H13v-2zm3 0h1.5v2H16v-2z"/></svg>';
const ICON_PORT_VGA =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M5 7h14a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2V9a2 2 0 012-2zm0 2v6h14V9H5zm2 1.5a.75.75 0 100 1.5.75.75 0 000-1.5zm3 0a.75.75 0 100 1.5.75.75 0 000-1.5zm3 0a.75.75 0 100 1.5.75.75 0 000-1.5zm3 0a.75.75 0 100 1.5.75.75 0 000-1.5z"/></svg>';
const ICON_PORT_USBC =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M5 10a3 3 0 013-3h8a3 3 0 013 3v4a3 3 0 01-3 3H8a3 3 0 01-3-3v-4zm3-1.5A1.5 1.5 0 006.5 10v4A1.5 1.5 0 008 15.5h8a1.5 1.5 0 001.5-1.5v-4A1.5 1.5 0 0016 8.5H8zm1 2h6v3H9v-3z"/></svg>';

const ICON_THERMOMETER =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 3a3 3 0 00-3 3v8.17A4 4 0 1015 14.17V6a3 3 0 00-3-3zm-1.5 3a1.5 1.5 0 113 0v8.76a2.5 2.5 0 11-3 0V6zm1.5 5a1 1 0 011 1v2.27a1.5 1.5 0 11-2 0V12a1 1 0 011-1z"/></svg>';

const ICON_MODE_MOVIE =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M4 5h16v14H4V5zm2 2v2h2V7H6zm0 4v2h2v-2H6zm0 4v2h2v-2H6zm10-8v2h2V7h-2zm0 4v2h2v-2h-2zm0 4v2h2v-2h-2zm-6-7h4v8h-4V8z"/></svg>';
const ICON_MODE_GAME =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M7 8a5 5 0 00-5 5 4 4 0 007.4 2.1L11 14h2l1.6 1.1A4 4 0 0022 13a5 5 0 00-5-5H7zm1 3v1H7v-1H6v-1h1V9h1v1h1v1H8zm7 0a1 1 0 110-2 1 1 0 010 2zm2 2a1 1 0 110-2 1 1 0 010 2z"/></svg>';
const ICON_MODE_SPORT =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 2a10 10 0 100 20 10 10 0 000-20zm0 2c1.7 0 3.3.5 4.6 1.4l-1.4 2.4L12 6.5l-3.2 1.3-1.4-2.4A8 8 0 0112 4zm-7.6 4l2.5 1.3-.5 3.5L4 16.4A8 8 0 014.4 8zm15.2 0a8 8 0 01.4 8.4l-2.4-1.6-.5-3.5L19.6 8zM12 8.7l3 1.2.6 3.2L13 15h-2l-2.6-1.9.6-3.2L12 8.7zm-5.3 8.8L9 16.5l2.4 1h1.2l2.4-1 2.3 1A8 8 0 016.7 17.5z"/></svg>';
const ICON_MODE_PRO =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M3 4h18v12H13v2h4v2H7v-2h4v-2H3V4zm2 2v8h14V6H5zm2 2h6v2H7V8zm0 3h10v2H7v-2z"/></svg>';
const ICON_MODE_DOCS =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M6 3h9l4 4v14H6V3zm2 2v14h10V9h-4V5H8zm2 6h6v2h-6v-2zm0 3h6v2h-6v-2z"/></svg>';
const ICON_MODE_USER =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 3a4 4 0 100 8 4 4 0 000-8zm0 2a2 2 0 110 4 2 2 0 010-4zm0 8c-3.3 0-7 1.5-7 4.5V20h14v-2.5c0-3-3.7-4.5-7-4.5z"/></svg>';
const ICON_MODE_DEFAULT =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M3 5h18v12H3V5zm2 2v8h14V7H5zm-2 12h18v2H3v-2z"/></svg>';
const ICON_MODE_MIXED =
    '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M3 5h18v14H3V5zm2 2v10h7V7H5zm9 0v10h5V7h-5z"/></svg>';

function inputSourceIcon(src) {
    const s = String(src || '').toUpperCase();
    if (s.startsWith('HDMI'))  return ICON_PORT_HDMI;
    if (s.startsWith('DP'))    return ICON_PORT_DP;
    if (s.startsWith('DVI'))   return ICON_PORT_DVI;
    if (s.startsWith('VGA'))   return ICON_PORT_VGA;
    if (s.startsWith('USB'))   return ICON_PORT_USBC;
    return ICON_PORT_GENERIC;
}

function pictureModeIcon(label) {
    const k = String(label || '').toLowerCase();
    if (k.includes('movie'))         return ICON_MODE_MOVIE;
    if (k.includes('game'))          return ICON_MODE_GAME;
    if (k.includes('sport'))         return ICON_MODE_SPORT;
    if (k.includes('professional'))  return ICON_MODE_PRO;
    if (k.includes('productivity'))  return ICON_MODE_DOCS;
    if (k.includes('user'))          return ICON_MODE_USER;
    if (k.includes('mixed'))         return ICON_MODE_MIXED;
    return ICON_MODE_DEFAULT;
}

// Humanise enum-style identifiers returned by monitorcontrol so users
// don't see SHOUT_CASE strings in the UI.
function humanizeInputSource(raw) {
    if (!raw) return '';
    // OFF / RESERVED → "Off" / "Reserved"
    // VGA1 → "VGA 1", HDMI1 → "HDMI 1", DP1 → "DisplayPort 1"
    const map = { DP: 'DisplayPort', DVI: 'DVI', HDMI: 'HDMI', VGA: 'VGA', USBC: 'USB-C' };
    const m = String(raw).toUpperCase().match(/^(DP|DVI|HDMI|VGA|USBC|USB_C)(\d*)$/);
    if (m) {
        const key = m[1] === 'USB_C' ? 'USBC' : m[1];
        return `${map[key]}${m[2] ? ' ' + m[2] : ''}`;
    }
    return String(raw)
        .replace(/_/g, ' ')
        .toLowerCase()
        .replace(/\b\w/g, c => c.toUpperCase());
}

function humanizeColorPreset(raw) {
    if (!raw) return '';
    // COLOR_TEMP_6500K → "6500 K", COLOR_TEMP_NATIVE → "Native",
    // COLOR_TEMP_USER1 → "User 1"
    const s = String(raw).replace(/^COLOR_TEMP_?/i, '');
    const kelvin = s.match(/^(\d{4,5})K?$/);
    if (kelvin) return `${kelvin[1]} K`;
    const user = s.match(/^USER\s*_?(\d+)$/i);
    if (user) return `User ${user[1]}`;
    return s
        .replace(/_/g, ' ')
        .toLowerCase()
        .replace(/\b\w/g, c => c.toUpperCase());
}

export async function loadDisplayMonitors() {
    if (!hasCredentials()) return;

    const container = document.getElementById('displayMonitors');
    if (!container) return;

    try {
        const response = await fetch('/api/display/monitors', {
            headers: getAuthHeaders()
        });

        if (!response.ok) {
            container.innerHTML = `<div class="empty-state-illustration">
                <svg viewBox="0 0 64 64"><rect x="8" y="10" width="48" height="32" rx="3" fill="none" stroke="currentColor" stroke-width="2"/><line x1="32" y1="42" x2="32" y2="50" stroke="currentColor" stroke-width="2"/><line x1="22" y1="50" x2="42" y2="50" stroke="currentColor" stroke-width="2"/></svg>
                <p data-i18n="display.error">Failed to load monitors</p>
            </div>`;
            return;
        }

        const monitors = await response.json();

        if (monitors.length === 0) {
            container.innerHTML = `<div class="empty-state-illustration">
                <svg viewBox="0 0 64 64"><rect x="8" y="10" width="48" height="32" rx="3" fill="none" stroke="currentColor" stroke-width="2"/><line x1="32" y1="42" x2="32" y2="50" stroke="currentColor" stroke-width="2"/><line x1="22" y1="50" x2="42" y2="50" stroke="currentColor" stroke-width="2"/></svg>
                <p data-i18n="display.no_monitors">No monitors detected</p>
            </div>`;
            return;
        }

        // Destroy IconSelects from a previous render so listeners + popups
        // don't pile up.
        _displayIconSelects.forEach(inst => { try { inst.destroy(); } catch (_) {} });
        _displayIconSelects = [];

        container.innerHTML = '';
        const pendingIconSelects = [];
        monitors.forEach(monitor => {
            const card = document.createElement('div');
            card.className = 'display-monitor-card';
            card.id = `monitor-card-${monitor.id}`;

            const brightnessValue = monitor.brightness !== null ? monitor.brightness : 0;
            const brightnessDisabled = monitor.brightness === null ? 'disabled' : '';

            let powerBtn = '';
            if (monitor.power_supported) {
                // Inline onclick with string-interpolated monitor.name is a DOM-XSS
                // foot-gun if the OS ever reports a name containing quotes / angle
                // brackets. Use a delegated click handler bound to data-* attrs.
                powerBtn = `
                    <button class="display-power-btn ${monitor.power_on ? 'on' : 'off'}" id="power-btn-${monitor.id}"
                            data-action="toggle-power" data-monitor-id="${monitor.id}"
                            title="${escapeHtml(monitor.power_on ? t('display.power_off') : t('display.power_on'))}">
                        <svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42A6.92 6.92 0 0119 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.27 1.08-4.28 2.76-5.56L6.34 5.02A8.95 8.95 0 003 12c0 4.97 4.03 9 9 9s9-4.03 9-9a8.95 8.95 0 00-3.17-6.83z"/></svg>
                    </button>`;
            }

            const details = [monitor.resolution, monitor.manufacturer].filter(Boolean).join(' \u00B7 ');
            const detailsHtml = details ? `<span class="display-monitor-details">${escapeHtml(details)}</span>` : '';
            const primaryBadge = monitor.is_primary
                ? `<span class="display-primary-badge" title="${t('display.primary')}" aria-label="${t('display.primary')}">
                       <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true">
                           <path fill="currentColor" d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
                       </svg>
                   </span>`
                : '';

            // Contrast (DDC/CI) — render only if the monitor reports it.
            let contrastRow = '';
            if (monitor.contrast_supported) {
                const contrastValue = monitor.contrast !== null && monitor.contrast !== undefined
                    ? monitor.contrast : 50;
                contrastRow = `
                <div class="display-slider-row">
                    <svg class="display-slider-icon" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
                        <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18V4c4.41 0 8 3.59 8 8s-3.59 8-8 8z"/>
                    </svg>
                    <span class="display-slider-label" data-i18n="display.contrast">${t('display.contrast')}</span>
                    <input type="range" class="display-slider display-contrast-slider"
                           min="0" max="100" value="${contrastValue}"
                           data-display-slider="contrast" data-monitor-id="${monitor.id}">
                    <span class="display-slider-value" id="contrast-val-${monitor.id}">${contrastValue}%</span>
                </div>`;
            }

            // Build the picture-tuning selects (input source / color preset / picture mode).
            const tuningRows = [];

            // Each tuning field renders a hidden <select> (state holder)
            // which IconSelect then enhances after the card lands in the DOM.
            const tuningTargets = [];

            if (monitor.input_source_supported && monitor.available_input_sources.length > 0) {
                const current = monitor.input_source;
                const options = monitor.available_input_sources.map(src => {
                    const selected = src === current ? 'selected' : '';
                    return `<option value="${escapeHtml(src)}" ${selected}>${escapeHtml(humanizeInputSource(src))}</option>`;
                }).join('');
                tuningRows.push(`
                <div class="display-tuning-field">
                    <span class="display-tuning-label" data-i18n="display.input_source">${t('display.input_source')}</span>
                    <select data-display-select="input" data-monitor-id="${monitor.id}"
                            aria-label="${t('display.input_source')}">
                        ${options}
                    </select>
                </div>`);
                tuningTargets.push({
                    kind: 'input',
                    monitorId: monitor.id,
                    items: monitor.available_input_sources.map(src => ({
                        value: src,
                        icon: inputSourceIcon(src),
                        label: humanizeInputSource(src),
                    })),
                });
            }

            if (monitor.color_preset_supported && monitor.available_color_presets.length > 0) {
                const current = monitor.color_preset;
                const options = monitor.available_color_presets.map(p => {
                    const selected = p === current ? 'selected' : '';
                    return `<option value="${escapeHtml(p)}" ${selected}>${escapeHtml(humanizeColorPreset(p))}</option>`;
                }).join('');
                tuningRows.push(`
                <div class="display-tuning-field">
                    <span class="display-tuning-label" data-i18n="display.color_preset">${t('display.color_preset')}</span>
                    <select data-display-select="color" data-monitor-id="${monitor.id}"
                            aria-label="${t('display.color_preset')}">
                        ${options}
                    </select>
                </div>`);
                tuningTargets.push({
                    kind: 'color',
                    monitorId: monitor.id,
                    items: monitor.available_color_presets.map(p => ({
                        value: p,
                        icon: ICON_THERMOMETER,
                        label: humanizeColorPreset(p),
                    })),
                });
            }

            if (monitor.picture_mode_supported && monitor.available_picture_modes.length > 0) {
                const current = monitor.picture_mode_code;
                const options = monitor.available_picture_modes.map(m => {
                    const selected = m.code === current ? 'selected' : '';
                    return `<option value="${m.code}" ${selected}>${escapeHtml(m.label)}</option>`;
                }).join('');
                tuningRows.push(`
                <div class="display-tuning-field">
                    <span class="display-tuning-label" data-i18n="display.picture_mode">${t('display.picture_mode')}</span>
                    <select data-display-select="mode" data-monitor-id="${monitor.id}"
                            aria-label="${t('display.picture_mode')}">
                        ${options}
                    </select>
                </div>`);
                tuningTargets.push({
                    kind: 'mode',
                    monitorId: monitor.id,
                    items: monitor.available_picture_modes.map(m => ({
                        value: String(m.code),
                        icon: pictureModeIcon(m.label),
                        label: m.label,
                    })),
                });
            }

            pendingIconSelects.push(...tuningTargets);

            const tuningBlock = tuningRows.length > 0
                ? `<div class="display-tuning">
                       <div class="display-tuning-title" data-i18n="display.tuning">${t('display.tuning')}</div>
                       <div class="display-tuning-grid">${tuningRows.join('')}</div>
                   </div>`
                : '';

            card.innerHTML = `
                <div class="display-monitor-header">
                    <svg class="display-monitor-icon" viewBox="0 0 24 24" width="20" height="20">
                        <path fill="currentColor" d="M20 3H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h6v2H8v2h8v-2h-2v-2h6c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12H4V5h16v10z"/>
                    </svg>
                    <div class="display-monitor-info">
                        <span class="display-monitor-name"><span class="display-monitor-name-text">${escapeHtml(monitor.name)}</span>${primaryBadge}</span>
                        ${detailsHtml}
                    </div>
                    ${powerBtn}
                </div>
                <div class="display-slider-row display-brightness-control">
                    <svg class="display-slider-icon display-brightness-icon" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
                        <path fill="currentColor" d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/>
                    </svg>
                    <span class="display-slider-label" data-i18n="display.brightness">${t('display.brightness')}</span>
                    <input type="range" class="display-slider display-brightness-slider" min="0" max="100" value="${brightnessValue}" ${brightnessDisabled}
                           data-display-slider="brightness" data-monitor-id="${monitor.id}">
                    <span class="display-slider-value display-brightness-value" id="brightness-val-${monitor.id}">${brightnessValue}%</span>
                </div>
                ${contrastRow}
                ${tuningBlock}`;

            container.appendChild(card);
        });

        // Bind a single delegated click handler for the power buttons,
        // plus input/change handlers for the brightness & contrast sliders.
        // Avoids inline on* attributes (blocked by script-src 'self' CSP).
        container.removeEventListener('click', _onPowerButtonClick);
        container.addEventListener('click', _onPowerButtonClick);
        container.removeEventListener('input', _onDisplaySliderInput);
        container.addEventListener('input', _onDisplaySliderInput);
        container.removeEventListener('change', _onDisplaySliderChange);
        container.addEventListener('change', _onDisplaySliderChange);

        // Enhance every tuning <select> with an IconSelect now that the
        // cards are in the DOM (IconSelect needs offsetParent + sibling).
        pendingIconSelects.forEach(({ kind, monitorId, items }) => {
            const sel = container.querySelector(
                `select[data-display-select="${kind}"][data-monitor-id="${monitorId}"]`
            );
            if (!sel) return;
            const handler = kind === 'input' ? onDisplayInputSourceChange
                : kind === 'color' ? onDisplayColorPresetChange
                : onDisplayPictureModeChange;
            _displayIconSelects.push(new IconSelect({
                target: sel,
                items,
                columns: 1,
                horizontal: true,
                onChange: (value) => handler(monitorId, value),
            }));
        });
    } catch (e) {
        console.error('Failed to load display monitors:', e);
    }
}

export function onDisplayBrightnessInput(monitorId, value) {
    const label = document.getElementById(`brightness-val-${monitorId}`);
    if (label) label.textContent = `${value}%`;

    if (displayBrightnessTimers[monitorId]) clearTimeout(displayBrightnessTimers[monitorId]);
    displayBrightnessTimers[monitorId] = setTimeout(() => {
        sendDisplayBrightness(monitorId, parseInt(value));
        displayBrightnessTimers[monitorId] = null;
    }, DISPLAY_THROTTLE_MS);
}

export function onDisplayBrightnessChange(monitorId, value) {
    if (displayBrightnessTimers[monitorId]) {
        clearTimeout(displayBrightnessTimers[monitorId]);
        displayBrightnessTimers[monitorId] = null;
    }
    sendDisplayBrightness(monitorId, parseInt(value));
}

async function sendDisplayBrightness(monitorId, brightness) {
    try {
        await fetch(`/api/display/brightness/${monitorId}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
            body: JSON.stringify({ brightness })
        });
    } catch (e) {
        console.error('Failed to set brightness:', e);
    }
}

export function onDisplayContrastInput(monitorId, value) {
    const label = document.getElementById(`contrast-val-${monitorId}`);
    if (label) label.textContent = `${value}%`;

    if (displayContrastTimers[monitorId]) clearTimeout(displayContrastTimers[monitorId]);
    displayContrastTimers[monitorId] = setTimeout(() => {
        sendDisplayContrast(monitorId, parseInt(value));
        displayContrastTimers[monitorId] = null;
    }, DISPLAY_THROTTLE_MS);
}

export function onDisplayContrastChange(monitorId, value) {
    if (displayContrastTimers[monitorId]) {
        clearTimeout(displayContrastTimers[monitorId]);
        displayContrastTimers[monitorId] = null;
    }
    sendDisplayContrast(monitorId, parseInt(value));
}

async function sendDisplayContrast(monitorId, contrast) {
    try {
        const r = await fetch(`/api/display/contrast/${monitorId}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
            body: JSON.stringify({ contrast })
        });
        const data = await r.json().catch(() => ({}));
        if (!data.success) showToast(t('display.msg.contrast_failed'), 'error');
    } catch (e) {
        console.error('Failed to set contrast:', e);
        showToast(t('display.msg.contrast_failed'), 'error');
    }
}

export async function onDisplayInputSourceChange(monitorId, source) {
    try {
        const r = await fetch(`/api/display/input_source/${monitorId}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
            body: JSON.stringify({ source })
        });
        const data = await r.json().catch(() => ({}));
        if (data.success) showToast(t('display.msg.input_changed'), 'success');
        else showToast(t('display.msg.input_failed'), 'error');
    } catch (e) {
        console.error('Failed to set input source:', e);
        showToast(t('display.msg.input_failed'), 'error');
    }
}

export async function onDisplayColorPresetChange(monitorId, preset) {
    try {
        const r = await fetch(`/api/display/color_preset/${monitorId}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
            body: JSON.stringify({ preset })
        });
        const data = await r.json().catch(() => ({}));
        if (data.success) showToast(t('display.msg.color_changed'), 'success');
        else showToast(t('display.msg.color_failed'), 'error');
    } catch (e) {
        console.error('Failed to set color preset:', e);
        showToast(t('display.msg.color_failed'), 'error');
    }
}

export async function onDisplayPictureModeChange(monitorId, codeRaw) {
    const code = parseInt(codeRaw, 10);
    if (Number.isNaN(code)) return;
    try {
        const r = await fetch(`/api/display/picture_mode/${monitorId}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
            body: JSON.stringify({ code })
        });
        const data = await r.json().catch(() => ({}));
        if (data.success) showToast(t('display.msg.mode_changed'), 'success');
        else showToast(t('display.msg.mode_failed'), 'error');
    } catch (e) {
        console.error('Failed to set picture mode:', e);
        showToast(t('display.msg.mode_failed'), 'error');
    }
}

function _onPowerButtonClick(event) {
    const btn = event.target.closest('[data-action="toggle-power"]');
    if (!btn) return;
    const id = Number(btn.dataset.monitorId);
    if (Number.isFinite(id)) toggleDisplayPower(id);
}

function _onDisplaySliderInput(event) {
    const el = event.target.closest('input[data-display-slider]');
    if (!el) return;
    const id = Number(el.dataset.monitorId);
    if (!Number.isFinite(id)) return;
    if (el.dataset.displaySlider === 'brightness') {
        onDisplayBrightnessInput(id, el.value);
    } else if (el.dataset.displaySlider === 'contrast') {
        onDisplayContrastInput(id, el.value);
    }
}

function _onDisplaySliderChange(event) {
    const el = event.target.closest('input[data-display-slider]');
    if (!el) return;
    const id = Number(el.dataset.monitorId);
    if (!Number.isFinite(id)) return;
    if (el.dataset.displaySlider === 'brightness') {
        onDisplayBrightnessChange(id, el.value);
    } else if (el.dataset.displaySlider === 'contrast') {
        onDisplayContrastChange(id, el.value);
    }
}

export async function toggleDisplayPower(monitorId) {
    const btn = document.getElementById(`power-btn-${monitorId}`);
    const isOn = btn && btn.classList.contains('on');
    const newState = !isOn;

    try {
        const response = await fetch(`/api/display/power/${monitorId}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
            body: JSON.stringify({ on: newState })
        });
        const data = await response.json();
        if (data.success) {
            if (btn) {
                btn.classList.toggle('on', newState);
                btn.classList.toggle('off', !newState);
                btn.title = newState ? t('display.power_off') : t('display.power_on');
            }
            showToast(t(newState ? 'display.msg.power_on' : 'display.msg.power_off'), 'success');
        } else {
            showToast(t('display.msg.power_failed'), 'error');
        }
    } catch (e) {
        console.error('Failed to set display power:', e);
        showToast(t('display.msg.power_failed'), 'error');
    }
}

// ============================================================
// Header Quick Links
// ============================================================

export async function loadHeaderLinks() {
    if (!hasCredentials()) return;

    const container = document.getElementById('headerLinks');
    if (!container) return;

    try {
        const response = await fetch('/api/links/list', {
            headers: getAuthHeaders()
        });

        if (!response.ok) return;

        const links = await response.json();
        container.innerHTML = '';

        for (const link of links) {
            const a = document.createElement('a');
            a.href = link.url;
            a.target = '_blank';
            a.rel = 'noopener noreferrer';
            // Prevent leaking the WebUI URL (with ?token=) via Referer.
            a.referrerPolicy = 'no-referrer';
            a.className = 'header-link';
            a.title = link.label || link.url;

            const iconSvg = await fetchMdiIcon(link.icon || 'mdi:link');
            a.innerHTML = iconSvg;

            container.appendChild(a);
        }
    } catch (e) {
        console.warn('Failed to load header links:', e);
    }
}

// ============================================================
// Links Management
// ============================================================

let _loadLinksPromise = null;
export let linkFormDirty = false;
export function setLinkFormDirty(value) { linkFormDirty = value; }

export async function loadLinksTable() {
    if (_loadLinksPromise) return _loadLinksPromise;
    _loadLinksPromise = _loadLinksTableImpl();
    _loadLinksPromise.finally(() => { _loadLinksPromise = null; });
    return _loadLinksPromise;
}

async function _loadLinksTableImpl() {
    const tbody = document.getElementById('linksTableBody');

    try {
        const response = await fetch('/api/links/list', {
            headers: getAuthHeaders()
        });

        if (!response.ok) {
            throw new Error('Failed to fetch links');
        }

        const linksList = await response.json();

        if (linksList.length === 0) {
            tbody.innerHTML = '<tr><td colspan="4" class="empty-state"><div class="empty-state-illustration"><svg viewBox="0 0 64 64"><path d="M26 20a10 10 0 010 14l-6 6a10 10 0 01-14-14l6-6a10 10 0 0114 0" fill="none" stroke="currentColor" stroke-width="2"/><path d="M38 44a10 10 0 010-14l6-6a10 10 0 0114 14l-6 6a10 10 0 01-14 0" fill="none" stroke="currentColor" stroke-width="2"/><path d="M24 40l16-16" stroke="currentColor" stroke-width="2"/></svg><p>' + t('links.empty') + '</p></div></td></tr>';
            return;
        }

        tbody.innerHTML = linksList.map(link => `
            <tr>
                <td><span class="name-with-icon"><span class="table-icon" data-mdi-icon="${escapeHtml(link.icon || 'mdi:link')}"></span><code>${escapeHtml(link.name)}</code></span></td>
                <td style="max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
                    title="${escapeHtml(link.url)}">${escapeHtml(link.url)}</td>
                <td>${escapeHtml(link.label || '')}</td>
                <td>
                    <div class="action-buttons">
                        <button class="action-btn" data-action="edit" data-link-name="${escapeHtml(link.name)}" title="${t('links.button.edit')}">
                            <svg viewBox="0 0 24 24"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
                        </button>
                        <button class="action-btn delete" data-action="delete" data-link-name="${escapeHtml(link.name)}" title="${t('links.button.delete')}">
                            <svg viewBox="0 0 24 24"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
                        </button>
                    </div>
                </td>
            </tr>
        `).join('');
        resolveMdiIcons(tbody);
    } catch (error) {
        console.error('Error loading links:', error);
        tbody.innerHTML = `<tr><td colspan="4" class="empty-state" style="color: var(--error);">${escapeHtml(t('links.msg.load_failed'))}</td></tr>`;
    }
}

export function showAddLinkDialog() {
    const dialog = document.getElementById('linkDialog');
    const form = document.getElementById('linkForm');
    const title = document.getElementById('linkDialogTitle');

    form.reset();
    document.getElementById('linkOriginalName').value = '';
    document.getElementById('linkIsEdit').value = 'false';
    document.getElementById('linkName').disabled = false;
    document.getElementById('linkIconPreview').innerHTML = '';
    title.textContent = t('links.dialog.add');

    linkFormDirty = false;

    document.body.classList.add('dialog-open');
    dialog.showModal();
}

export async function showEditLinkDialog(linkName) {
    const dialog = document.getElementById('linkDialog');
    const title = document.getElementById('linkDialogTitle');

    try {
        const response = await fetch('/api/links/list', {
            headers: getAuthHeaders()
        });

        if (!response.ok) {
            throw new Error('Failed to fetch link details');
        }

        const linksList = await response.json();
        const link = linksList.find(l => l.name === linkName);

        if (!link) {
            showToast(t('links.msg.not_found'), 'error');
            return;
        }

        document.getElementById('linkOriginalName').value = linkName;
        document.getElementById('linkIsEdit').value = 'true';
        document.getElementById('linkName').value = linkName;
        document.getElementById('linkName').disabled = true;
        document.getElementById('linkUrl').value = link.url;
        document.getElementById('linkIcon').value = link.icon || '';
        document.getElementById('linkLabel').value = link.label || '';
        document.getElementById('linkDescription').value = link.description || '';

        // Update icon preview
        const preview = document.getElementById('linkIconPreview');
        if (link.icon) {
            fetchMdiIcon(link.icon).then(svg => { preview.innerHTML = svg; });
        } else {
            preview.innerHTML = '';
        }

        title.textContent = t('links.dialog.edit');

        linkFormDirty = false;

        document.body.classList.add('dialog-open');
        dialog.showModal();
    } catch (error) {
        console.error('Error loading link for edit:', error);
        showToast(t('links.msg.load_failed'), 'error');
    }
}

export async function closeLinkDialog() {
    if (linkFormDirty) {
        if (!await showConfirm(t('links.confirm.unsaved'))) {
            return;
        }
    }

    const dialog = document.getElementById('linkDialog');
    linkFormDirty = false;
    closeDialog(dialog);
    document.body.classList.remove('dialog-open');
}

export async function saveLink(event) {
    event.preventDefault();

    const submitBtn = event.target.querySelector('button[type="submit"]');
    if (submitBtn) submitBtn.disabled = true;

    const isEdit = document.getElementById('linkIsEdit').value === 'true';
    const linkName = isEdit ?
        document.getElementById('linkOriginalName').value :
        document.getElementById('linkName').value;

    const data = {
        url: document.getElementById('linkUrl').value,
        icon: document.getElementById('linkIcon').value || 'mdi:link',
        label: document.getElementById('linkLabel').value || '',
        description: document.getElementById('linkDescription').value || ''
    };

    const encodedName = encodeURIComponent(linkName);
    const endpoint = isEdit ?
        `/api/links/update/${encodedName}` :
        `/api/links/create/${encodedName}`;

    const method = isEdit ? 'PUT' : 'POST';

    try {
        const response = await fetch(endpoint, {
            method,
            headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
            body: JSON.stringify(data)
        });

        const result = await response.json();

        if (response.ok && result.success) {
            showToast(t(isEdit ? 'links.msg.updated' : 'links.msg.created'), 'success');
            linkFormDirty = false;
            closeLinkDialog();
        } else {
            showToast(result.detail || t(isEdit ? 'links.msg.update_failed' : 'links.msg.create_failed'), 'error');
        }
    } catch (error) {
        console.error('Error saving link:', error);
        showToast(t(isEdit ? 'links.msg.update_failed' : 'links.msg.create_failed'), 'error');
    } finally {
        if (submitBtn) submitBtn.disabled = false;
    }
}

export async function deleteLinkConfirm(linkName) {
    if (!await showConfirm(t('links.confirm.delete').replace('{name}', linkName))) {
        return;
    }

    try {
        const response = await fetch(`/api/links/delete/${encodeURIComponent(linkName)}`, {
            method: 'DELETE',
            headers: getAuthHeaders()
        });

        const result = await response.json();

        if (response.ok && result.success) {
            showToast(t('links.msg.deleted'), 'success');
        } else {
            showToast(result.detail || t('links.msg.delete_failed'), 'error');
        }
    } catch (error) {
        console.error('Error deleting link:', error);
        showToast(t('links.msg.delete_failed'), 'error');
    }
}
