/**
 * Appearance — style presets (font + colors) and background effect presets.
 *
 * Persists choices to localStorage. Style presets override CSS variables;
 * background effects toggle CSS classes on <html> or start WebGL shaders.
 */

import { t } from '../core/i18n.ts';
import { showToast } from '../core/ui.ts';
import {
    startShaderEffect, stopShaderEffect, isShaderEffect,
    updateShaderAccent, updateShaderTheme,
    type ShaderEffectId,
} from '../core/bg-shaders.ts';

// ─── Types ──────────────────────────────────────────────────

interface StylePreset {
    readonly id: string;
    /** i18n key for the display name */
    readonly nameKey: string;
    /** Body font-family stack */
    readonly fontBody: string;
    /** Heading font-family (h1 logo) */
    readonly fontHeading: string;
    /** Primary accent color */
    readonly accent: string;
    /** Dark-theme overrides (applied when data-theme="dark") */
    readonly dark: ThemeVars;
    /** Light-theme overrides (applied when data-theme="light") */
    readonly light: ThemeVars;
    /** Google Fonts URL to load (empty = use local fonts only) */
    readonly fontUrl: string;
}

interface ThemeVars {
    readonly bgColor: string;
    readonly bgSecondary: string;
    readonly cardBg: string;
    readonly textColor: string;
    readonly textSecondary: string;
    readonly textMuted: string;
    readonly borderColor: string;
    readonly inputBg: string;
}

interface BgEffectPreset {
    readonly id: string;
    readonly nameKey: string;
    /** CSS class added to bg-effect-layer. '' = none (shader or webgl-only). */
    readonly cssClass: string;
    /** Reuse the existing WebGL noise-field (data-bg-anim). */
    readonly useBgAnim: boolean;
    /** Shader effect ID (from bg-shaders.ts). '' = not a shader. */
    readonly shaderId: string;
}

// ─── Style preset definitions ───────────────────────────────

