/**
 * API utilities — base URL, auth headers, fetch wrapper, helpers.
 */

import { apiKey, setApiKey, authRequired, setAuthRequired, refreshInterval, setRefreshInterval, displaysCache } from './state.ts';
import { t } from './i18n.ts';
import { showToast } from './ui.ts';
import { getEl, queryEl } from './dom-utils.ts';
import { serverRestarting, clearRestartingFlag } from './events-ws.ts';

export const API_BASE = '/api/v1';

export function getHeaders() {
    const headers: Record<string, string> = {
        'Content-Type': 'application/json'
    };
    if (apiKey) {
        headers['Authorization'] = `Bearer ${apiKey}`;
    }
    return headers;
}

export class ApiError extends Error {
    status: number;
    isAuth: boolean;

    constructor(status: number, message: string) {
        super(message);
        this.name = 'ApiError';
        this.status = status;
        this.isAuth = status === 401;
    }
}

interface FetchAuthOpts extends RequestInit {
    retry?: boolean;
    timeout?: number;
    handle401?: boolean;
}

export async function fetchWithAuth(url: string, options: FetchAuthOpts = {}): Promise<Response> {
    const { retry = true, timeout = 10000, handle401: auto401 = true, ...fetchOpts } = options;
    const fullUrl = url.startsWith('http') ? url : `${API_BASE}${url}`;
    const headers = fetchOpts.headers
        ? { ...getHeaders(), ...fetchOpts.headers }
        : getHeaders();

    const maxAttempts = retry ? 3 : 1;
    for (let attempt = 0; attempt < maxAttempts; attempt++) {
        const controller = new AbortController();
        if (fetchOpts.signal) {
            fetchOpts.signal.addEventListener('abort', () => controller.abort(), { once: true });
        }
        const timer = setTimeout(() => controller.abort(), timeout);
        try {
            const resp = await fetch(fullUrl, { ...fetchOpts, headers, signal: controller.signal });
            clearTimeout(timer);
            if (resp.status === 401 && auto401) {
                handle401Error();
                throw new ApiError(401, 'Session expired');
            }
            if (resp.status >= 500 && attempt < maxAttempts - 1) {
                await new Promise(r => setTimeout(r, 500 * 2 ** attempt));
                continue;
            }
            return resp;
        } catch (err) {
            clearTimeout(timer);
            if (err instanceof ApiError) throw err;
            if (attempt < maxAttempts - 1) {
                await new Promise(r => setTimeout(r, 500 * 2 ** attempt));
                continue;
            }
            // Final attempt failed — show toast only if the connection
            // overlay isn't already covering the screen
            if (_serverOnline !== false) {
                const errMsg = (err as Error)?.name === 'AbortError'
                    ? t('api.error.timeout')
                    : t('api.error.network');
                showToast(errMsg, 'error');
            }
            throw err;
        }
    }
    throw new Error('fetchWithAuth: unreachable code — retry loop exhausted');
}

// ── Cached metrics-history fetch ────────────────────────────
let _metricsHistoryCache: { data: any; ts: number } | null = null;
const _METRICS_CACHE_TTL = 5000; // 5 seconds

/** Fetch metrics history with a short TTL cache to avoid duplicate requests across tabs. */
export async function fetchMetricsHistory(): Promise<any | null> {
    const now = Date.now();
    if (_metricsHistoryCache && now - _metricsHistoryCache.ts < _METRICS_CACHE_TTL) {
        return _metricsHistoryCache.data;
    }
    try {
        const resp = await fetch(`${API_BASE}/system/metrics-history`, { headers: getHeaders() });
        if (!resp.ok) return null;
        const data = await resp.json();
        _metricsHistoryCache = { data, ts: now };
        return data;
    } catch {
        return null;
    }
}

