/**
 * Performance charts — real-time CPU, RAM, GPU usage with Chart.js.
 * Supports system-wide and app-level (process) metrics with a toggle.
 * History is seeded from the server-side ring buffer on init.
 */

import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);
window.Chart = Chart;  // expose globally for targets.js, dashboard.js

import { API_BASE, getHeaders, fetchMetricsHistory } from '../core/api.ts';
import { t } from '../core/i18n.ts';
import { dashboardPollInterval } from '../core/state.ts';
import { createColorPicker, registerColorPicker } from '../core/color-picker.ts';

const MAX_SAMPLES = 120;
const CHART_KEYS = ['cpu', 'ram', 'gpu'];
const PERF_MODE_KEY = 'perfMetricsMode';

type PerfMode = 'system' | 'app' | 'both';

let _pollTimer: ReturnType<typeof setInterval> | null = null;
let _charts: Record<string, any> = {};     // { cpu: Chart, ram: Chart, gpu: Chart }
let _history: Record<string, number[]> = { cpu: [], ram: [], gpu: [] };
let _appHistory: Record<string, number[]> = { cpu: [], ram: [], gpu: [] };
let _hasGpu: boolean | null = null;   // null = unknown, true/false after first fetch
let _mode: PerfMode = (localStorage.getItem(PERF_MODE_KEY) as PerfMode) || 'both';

function _getColor(key: string): string {
    return localStorage.getItem(`perfChartColor_${key}`)
        || getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim()
        || '#4CAF50';
}

function _getAppColor(key: string): string {
    const base = _getColor(key);
    // Use a lighter/shifted version for the app line
    return base + '99';  // 60% opacity hex suffix
}

function _onChartColorChange(key: string, hex: string | null): void {
    if (hex) {
        localStorage.setItem(`perfChartColor_${key}`, hex);
    } else {
        // Reset: remove saved color, fall back to default
        localStorage.removeItem(`perfChartColor_${key}`);
        hex = _getColor(key);
        // Update swatch to show the actual default color
        const swatch = document.getElementById(`cp-swatch-perf-${key}`);
        if (swatch) swatch.style.background = hex;
    }
    const chart = _charts[key];
    if (chart) {
        chart.data.datasets[0].borderColor = hex;
        chart.data.datasets[0].backgroundColor = hex + '26';
        chart.data.datasets[1].borderColor = hex + '99';
        chart.data.datasets[1].backgroundColor = hex + '14';
        chart.update();
    }
}

/** Build the 3-way toggle HTML for perf section header. */
export function renderPerfModeToggle(): string {
    return `<span class="perf-mode-toggle" onclick="event.stopPropagation()">
        <button class="perf-mode-btn${_mode === 'system' ? ' active' : ''}" data-perf-mode="system" onclick="setPerfMode('system')" title="${t('dashboard.perf.mode.system')}">${t('dashboard.perf.mode.system')}</button>
        <button class="perf-mode-btn${_mode === 'app' ? ' active' : ''}" data-perf-mode="app" onclick="setPerfMode('app')" title="${t('dashboard.perf.mode.app')}">${t('dashboard.perf.mode.app')}</button>
        <button class="perf-mode-btn${_mode === 'both' ? ' active' : ''}" data-perf-mode="both" onclick="setPerfMode('both')" title="${t('dashboard.perf.mode.both')}">${t('dashboard.perf.mode.both')}</button>
    </span>`;
}

/** Change the perf metrics display mode. */
export function setPerfMode(mode: PerfMode): void {
    _mode = mode;
    localStorage.setItem(PERF_MODE_KEY, mode);

    // Update toggle button active states
    document.querySelectorAll('.perf-mode-btn').forEach(btn => {
        btn.classList.toggle('active', (btn as HTMLElement).dataset.perfMode === mode);
    });

    // Update dataset visibility on all charts
    for (const key of CHART_KEYS) {
        const chart = _charts[key];
        if (!chart) continue;
        const showSystem = mode === 'system' || mode === 'both';
        const showApp = mode === 'app' || mode === 'both';
        chart.data.datasets[0].hidden = !showSystem;
        chart.data.datasets[1].hidden = !showApp;
        chart.update('none');
    }
}