const STYLE_PRESETS: readonly StylePreset[] = [
    {
        id: 'default',
        nameKey: 'appearance.preset.default',
        fontBody: "'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
        fontHeading: "'Orbitron', sans-serif",
        accent: '#4CAF50',
        fontUrl: '',
        dark: {
            bgColor: '#1a1a1a', bgSecondary: '#242424', cardBg: '#2d2d2d',
            textColor: '#e0e0e0', textSecondary: '#999', textMuted: '#777',
            borderColor: '#404040', inputBg: '#1a1a2e',
        },
        light: {
            bgColor: '#f5f5f5', bgSecondary: '#eee', cardBg: '#ffffff',
            textColor: '#333333', textSecondary: '#595959', textMuted: '#767676',
            borderColor: '#e0e0e0', inputBg: '#f0f0f0',
        },
    },
    {
        id: 'midnight',
        nameKey: 'appearance.preset.midnight',
        fontBody: "'IBM Plex Sans', 'DM Sans', -apple-system, sans-serif",
        fontHeading: "'Orbitron', sans-serif",
        accent: '#7C4DFF',
        fontUrl: 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;600;700&display=swap',
        dark: {
            bgColor: '#0f0e1a', bgSecondary: '#181627', cardBg: '#1e1c30',
            textColor: '#d4d0f0', textSecondary: '#9590b8', textMuted: '#6b6790',
            borderColor: '#2e2b4a', inputBg: '#141225',
        },
        light: {
            bgColor: '#f0eef8', bgSecondary: '#e5e2f0', cardBg: '#ffffff',
            textColor: '#2a2640', textSecondary: '#5c5878', textMuted: '#8884a0',
            borderColor: '#d0cde0', inputBg: '#eae8f2',
        },
    },
    {
        id: 'ember',
        nameKey: 'appearance.preset.ember',
        fontBody: "'Nunito Sans', 'DM Sans', -apple-system, sans-serif",
        fontHeading: "'Orbitron', sans-serif",
        accent: '#FF6D00',
        fontUrl: 'https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@400;600;700&display=swap',
        dark: {
            bgColor: '#1a1410', bgSecondary: '#241c14', cardBg: '#2c221a',
            textColor: '#e8ddd0', textSecondary: '#a89880', textMuted: '#7a6e5e',
            borderColor: '#3d3328', inputBg: '#1e1812',
        },
        light: {
            bgColor: '#faf5ee', bgSecondary: '#f0e8da', cardBg: '#ffffff',
            textColor: '#3a2e20', textSecondary: '#6e5e48', textMuted: '#998870',
            borderColor: '#e0d4c2', inputBg: '#f5efe4',
        },
    },
    {
        id: 'arctic',
        nameKey: 'appearance.preset.arctic',
        fontBody: "'Outfit', 'DM Sans', -apple-system, sans-serif",
        fontHeading: "'Orbitron', sans-serif",
        accent: '#0091EA',
        fontUrl: 'https://fonts.googleapis.com/css2?family=Outfit:wght@400;600;700&display=swap',
        dark: {
            bgColor: '#0e1820', bgSecondary: '#142028', cardBg: '#1a2830',
            textColor: '#d0e4f0', textSecondary: '#88a8c0', textMuted: '#607888',
            borderColor: '#283848', inputBg: '#121e28',
        },
        light: {
            bgColor: '#f0f6fa', bgSecondary: '#e4eef4', cardBg: '#ffffff',
            textColor: '#1a3040', textSecondary: '#4a6a80', textMuted: '#7898a8',
            borderColor: '#d0dfe8', inputBg: '#e8f0f5',
        },
    },
    {
        id: 'terminal',
        nameKey: 'appearance.preset.terminal',
        fontBody: "'JetBrains Mono', 'Cascadia Code', 'Fira Code', monospace",
        fontHeading: "'JetBrains Mono', monospace",
        accent: '#00E676',
        fontUrl: 'https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&display=swap',
        dark: {
            bgColor: '#0a0a0a', bgSecondary: '#111111', cardBg: '#161616',
            textColor: '#c8e6c9', textSecondary: '#6a9b6e', textMuted: '#4a7a4e',
            borderColor: '#1e3a1e', inputBg: '#0d0d0d',
        },
        light: {
            bgColor: '#f0f8f0', bgSecondary: '#e0f0e0', cardBg: '#ffffff',
            textColor: '#1a2e1a', textSecondary: '#3a6a3a', textMuted: '#5a8a5a',
            borderColor: '#c0dcc0', inputBg: '#e8f2e8',
        },
    },
    {
        id: 'neon',
        nameKey: 'appearance.preset.neon',
        fontBody: "'Exo 2', 'DM Sans', -apple-system, sans-serif",
        fontHeading: "'Orbitron', sans-serif",
        accent: '#FF1493',
        fontUrl: 'https://fonts.googleapis.com/css2?family=Exo+2:wght@400;600;700&display=swap',
        dark: {
            bgColor: '#08060e', bgSecondary: '#100d18', cardBg: '#14101e',
            textColor: '#e8d8f0', textSecondary: '#a888c0', textMuted: '#785898',
            borderColor: '#281e40', inputBg: '#0c0810',
        },
        light: {
            bgColor: '#faf0f8', bgSecondary: '#f0e4ee', cardBg: '#ffffff',
            textColor: '#30182a', textSecondary: '#704060', textMuted: '#a07090',
            borderColor: '#e0c8d8', inputBg: '#f5eaf2',
        },
    },
    {
        id: 'sakura',
        nameKey: 'appearance.preset.sakura',
        fontBody: "'Libre Baskerville', 'Georgia', serif",
        fontHeading: "'Playfair Display', 'Georgia', serif",
        accent: '#E8607C',
        fontUrl: 'https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@400;700&family=Playfair+Display:wght@700&display=swap',
        dark: {
            bgColor: '#1a1216', bgSecondary: '#241820', cardBg: '#2c1e26',
            textColor: '#f0dce4', textSecondary: '#b89aa8', textMuted: '#8a6e7c',
            borderColor: '#3e2c36', inputBg: '#1e1418',
        },
        light: {
            bgColor: '#fdf5f7', bgSecondary: '#f8e8ee', cardBg: '#ffffff',
            textColor: '#3a1e28', textSecondary: '#6e4a58', textMuted: '#a07888',
            borderColor: '#ecd0da', inputBg: '#faf0f4',
        },
    },
    {
        id: 'ocean',
        nameKey: 'appearance.preset.ocean',
        fontBody: "'Plus Jakarta Sans', 'DM Sans', -apple-system, sans-serif",
        fontHeading: "'Orbitron', sans-serif",
        accent: '#0288D1',
        fontUrl: 'https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700&display=swap',
        dark: {
            bgColor: '#0a1628', bgSecondary: '#0e1e34', cardBg: '#142640',
            textColor: '#c8ddf0', textSecondary: '#7498b8', textMuted: '#506e88',
            borderColor: '#1e3858', inputBg: '#0c1a2e',
        },
        light: {
            bgColor: '#eef5fa', bgSecondary: '#deeaf4', cardBg: '#ffffff',
            textColor: '#0e2840', textSecondary: '#386080', textMuted: '#6890a8',
            borderColor: '#c0d8ea', inputBg: '#e4eff6',
        },
    },
    {
        id: 'copper',
        nameKey: 'appearance.preset.copper',
        fontBody: "'Barlow', 'DM Sans', -apple-system, sans-serif",
        fontHeading: "'Barlow Condensed', 'Barlow', sans-serif",
        accent: '#D4764E',
        fontUrl: 'https://fonts.googleapis.com/css2?family=Barlow:wght@400;600;700&family=Barlow+Condensed:wght@700&display=swap',
        dark: {
            bgColor: '#18120e', bgSecondary: '#221a14', cardBg: '#2a201a',
            textColor: '#e4d4c4', textSecondary: '#a88e78', textMuted: '#7a6454',
            borderColor: '#3a2e24', inputBg: '#1c1610',
        },
        light: {
            bgColor: '#faf6f2', bgSecondary: '#f0e6dc', cardBg: '#ffffff',
            textColor: '#362618', textSecondary: '#6e5440', textMuted: '#9a8268',
            borderColor: '#dcd0c2', inputBg: '#f6efe6',
        },
    },
    {
        id: 'vapor',
        nameKey: 'appearance.preset.vapor',
        fontBody: "'Quicksand', 'DM Sans', -apple-system, sans-serif",
        fontHeading: "'Righteous', 'Orbitron', sans-serif",
        accent: '#E040FB',
        fontUrl: 'https://fonts.googleapis.com/css2?family=Quicksand:wght@400;600;700&family=Righteous&display=swap',
        dark: {
            bgColor: '#0e0818', bgSecondary: '#160e24', cardBg: '#1c1430',
            textColor: '#e8d0f8', textSecondary: '#a078c0', textMuted: '#705898',
            borderColor: '#2c1e48', inputBg: '#120a1e',
        },
        light: {
            bgColor: '#faf2fe', bgSecondary: '#f0e4f8', cardBg: '#ffffff',
            textColor: '#281438', textSecondary: '#604078', textMuted: '#9870b0',
            borderColor: '#dcc8ee', inputBg: '#f4eaf8',
        },
    },
    {
        id: 'monolith',
        nameKey: 'appearance.preset.monolith',
        fontBody: "'Instrument Sans', 'DM Sans', -apple-system, sans-serif",
        fontHeading: "'Instrument Sans', sans-serif",
        accent: '#FFFFFF',
        fontUrl: 'https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;600;700&display=swap',
        dark: {
            bgColor: '#000000', bgSecondary: '#0a0a0a', cardBg: '#111111',
            textColor: '#e8e8e8', textSecondary: '#888888', textMuted: '#555555',
            borderColor: '#222222', inputBg: '#080808',
        },
        light: {
            bgColor: '#ffffff', bgSecondary: '#f2f2f2', cardBg: '#fafafa',
            textColor: '#111111', textSecondary: '#555555', textMuted: '#888888',
            borderColor: '#dddddd', inputBg: '#f5f5f5',
        },
    },
];