export function escapeHtml(text: string) {
    if (!text) return '';
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

export function isSerialDevice(type: string) {
    return type === 'adalight' || type === 'ambiled';
}

export function isMockDevice(type: string) {
    return type === 'mock';
}

export function isMqttDevice(type: string) {
    return type === 'mqtt';
}

export function isWsDevice(type: string) {
    return type === 'ws';
}

export function isOpenrgbDevice(type: string) {
    return type === 'openrgb';
}

export function isDmxDevice(type: string) {
    return type === 'dmx';
}

export function isEspnowDevice(type: string) {
    return type === 'espnow';
}

export function isHueDevice(type: string) {
    return type === 'hue';
}

export function isUsbhidDevice(type: string) {
    return type === 'usbhid';
}

export function isSpiDevice(type: string) {
    return type === 'spi';
}

export function isChromaDevice(type: string) {
    return type === 'chroma';
}

export function isGameSenseDevice(type: string) {
    return type === 'gamesense';
}

export function handle401Error() {
    if (!authRequired) return; // Auth disabled — ignore 401s
    if (!apiKey) return; // Already handled or no session
    localStorage.removeItem('wled_api_key');
    setApiKey(null);

    if (refreshInterval) {
        clearInterval(refreshInterval);
        setRefreshInterval(null);
    }

    if (typeof window.updateAuthUI === 'function') {
        window.updateAuthUI();
    }

    const expiredMsg = t('auth.session_expired');

    if (typeof window.showApiKeyModal === 'function') {
        window.showApiKeyModal(expiredMsg, true);
    } else {
        const toast = document.getElementById('toast');
        if (toast) {
            toast.textContent = expiredMsg;
            toast.className = 'toast error show';
            setTimeout(() => { toast.className = 'toast'; }, 3000);
        }
    }
}

let _connCheckTimer: ReturnType<typeof setInterval> | null = null;
let _serverOnline: boolean | null = null;  // null = unknown, true/false

/** Toggle which message block is visible inside the connection overlay. */
function _setOverlayMode(restarting: boolean) {
    const msgOffline = document.getElementById('conn-msg-offline');
    const msgRestarting = document.getElementById('conn-msg-restarting');
    if (msgOffline) msgOffline.style.display = restarting ? 'none' : '';
    if (msgRestarting) msgRestarting.style.display = restarting ? '' : 'none';
}

/**
 * Show the restart overlay immediately (called when server_restarting
 * event arrives via WebSocket, before the connection actually drops).
 */
export function showRestartingOverlay() {
    _serverOnline = false;
    const banner = document.getElementById('connection-overlay');
    const badge = document.getElementById('server-status');
    if (banner) {
        (banner as HTMLElement).style.display = 'flex';
        banner.setAttribute('aria-hidden', 'false');
    }
    _setOverlayMode(true);
    if (badge) badge.className = 'status-badge offline';
}

function _setConnectionState(online: boolean) {
    const changed = _serverOnline !== online;
    _serverOnline = online;
    const banner = document.getElementById('connection-overlay');
    const badge = document.getElementById('server-status');
    if (online) {
        if (banner) { (banner as HTMLElement).style.display = 'none'; banner.setAttribute('aria-hidden', 'true'); }
        if (badge) badge.className = 'status-badge online';
        // Clear the restarting flag once the server is back
        if (serverRestarting) clearRestartingFlag();
    } else {
        if (banner) {
            (banner as HTMLElement).style.display = 'flex';
            banner.setAttribute('aria-hidden', 'false');
        }
        _setOverlayMode(serverRestarting);
        if (badge) badge.className = 'status-badge offline';
    }
    return changed;
}

export let demoMode = false;
export let serverRepoUrl = '';
export let serverDonateUrl = '';

export async function loadServerInfo() {
    try {
        const response = await fetch('/health', { signal: AbortSignal.timeout(5000) });
        const data = await response.json();

        const versionEl = queryEl('version-number');
        if (versionEl) versionEl.textContent = `v${data.version}`;
        const statusEl = queryEl('server-status');
        if (statusEl) statusEl.textContent = '●';
        const wasOffline = _serverOnline === false;
        _setConnectionState(true);
        if (wasOffline) {
            // Server came back — hard reload to ensure fresh data
            location.reload();
            return;
        }

        // Auth mode detection
        const authNeeded = data.auth_required !== false;
        setAuthRequired(authNeeded);
        (window as any)._authRequired = authNeeded;

        // Project URLs (repo, donate)
        if (data.repo_url) serverRepoUrl = data.repo_url;
        if (data.donate_url) serverDonateUrl = data.donate_url;

        // Demo mode detection
        if (data.demo_mode && !demoMode) {
            demoMode = true;
            document.body.dataset.demo = 'true';
            const badge = document.getElementById('demo-badge');
            if (badge) badge.style.display = '';
            const banner = document.getElementById('demo-banner');
            if (banner && localStorage.getItem('demo-banner-dismissed') !== 'true') {
                banner.style.display = '';
            }
        }
    } catch (error) {
        console.error('Failed to load server info:', error);
        _setConnectionState(false);
    }
}

/**
 * Start periodic health checks. Shows/hides the connection banner.
 * @param {number} interval - Check interval in ms (default 10s)
 */
export function startConnectionMonitor(interval = 10000) {
    stopConnectionMonitor();
    _connCheckTimer = setInterval(loadServerInfo, interval);
}

export function stopConnectionMonitor() {
    if (_connCheckTimer) {
        clearInterval(_connCheckTimer);
        _connCheckTimer = null;
    }
}

export async function loadDisplays(engineType: string | null = null) {
    if (engineType) {
        // Filtered fetch — bypass cache (engine-specific display list)
        try {
            const response = await fetchWithAuth(`/config/displays?engine_type=${engineType}`);
            const data = await response.json();
            if (data.displays && data.displays.length > 0) {
                displaysCache.update(data.displays);
            }
        } catch (error) {
            if (error instanceof ApiError && error.isAuth) return;
            console.error('Failed to load displays:', error);
        }
    } else {
        await displaysCache.fetch();
    }
}

export function configureApiKey() {
    const currentKey = localStorage.getItem('wled_api_key');
    const message = currentKey
        ? t('auth.prompt_update')
        : t('auth.prompt_enter');

    const key = prompt(message);

    if (key === null) {
        return;
    }

    if (key === '') {
        localStorage.removeItem('wled_api_key');
        setApiKey(null);
        const keyBtnHide = queryEl('api-key-btn');
        if (keyBtnHide) keyBtnHide.style.display = 'none';
    } else {
        localStorage.setItem('wled_api_key', key);
        setApiKey(key);
        const keyBtnShow = queryEl('api-key-btn');
        if (keyBtnShow) keyBtnShow.style.display = 'inline-block';
    }

    loadServerInfo();
    loadDisplays();
    document.dispatchEvent(new CustomEvent('auth:keyChanged'));
}