/** Returns the static HTML for the perf section (canvas placeholders). */
export function renderPerfSection(): string {
    // Register callbacks before rendering
    for (const key of CHART_KEYS) {
        registerColorPicker(`perf-${key}`, hex => _onChartColorChange(key, hex));
    }

    return `<div class="perf-charts-grid">
        <div class="perf-chart-card">
            <div class="perf-chart-header">
                <span class="perf-chart-label">${t('dashboard.perf.cpu')} ${createColorPicker({ id: 'perf-cpu', currentColor: _getColor('cpu'), onPick: undefined, anchor: 'left', showReset: true })}</span>
                <span class="perf-chart-value" id="perf-cpu-value">-</span>
            </div>
            <div class="perf-chart-wrap"><span class="perf-chart-subtitle" id="perf-cpu-name"></span><canvas id="perf-chart-cpu"></canvas></div>
        </div>
        <div class="perf-chart-card">
            <div class="perf-chart-header">
                <span class="perf-chart-label">${t('dashboard.perf.ram')} ${createColorPicker({ id: 'perf-ram', currentColor: _getColor('ram'), onPick: undefined, anchor: 'left', showReset: true })}</span>
                <span class="perf-chart-value" id="perf-ram-value">-</span>
            </div>
            <div class="perf-chart-wrap"><canvas id="perf-chart-ram"></canvas></div>
        </div>
        <div class="perf-chart-card" id="perf-gpu-card">
            <div class="perf-chart-header">
                <span class="perf-chart-label">${t('dashboard.perf.gpu')} ${createColorPicker({ id: 'perf-gpu', currentColor: _getColor('gpu'), onPick: undefined, anchor: 'left', showReset: true })}</span>
                <span class="perf-chart-value" id="perf-gpu-value">-</span>
            </div>
            <div class="perf-chart-wrap"><span class="perf-chart-subtitle" id="perf-gpu-name"></span><canvas id="perf-chart-gpu"></canvas></div>
        </div>
    </div>`;
}

function _createChart(canvasId: string, key: string): any {
    const ctx = document.getElementById(canvasId) as HTMLCanvasElement | null;
    if (!ctx) return null;
    const color = _getColor(key);
    const showSystem = _mode === 'system' || _mode === 'both';
    const showApp = _mode === 'app' || _mode === 'both';
    return new Chart(ctx, {
        type: 'line',
        data: {
            labels: Array(MAX_SAMPLES).fill(''),
            datasets: [
                {
                    // System-wide dataset
                    data: [],
                    borderColor: color,
                    backgroundColor: color + '26',
                    borderWidth: 1.5,
                    tension: 0.3,
                    fill: true,
                    pointRadius: 0,
                    hidden: !showSystem,
                },
                {
                    // App-level dataset (dashed line)
                    data: [],
                    borderColor: color + '99',
                    backgroundColor: color + '14',
                    borderWidth: 1.5,
                    borderDash: [4, 3],
                    tension: 0.3,
                    fill: true,
                    pointRadius: 0,
                    hidden: !showApp,
                },
            ],
        },
        options: {
            responsive: true,
            maintainAspectRatio: false,
            animation: false,
            plugins: { legend: { display: false }, tooltip: { enabled: false } },
            scales: {
                x: { display: false },
                y: { min: 0, max: 100, display: false },
            },
            layout: { padding: 0 },
        },
    });
}

/** Seed charts from server-side metrics history. */
async function _seedFromServer(): Promise<void> {
    try {
        const data = await fetchMetricsHistory();
        if (!data) return;
        const samples = data.system || [];
        _history.cpu = samples.map((s: any) => s.cpu).filter((v: any) => v != null);
        _history.ram = samples.map((s: any) => s.ram_pct).filter((v: any) => v != null);
        _history.gpu = samples.map((s: any) => s.gpu_util).filter((v: any) => v != null);
        _appHistory.cpu = samples.map((s: any) => s.app_cpu).filter((v: any) => v != null);
        _appHistory.ram = samples.map((s: any) => s.app_ram).filter((v: any) => v != null);
        _appHistory.gpu = samples.map((s: any) => s.app_gpu_mem).filter((v: any) => v != null);

        // Detect GPU availability from history
        if (_history.gpu.length > 0) {
            _hasGpu = true;
        }

        for (const key of CHART_KEYS) {
            const chart = _charts[key];
            if (!chart) continue;
            // System dataset
            if (_history[key].length > 0) {
                chart.data.datasets[0].data = [..._history[key]];
            }
            // App dataset
            if (_appHistory[key].length > 0) {
                chart.data.datasets[1].data = [..._appHistory[key]];
            }
            // Align labels to the longer dataset
            const maxLen = Math.max(chart.data.datasets[0].data.length, chart.data.datasets[1].data.length);
            chart.data.labels = Array(maxLen).fill('');
            chart.update();
        }
    } catch {
        // Silently ignore — charts will fill from polling
    }
}

/** Initialize Chart.js instances on the already-mounted canvases. */
export async function initPerfCharts(): Promise<void> {
    _destroyCharts();
    _charts.cpu = _createChart('perf-chart-cpu', 'cpu');
    _charts.ram = _createChart('perf-chart-ram', 'ram');
    _charts.gpu = _createChart('perf-chart-gpu', 'gpu');
    await _seedFromServer();
}

function _destroyCharts(): void {
    for (const key of Object.keys(_charts)) {
        if (_charts[key]) { _charts[key].destroy(); _charts[key] = null; }
    }
}