// ─── Background effect definitions ──────────────────────────

const BG_EFFECT_PRESETS: readonly BgEffectPreset[] = [
    { id: 'none',       nameKey: 'appearance.bg.none',       cssClass: '',                    useBgAnim: false, shaderId: '' },
    { id: 'noise',      nameKey: 'appearance.bg.noise',      cssClass: '',                    useBgAnim: true,  shaderId: '' },
    { id: 'aurora',     nameKey: 'appearance.bg.aurora',     cssClass: '',                    useBgAnim: false, shaderId: 'aurora' },
    { id: 'plasma',     nameKey: 'appearance.bg.plasma',     cssClass: '',                    useBgAnim: false, shaderId: 'plasma' },
    { id: 'rain',       nameKey: 'appearance.bg.rain',       cssClass: '',                    useBgAnim: false, shaderId: 'rain' },
    { id: 'stars',      nameKey: 'appearance.bg.stars',      cssClass: '',                    useBgAnim: false, shaderId: 'stars' },
    { id: 'warp',       nameKey: 'appearance.bg.warp',       cssClass: '',                    useBgAnim: false, shaderId: 'warp' },
    { id: 'grid',       nameKey: 'appearance.bg.grid',       cssClass: 'bg-effect-grid',      useBgAnim: false, shaderId: '' },
    { id: 'mesh',       nameKey: 'appearance.bg.mesh',       cssClass: 'bg-effect-mesh',      useBgAnim: false, shaderId: '' },
    { id: 'scanlines',  nameKey: 'appearance.bg.scanlines',  cssClass: 'bg-effect-scanlines', useBgAnim: false, shaderId: '' },
];

