/**
 * Auto-update — check for new releases, show banner, manage settings.
 */

import { fetchWithAuth } from '../core/api.ts';
import { showToast, showConfirm } from '../core/ui.ts';
import { t } from '../core/i18n.ts';
import { IconSelect } from '../core/icon-select.ts';
import { ICON_EXTERNAL_LINK, ICON_X, ICON_DOWNLOAD } from '../core/icons.ts';

// ─── State ──────────────────────────────────────────────────

interface UpdateRelease {
    version: string;
    tag: string;
    name: string;
    body: string;
    prerelease: boolean;
    published_at: string;
}

interface UpdateStatus {
    current_version: string;
    has_update: boolean;
    checking: boolean;
    last_check: number | null;
    last_error: string | null;
    releases_url: string;
    install_type: string;
    can_auto_update: boolean;
    downloading: boolean;
    download_progress: number;
    applying: boolean;
    release: UpdateRelease | null;
    dismissed_version: string;
}

let _lastStatus: UpdateStatus | null = null;
let _releaseNotesBody = '';

// ─── Version badge highlight ────────────────────────────────

function _setVersionBadgeUpdate(hasUpdate: boolean): void {
    const badge = document.getElementById('server-version');
    if (!badge) return;
    badge.classList.toggle('has-update', hasUpdate);

    if (hasUpdate) {
        badge.style.cursor = 'pointer';
        badge.title = t('update.badge_tooltip');
        badge.onclick = () => switchSettingsTabToUpdate();
    } else {
        badge.style.cursor = '';
        badge.title = '';
        badge.onclick = null;
    }
}

function switchSettingsTabToUpdate(): void {
    if (typeof (window as any).openSettingsModal === 'function') {
        (window as any).openSettingsModal();
    }
    setTimeout(() => {
        if (typeof (window as any).switchSettingsTab === 'function') {
            (window as any).switchSettingsTab('updates');
        }
    }, 50);
}

// ─── Update banner ──────────────────────────────────────────

function _showBanner(status: UpdateStatus): void {
    const release = status.release;
    if (!release) return;

    const dismissed = localStorage.getItem('update-dismissed-version');
    if (dismissed === release.version) return;

    const banner = document.getElementById('update-banner');
    if (!banner) return;

    const versionLabel = release.prerelease
        ? `${release.version} (${t('update.prerelease')})`
        : release.version;

    let actions = '';

    // "Update Now" button if auto-update is supported
    if (status.can_auto_update) {
        actions += `<button class="btn btn-icon update-banner-action update-banner-apply" onclick="applyUpdate()" title="${t('update.apply_now')}">
            ${ICON_DOWNLOAD}
        </button>`;
    }

    actions += `<a href="${status.releases_url}" target="_blank" rel="noopener" class="btn btn-icon update-banner-action" title="${t('update.view_release')}">
        ${ICON_EXTERNAL_LINK}
    </a>`;

    actions += `<button class="btn btn-icon update-banner-action" onclick="dismissUpdate()" title="${t('update.dismiss')}">
        ${ICON_X}
    </button>`;

    banner.innerHTML = `
        <span class="update-banner-text">
            ${t('update.available').replace('{version}', versionLabel)}
        </span>
        ${actions}
    `;
    banner.style.display = 'flex';
}

function _hideBanner(): void {
    const banner = document.getElementById('update-banner');
    if (banner) banner.style.display = 'none';
}

export function dismissUpdate(): void {
    if (!_lastStatus?.release) return;
    const version = _lastStatus.release.version;
    localStorage.setItem('update-dismissed-version', version);
    _hideBanner();
    _setVersionBadgeUpdate(false);

    fetchWithAuth('/system/update/dismiss', {
        method: 'POST',
        body: JSON.stringify({ version }),
    }).catch(() => {});
}

// ─── Apply update ───────────────────────────────────────────

export async function applyUpdate(): Promise<void> {
    if (!_lastStatus?.release) return;

    const version = _lastStatus.release.version;
    const confirmed = await showConfirm(
        t('update.apply_confirm').replace('{version}', version)
    );
    if (!confirmed) return;

    // Disable the apply button
    const btns = document.querySelectorAll('.update-banner-apply, #update-apply-btn');
    btns.forEach(b => (b as HTMLButtonElement).disabled = true);

    try {
        const resp = await fetchWithAuth('/system/update/apply', {
            method: 'POST',
            timeout: 600000, // 10 min for download + apply
        });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        // Server will shut down — the frontend reconnect overlay handles the rest
        showToast(t('update.applying'), 'info');
    } catch (err) {
        showToast(t('update.apply_error') + ': ' + (err as Error).message, 'error');
        btns.forEach(b => (b as HTMLButtonElement).disabled = false);
    }
}