function _pushSample(key: string, sysValue: number, appValue: number | null): void {
    // System history
    _history[key].push(sysValue);
    if (_history[key].length > MAX_SAMPLES) _history[key].shift();

    // App history
    if (appValue != null) {
        _appHistory[key].push(appValue);
        if (_appHistory[key].length > MAX_SAMPLES) _appHistory[key].shift();
    }

    const chart = _charts[key];
    if (!chart) return;

    // Update system dataset
    const sysDs = chart.data.datasets[0].data;
    sysDs.length = 0;
    sysDs.push(..._history[key]);

    // Update app dataset
    const appDs = chart.data.datasets[1].data;
    appDs.length = 0;
    appDs.push(..._appHistory[key]);

    // Ensure labels array matches the longer dataset
    const maxLen = Math.max(sysDs.length, appDs.length);
    while (chart.data.labels.length < maxLen) chart.data.labels.push('');
    chart.data.labels.length = maxLen;
    chart.update('none');
}

/** Format the value display based on mode. */
function _formatValue(sysVal: string, appVal: string | null): string {
    if (_mode === 'system') return sysVal;
    if (_mode === 'app') return appVal ?? '-';
    // 'both': show both
    if (appVal != null) return `${sysVal} / ${appVal}`;
    return sysVal;
}

async function _fetchPerformance(): Promise<void> {
    try {
        const resp = await fetch(`${API_BASE}/system/performance`, { headers: getHeaders() });
        if (!resp.ok) return;
        const data = await resp.json();

        // CPU — app_cpu_percent is in the same scale as cpu_percent (per-core %)
        _pushSample('cpu', data.cpu_percent, data.app_cpu_percent);
        const cpuEl = document.getElementById('perf-cpu-value');
        if (cpuEl) {
            cpuEl.textContent = _formatValue(
                `${data.cpu_percent.toFixed(0)}%`,
                `${data.app_cpu_percent.toFixed(0)}%`
            );
        }
        if (data.cpu_name) {
            const nameEl = document.getElementById('perf-cpu-name');
            if (nameEl && !nameEl.textContent) nameEl.textContent = data.cpu_name;
        }

        // RAM — convert app_ram_mb to percent of total for consistent chart scale
        const appRamPct = data.ram_total_mb > 0
            ? (data.app_ram_mb / data.ram_total_mb) * 100
            : 0;
        _pushSample('ram', data.ram_percent, appRamPct);
        const ramEl = document.getElementById('perf-ram-value');
        if (ramEl) {
            const usedGb = (data.ram_used_mb / 1024).toFixed(1);
            const totalGb = (data.ram_total_mb / 1024).toFixed(1);
            const appMb = data.app_ram_mb.toFixed(0);
            ramEl.textContent = _formatValue(
                `${usedGb}/${totalGb} GB`,
                `${appMb} MB`
            );
        }

        // GPU
        if (data.gpu) {
            _hasGpu = true;
            // GPU utilization is system-wide only (no per-process util from NVML)
            // For app, show memory percentage if available
            const appGpuPct = (data.gpu.app_memory_mb != null && data.gpu.memory_total_mb)
                ? (data.gpu.app_memory_mb / data.gpu.memory_total_mb) * 100
                : null;
            _pushSample('gpu', data.gpu.utilization, appGpuPct);
            const gpuEl = document.getElementById('perf-gpu-value');
            if (gpuEl) {
                const sysText = `${data.gpu.utilization.toFixed(0)}% · ${data.gpu.temperature_c}°C`;
                const appText = data.gpu.app_memory_mb != null
                    ? `${data.gpu.app_memory_mb.toFixed(0)} MB VRAM`
                    : null;
                gpuEl.textContent = _formatValue(sysText, appText);
            }
            if (data.gpu.name) {
                const nameEl = document.getElementById('perf-gpu-name');
                if (nameEl && !nameEl.textContent) nameEl.textContent = data.gpu.name;
            }
        } else if (_hasGpu === null) {
            _hasGpu = false;
            const card = document.getElementById('perf-gpu-card');
            if (card) {
                const canvas = card.querySelector('canvas');
                if (canvas) canvas.style.display = 'none';
                const noGpu = document.createElement('div');
                noGpu.className = 'perf-chart-unavailable';
                noGpu.textContent = t('dashboard.perf.unavailable');
                card.appendChild(noGpu);
            }
        }
    } catch {
        // Silently ignore fetch errors (e.g., network issues, tab hidden)
    }
}

export function startPerfPolling(): void {
    if (_pollTimer) return;
    _fetchPerformance();
    _pollTimer = setInterval(_fetchPerformance, dashboardPollInterval);
}

export function stopPerfPolling(): void {
    if (_pollTimer) {
        clearInterval(_pollTimer);
        _pollTimer = null;
    }
}

// Pause polling when browser tab becomes hidden, resume when visible
document.addEventListener('visibilitychange', () => {
    if (document.hidden) {
        stopPerfPolling();
    } else {
        // Only resume if dashboard is active
        const activeTab = localStorage.getItem('activeTab') || 'dashboard';
        if (activeTab === 'dashboard') startPerfPolling();
    }
});