// ─── Persistence keys ───────────────────────────────────────

const LS_STYLE_PRESET = 'stylePreset';
const LS_BG_EFFECT = 'bgEffect';

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

let _activeStyleId = 'default';
let _activeBgEffectId = 'none';

// ─── Public API ─────────────────────────────────────────────

/** Get all style presets (for rendering the picker). */
export function getStylePresets(): readonly StylePreset[] {
    return STYLE_PRESETS;
}

/** Get all background effect presets. */
export function getBgEffectPresets(): readonly BgEffectPreset[] {
    return BG_EFFECT_PRESETS;
}

/** Apply a style preset by ID. Persists to localStorage. */
export function applyStylePreset(id: string): void {
    const preset = STYLE_PRESETS.find(p => p.id === id);
    if (!preset) return;

    _activeStyleId = id;
    localStorage.setItem(LS_STYLE_PRESET, id);

    // Load Google Font if needed
    _ensureFont(preset.fontUrl, preset.id);

    // Apply font families
    document.documentElement.style.setProperty('--font-body', preset.fontBody);
    document.documentElement.style.setProperty('--font-heading', preset.fontHeading);

    // Apply accent color via existing mechanism
    const applyAccent = (window as any).applyAccentColor;
    if (typeof applyAccent === 'function') {
        applyAccent(preset.accent, true);
    } else {
        document.documentElement.style.setProperty('--primary-color', preset.accent);
        localStorage.setItem('accentColor', preset.accent);
    }

    // Update shader accent if a shader effect is running
    updateShaderAccent(preset.accent);

    // Apply theme-specific color overrides
    _applyThemeVars(preset);

    // Update UI selection state
    _updatePresetSelection('style', id);

    showToast(t('appearance.preset.applied'), 'info');
}