// ─── Status fetch (called on page load + WS event) ─────────

export async function loadUpdateStatus(): Promise<void> {
    try {
        const resp = await fetchWithAuth('/system/update/status');
        if (!resp.ok) return;
        const status: UpdateStatus = await resp.json();
        _lastStatus = status;
        _applyStatus(status);
    } catch {
        // silent — non-critical
    }
}

function _applyStatus(status: UpdateStatus): void {
    const dismissed = localStorage.getItem('update-dismissed-version');
    const hasVisibleUpdate = status.has_update
        && status.release != null
        && status.release.version !== dismissed;

    _setVersionBadgeUpdate(hasVisibleUpdate);

    if (hasVisibleUpdate) {
        _showBanner(status);
    } else {
        _hideBanner();
    }

    _renderUpdatePanel(status);
}

// ─── WS event handlers ─────────────────────────────────────

export function initUpdateListener(): void {
    document.addEventListener('server:update_available', ((e: CustomEvent) => {
        const data = e.detail;
        if (data && data.version && !data.dismissed) {
            loadUpdateStatus();
        }
    }) as EventListener);

    // Download progress
    document.addEventListener('server:update_download_progress', ((e: CustomEvent) => {
        const progress = e.detail?.progress;
        if (typeof progress === 'number') {
            _updateProgressBar(progress);
        }
    }) as EventListener);
}

function _updateProgressBar(progress: number): void {
    const bar = document.getElementById('update-progress-bar');
    if (bar) {
        bar.style.width = `${Math.round(progress * 100)}%`;
        bar.parentElement!.style.display = progress > 0 && progress < 1 ? '' : 'none';
    }
}

// ─── Manual check ──────────────────────────────────────────

export async function checkForUpdates(): Promise<void> {
    const btn = document.getElementById('update-check-btn') as HTMLButtonElement | null;
    const spinner = document.getElementById('update-check-spinner');
    if (btn) btn.disabled = true;
    if (spinner) spinner.style.display = '';

    try {
        const resp = await fetchWithAuth('/system/update/check', { method: 'POST' });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        const status: UpdateStatus = await resp.json();
        _lastStatus = status;
        _applyStatus(status);

        if (status.has_update && status.release) {
            showToast(t('update.available').replace('{version}', status.release.version), 'info');
        } else {
            showToast(t('update.up_to_date'), 'success');
        }
    } catch (err) {
        showToast(t('update.check_error') + ': ' + (err as Error).message, 'error');
    } finally {
        if (btn) btn.disabled = false;
        if (spinner) spinner.style.display = 'none';
    }
}

// ─── Settings panel ────────────────────────────────────────

let _channelIconSelect: IconSelect | null = null;

function _getChannelItems(): { value: string; icon: string; label: string; desc: string }[] {
    return [
        {
            value: 'false',
            icon: '<span style="color:#4CAF50;font-weight:700">S</span>',
            label: t('update.channel.stable'),
            desc: t('update.channel.stable_desc'),
        },
        {
            value: 'true',
            icon: '<span style="color:#ff9800;font-weight:700">P</span>',
            label: t('update.channel.prerelease'),
            desc: t('update.channel.prerelease_desc'),
        },
    ];
}

export function initUpdateSettingsPanel(): void {
    if (!_channelIconSelect) {
        const sel = document.getElementById('update-channel') as HTMLSelectElement | null;
        if (sel) {
            _channelIconSelect = new IconSelect({
                target: sel,
                items: _getChannelItems(),
                columns: 2,
            });
        }
    }
}

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

        const enabledEl = document.getElementById('update-enabled') as HTMLInputElement | null;
        const intervalEl = document.getElementById('update-interval') as HTMLSelectElement | null;
        const channelEl = document.getElementById('update-channel') as HTMLSelectElement | null;

        if (enabledEl) enabledEl.checked = data.enabled;
        if (intervalEl) intervalEl.value = String(data.check_interval_hours);
        if (_channelIconSelect) {
            _channelIconSelect.setValue(String(data.include_prerelease));
        } else if (channelEl) {
            channelEl.value = String(data.include_prerelease);
        }
    } catch (err) {
        console.error('Failed to load update settings:', err);
    }

    await loadUpdateStatus();
}