/** Apply a background effect by ID. Persists to localStorage. */
export function applyBgEffect(id: string): void {
    const effect = BG_EFFECT_PRESETS.find(e => e.id === id);
    if (!effect) return;

    _activeBgEffectId = id;
    localStorage.setItem(LS_BG_EFFECT, id);

    const html = document.documentElement;

    // ── 1. Stop all previous effects ──

    // Stop shader effects
    stopShaderEffect();

    // Remove CSS effect classes from the dedicated layer
    const layer = document.getElementById('bg-effect-layer');
    if (layer) {
        BG_EFFECT_PRESETS.forEach(e => {
            if (e.cssClass) layer.classList.remove(e.cssClass);
        });
    }

    // Turn off WebGL noise-field
    html.setAttribute('data-bg-anim', 'off');
    localStorage.setItem('bgAnim', 'off');

    // Clear data-bg-effect (CSS transparency trigger)
    html.removeAttribute('data-bg-effect');

    // ── 2. Activate the selected effect ──

    const hasVisualEffect = effect.useBgAnim || !!effect.cssClass || !!effect.shaderId;

    if (effect.useBgAnim) {
        // Original WebGL noise field
        html.setAttribute('data-bg-anim', 'on');
        localStorage.setItem('bgAnim', 'on');
    } else if (effect.shaderId && isShaderEffect(effect.shaderId)) {
        // New shader-based effect — needs body transparency via data-bg-effect
        html.setAttribute('data-bg-effect', effect.id);
        startShaderEffect(effect.shaderId as ShaderEffectId);
    } else if (effect.cssClass && layer) {
        // CSS-based effect
        layer.classList.add(effect.cssClass);
        html.setAttribute('data-bg-effect', effect.id);
    }

    // Update header toggle button opacity
    const bgAnimBtn = document.getElementById('bg-anim-btn');
    if (bgAnimBtn) bgAnimBtn.style.opacity = hasVisualEffect ? '1' : '0.5';

    _updatePresetSelection('bg', id);

    showToast(t('appearance.bg.applied'), 'info');
}

/** Restore saved presets on page load. Called from init. */
export function initAppearance(): void {
    _activeStyleId = localStorage.getItem(LS_STYLE_PRESET) || 'default';
    _activeBgEffectId = localStorage.getItem(LS_BG_EFFECT) || 'noise';

    // Apply style preset silently (without toast)
    const preset = STYLE_PRESETS.find(p => p.id === _activeStyleId);
    if (preset && preset.id !== 'default') {
        _ensureFont(preset.fontUrl, preset.id);
        document.documentElement.style.setProperty('--font-body', preset.fontBody);
        document.documentElement.style.setProperty('--font-heading', preset.fontHeading);
        _applyThemeVars(preset);

        // Sync accent to shader engine
        updateShaderAccent(preset.accent);
    }

    // Sync theme to shader engine
    const isDark = (document.documentElement.getAttribute('data-theme') || 'dark') === 'dark';
    updateShaderTheme(isDark);

    // Apply background effect silently
    const effect = BG_EFFECT_PRESETS.find(e => e.id === _activeBgEffectId);
    if (effect && effect.id !== 'none') {
        const html = document.documentElement;

        if (effect.useBgAnim) {
            html.setAttribute('data-bg-anim', 'on');
            localStorage.setItem('bgAnim', 'on');
        } else if (effect.shaderId && isShaderEffect(effect.shaderId)) {
            html.setAttribute('data-bg-effect', effect.id);
            startShaderEffect(effect.shaderId as ShaderEffectId);
        } else if (effect.cssClass) {
            const layer = document.getElementById('bg-effect-layer');
            if (layer) layer.classList.add(effect.cssClass);
            html.setAttribute('data-bg-effect', effect.id);
        }

        // Update header button
        const bgAnimBtn = document.getElementById('bg-anim-btn');
        if (bgAnimBtn) bgAnimBtn.style.opacity = '1';
    }
}

/** Render the Appearance tab content. Called when the tab is switched to. */
export function renderAppearanceTab(): void {
    const panel = document.getElementById('settings-panel-appearance');
    if (!panel) return;

    // Don't re-render if already populated
    if (panel.querySelector('.appearance-presets')) {
        _updatePresetSelection('style', _activeStyleId);
        _updatePresetSelection('bg', _activeBgEffectId);
        return;
    }

    // ── Style presets section ──
    const styleHtml = STYLE_PRESETS.map(p => {
        const active = p.id === _activeStyleId ? ' active' : '';
        return `<button class="ap-card${active}" data-preset-type="style" data-preset-id="${p.id}" onclick="applyStylePreset('${p.id}')">
            <div class="ap-card-preview" style="background:${p.dark.bgColor};border-color:${p.dark.borderColor}">
                <div class="ap-card-accent" style="background:${p.accent}"></div>
                <div class="ap-card-lines">
                    <span style="background:${p.dark.textColor}"></span>
                    <span style="background:${p.dark.textSecondary};width:70%"></span>
                    <span style="background:${p.dark.textMuted};width:45%"></span>
                </div>
            </div>
            <span class="ap-card-label" data-i18n="${p.nameKey}">${t(p.nameKey)}</span>
        </button>`;
    }).join('');

    // ── Background effects section ──
    const bgHtml = BG_EFFECT_PRESETS.map(e => {
        const active = e.id === _activeBgEffectId ? ' active' : '';
        return `<button class="ap-card ap-card-bg${active}" data-preset-type="bg" data-preset-id="${e.id}" onclick="applyBgEffect('${e.id}')">
            <div class="ap-bg-preview" data-effect="${e.id}">
                <div class="ap-bg-preview-inner"></div>
            </div>
            <span class="ap-card-label" data-i18n="${e.nameKey}">${t(e.nameKey)}</span>
        </button>`;
    }).join('');

    panel.innerHTML = `
        <div class="appearance-presets">
            <div class="form-group">
                <label data-i18n="appearance.style.label">${t('appearance.style.label')}</label>
                <small class="ap-hint" data-i18n="appearance.style.hint">${t('appearance.style.hint')}</small>
                <div class="ap-grid">${styleHtml}</div>
            </div>
            <div class="form-group" style="margin-top:1rem">
                <label data-i18n="appearance.bg.label">${t('appearance.bg.label')}</label>
                <small class="ap-hint" data-i18n="appearance.bg.hint">${t('appearance.bg.hint')}</small>
                <div class="ap-grid">${bgHtml}</div>
            </div>
        </div>`;
}

/** Return the currently active style preset ID. */
export function getActiveStylePreset(): string {
    return _activeStyleId;
}

/** Return the currently active bg effect preset ID. */
export function getActiveBgEffect(): string {
    return _activeBgEffectId;
}

// ─── Internal helpers ───────────────────────────────────────

/** Apply theme color CSS variables for the current active theme (dark/light). */
function _applyThemeVars(preset: StylePreset): void {
    const theme = document.documentElement.getAttribute('data-theme') || 'dark';
    const vars = theme === 'dark' ? preset.dark : preset.light;
    const root = document.documentElement.style;

    root.setProperty('--bg-color', vars.bgColor);
    root.setProperty('--bg-secondary', vars.bgSecondary);
    root.setProperty('--card-bg', vars.cardBg);
    root.setProperty('--text-color', vars.textColor);
    root.setProperty('--text-primary', vars.textColor);
    root.setProperty('--text-secondary', vars.textSecondary);
    root.setProperty('--text-muted', vars.textMuted);
    root.setProperty('--border-color', vars.borderColor);
    root.setProperty('--input-bg', vars.inputBg);
}

/** Ensure a Google Font stylesheet is loaded (idempotent). */
function _ensureFont(url: string, id: string): void {
    if (!url) return;
    const linkId = `gfont-${id}`;
    if (document.getElementById(linkId)) return;
    const link = document.createElement('link');
    link.id = linkId;
    link.rel = 'stylesheet';
    link.href = url;
    document.head.appendChild(link);
}

/** Update the visual selection ring on preset cards. */
function _updatePresetSelection(type: 'style' | 'bg', activeId: string): void {
    const attr = type === 'style' ? 'style' : 'bg';
    document.querySelectorAll(`[data-preset-type="${attr}"]`).forEach(el => {
        el.classList.toggle('active', (el as HTMLElement).dataset.presetId === activeId);
    });
}

// ─── Listen for theme changes to reapply preset colors ──────

document.addEventListener('themeChanged', () => {
    const preset = STYLE_PRESETS.find(p => p.id === _activeStyleId);
    if (preset && preset.id !== 'default') {
        _applyThemeVars(preset);
    }
});

// Also listen via MutationObserver on data-theme attribute
const _themeObserver = new MutationObserver((mutations) => {
    for (const m of mutations) {
        if (m.attributeName === 'data-theme') {
            const preset = STYLE_PRESETS.find(p => p.id === _activeStyleId);
            if (preset && preset.id !== 'default') {
                _applyThemeVars(preset);
            }
            // Sync shader theme
            const isDark = document.documentElement.getAttribute('data-theme') !== 'light';
            updateShaderTheme(isDark);
            break;
        }
    }
});
_themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