export async function saveUpdateSettings(): Promise<void> {
    const enabled = (document.getElementById('update-enabled') as HTMLInputElement)?.checked ?? true;
    const intervalStr = (document.getElementById('update-interval') as HTMLSelectElement)?.value ?? '24';
    const check_interval_hours = parseFloat(intervalStr);
    const channelVal = (document.getElementById('update-channel') as HTMLSelectElement)?.value ?? 'false';
    const include_prerelease = channelVal === 'true';

    try {
        const resp = await fetchWithAuth('/system/update/settings', {
            method: 'PUT',
            body: JSON.stringify({ enabled, check_interval_hours, include_prerelease }),
        });
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || `HTTP ${resp.status}`);
        }
        showToast(t('update.settings_saved'), 'success');
    } catch (err) {
        showToast(t('update.settings_save_error') + ': ' + (err as Error).message, 'error');
    }
}

function _renderUpdatePanel(status: UpdateStatus): void {
    const currentEl = document.getElementById('update-current-version');
    if (currentEl) currentEl.textContent = `v${status.current_version}`;

    const statusEl = document.getElementById('update-status-text');
    if (statusEl) {
        if (status.has_update && status.release) {
            statusEl.textContent = t('update.available').replace('{version}', status.release.version);
            statusEl.style.color = 'var(--warning-color)';
        } else if (status.last_error) {
            statusEl.textContent = t('update.check_error') + ': ' + status.last_error;
            statusEl.style.color = 'var(--danger-color)';
        } else {
            statusEl.textContent = t('update.up_to_date');
            statusEl.style.color = 'var(--primary-color)';
        }
    }

    const lastCheckEl = document.getElementById('update-last-check');
    if (lastCheckEl) {
        if (status.last_check) {
            lastCheckEl.textContent = t('update.last_check') + ': ' + new Date(status.last_check * 1000).toLocaleString();
        } else {
            lastCheckEl.textContent = t('update.last_check') + ': ' + t('update.never');
        }
    }

    // Install type info
    const installEl = document.getElementById('update-install-type');
    if (installEl) {
        installEl.textContent = t(`update.install_type.${status.install_type}`);
    }

    // "Update Now" button in settings panel
    const applyBtn = document.getElementById('update-apply-btn') as HTMLButtonElement | null;
    if (applyBtn) {
        const show = status.has_update && status.can_auto_update;
        applyBtn.style.display = show ? '' : 'none';
        applyBtn.disabled = status.downloading || status.applying;
        if (status.downloading) {
            applyBtn.textContent = `${t('update.downloading')} ${Math.round(status.download_progress * 100)}%`;
        } else if (status.applying) {
            applyBtn.textContent = t('update.applying');
        } else {
            applyBtn.textContent = t('update.apply_now');
        }
    }

    // Progress bar
    const progressBar = document.getElementById('update-progress-bar');
    if (progressBar) {
        const show = status.downloading && status.download_progress > 0 && status.download_progress < 1;
        progressBar.style.width = `${Math.round(status.download_progress * 100)}%`;
        progressBar.parentElement!.style.display = show ? '' : 'none';
    }

    // Release notes button visibility
    const notesGroup = document.getElementById('update-release-notes-group');
    if (notesGroup) {
        if (status.has_update && status.release && status.release.body) {
            _releaseNotesBody = status.release.body;
            notesGroup.style.display = '';
        } else {
            _releaseNotesBody = '';
            notesGroup.style.display = 'none';
        }
    }
}

// ─── Release Notes Overlay ─────────────────────────────────

export function openReleaseNotes(): void {
    const overlay = document.getElementById('release-notes-overlay');
    const content = document.getElementById('release-notes-content');
    if (overlay && content) {
        import('marked').then(({ marked }) => {
            content.innerHTML = marked.parse(_releaseNotesBody) as string;
            overlay.style.display = 'flex';
        });
    }
}

export function closeReleaseNotes(): void {
    const overlay = document.getElementById('release-notes-overlay');
    if (overlay) overlay.style.display = 'none';
}
