/**
 * Color Strip Sources — Card rendering for the streams list.
 * Extracted from color-strips.ts to reduce file size.
 */

import { escapeHtml } from '../../core/api.ts';
import {
    _cachedSyncClocks, _cachedCSPTemplates, _cachedWeatherSources,
    colorStripSourcesCache, gradientsCache, GradientEntity,
} from '../../core/state.ts';
import { t } from '../../core/i18n.ts';
import {
    getColorStripIcon, getPictureSourceIcon,
    ICON_CLONE, ICON_EDIT, ICON_CALIBRATION, ICON_OVERLAY,
    ICON_LED, ICON_PALETTE, ICON_FPS, ICON_MAP_PIN, ICON_MUSIC,
    ICON_AUDIO_LOOPBACK, ICON_TIMER, ICON_LINK_SOURCE, ICON_FILM,
    ICON_LINK, ICON_SPARKLES, ICON_ACTIVITY, ICON_CLOCK, ICON_BELL, ICON_TEST,
    ICON_AUTOMATION, ICON_FAST_FORWARD, ICON_THERMOMETER, ICON_PATTERN_TEMPLATE,
} from '../../core/icons.ts';
import { wrapCard } from '../../core/card-colors.ts';
import type { ColorStripSource } from '../../types.ts';
import { bindableValue, bindableColor } from '../../types.ts';
import { renderTagChips } from '../../core/tag-input.ts';
import { rgbArrayToHex } from '../css-gradient-editor.ts';

/* ── Types ────────────────────────────────────────────────────── */

type CardPropsRenderer = (source: ColorStripSource, opts: {
    clockBadge: string;
    animBadge: string;
    audioSourceMap: Record<string, any>;
    pictureSourceMap: Record<string, any>;
}) => string;

/* ── Gradient helpers ─────────────────────────────────────────── */

function _getGradients(): GradientEntity[] {
    return gradientsCache.data || [];
}

function _gradientEntityStripHTML(stops: Array<{ position: number; color: number[] }>, w = 80, h = 16) {
    const css = stops.map(s => `rgb(${s.color.join(',')}) ${(s.position * 100).toFixed(0)}%`).join(', ');
    return `<span style="display:inline-block;width:${w}px;height:${h}px;border-radius:3px;background:linear-gradient(to right,${css});flex-shrink:0"></span>`;
}

/* ── Non-picture types set ────────────────────────────────────── */

const NON_PICTURE_TYPES = new Set([
    'static', 'gradient', 'color_cycle', 'effect', 'composite', 'mapped',
    'audio', 'api_input', 'notification', 'daylight', 'candlelight', 'weather', 'processed', 'key_colors',
    'math_wave',
]);

/* ── Per-type card property renderers ─────────────────────────── */

const CSS_CARD_RENDERERS: Record<string, CardPropsRenderer> = {
    static: (source, { clockBadge, animBadge }) => {
        const hexColor = rgbArrayToHex(bindableColor(source.color, [255,255,255]));
        return `
            <span class="stream-card-prop" title="${t('color_strip.static_color')}">
                <span style="display:inline-block;width:14px;height:14px;background:${hexColor};border:1px solid #888;border-radius:2px;vertical-align:middle;margin-right:4px"></span>${hexColor.toUpperCase()}
            </span>
            ${animBadge}
            ${clockBadge}
        `;
    },
    color_cycle: (source, { clockBadge }) => {
        const colors = source.colors || [];
        const swatches = colors.slice(0, 8).map((c: any) =>
            `<span style="display:inline-block;width:12px;height:12px;background:${rgbArrayToHex(c)};border:1px solid #888;border-radius:2px;margin-right:2px"></span>`
        ).join('');
        return `
            <span class="stream-card-prop">${swatches}</span>
            ${clockBadge}
        `;
    },
    gradient: (source, { clockBadge, animBadge }) => {
        const stops = source.stops || [];
        const sortedStops = [...stops].sort((a, b) => a.position - b.position);
        let cssGradient = '';
        if (sortedStops.length >= 2) {
            const parts: string[] = [];
            sortedStops.forEach(s => {
                const pct = Math.round(s.position * 100);
                parts.push(`${rgbArrayToHex(s.color)} ${pct}%`);
                if (s.color_right) parts.push(`${rgbArrayToHex(s.color_right)} ${pct}%`);
            });
            cssGradient = `linear-gradient(to right, ${parts.join(', ')})`;
        }
        return `
            ${cssGradient ? `<span style="flex:1 1 100%;height:12px;background:${cssGradient};border-radius:3px;border:1px solid rgba(128,128,128,0.3)"></span>` : ''}
            <span class="stream-card-prop">${ICON_PALETTE} ${stops.length} ${t('color_strip.gradient.stops_count')}</span>
            ${animBadge}
            ${clockBadge}
        `;
    },
    effect: (source, { clockBadge }) => {
        const effectLabel = t('color_strip.effect.' + (source.effect_type || 'fire')) || source.effect_type || 'fire';
        const paletteLabel = source.palette ? (t('color_strip.palette.' + source.palette) || source.palette) : '';
        return `
            <span class="stream-card-prop">${ICON_FPS} ${escapeHtml(effectLabel)}</span>
            ${paletteLabel ? `<span class="stream-card-prop" title="${t('color_strip.effect.palette')}">${ICON_PALETTE} ${escapeHtml(paletteLabel)}</span>` : ''}
            ${clockBadge}
        `;
    },
    composite: (source) => {
        const layerCount = (source.layers || []).length;
        const enabledCount = (source.layers || []).filter((l: any) => l.enabled !== false).length;
        return `
            <span class="stream-card-prop">${ICON_LINK} ${enabledCount}/${layerCount} ${t('color_strip.composite.layers_count')}</span>
            ${source.led_count ? `<span class="stream-card-prop" title="${t('color_strip.leds')}">${ICON_LED} ${source.led_count}</span>` : ''}
        `;
    },
    mapped: (source) => {
        const zoneCount = (source.zones || []).length;
        return `
            <span class="stream-card-prop">${ICON_MAP_PIN} ${zoneCount} ${t('color_strip.mapped.zones_count')}</span>
            ${source.led_count ? `<span class="stream-card-prop" title="${t('color_strip.leds')}">${ICON_LED} ${source.led_count}</span>` : ''}
        `;
    },
    audio: (source, { audioSourceMap }) => {
        const vizLabel = t('color_strip.audio.viz.' + (source.visualization_mode || 'spectrum')) || source.visualization_mode || 'spectrum';
        const vizMode = source.visualization_mode || 'spectrum';
        const showPalette = (vizMode === 'spectrum' || vizMode === 'beat_pulse' || vizMode === 'energy_gradient' || vizMode === 'spectrum_bands') && source.palette;
        const audioPaletteLabel = showPalette ? (t('color_strip.palette.' + source.palette) || source.palette) : '';
        return `
            <span class="stream-card-prop">${ICON_MUSIC} ${escapeHtml(vizLabel)}</span>
            ${audioPaletteLabel ? `<span class="stream-card-prop" title="${t('color_strip.audio.palette')}">${ICON_PALETTE} ${escapeHtml(audioPaletteLabel)}</span>` : ''}
            ${source.audio_source_id ? (() => {
                const as = audioSourceMap && audioSourceMap[source.audio_source_id];
                const asName = as ? as.name : source.audio_source_id;
                const asSection = as ? (as.source_type === 'processed' ? 'audio-processed' : 'audio-capture') : 'audio-capture';
                const asTab = as ? (as.source_type === 'processed' ? 'audio_processed' : 'audio_capture') : 'audio_capture';
                return `<span class="stream-card-prop${as ? ' stream-card-link' : ''}" title="${t('color_strip.audio.source')}"${as ? ` onclick="event.stopPropagation(); navigateToCard('streams','${asTab}','${asSection}','data-id','${source.audio_source_id}')"` : ''}>${ICON_AUDIO_LOOPBACK} ${escapeHtml(asName)}</span>`;
            })() : ''}
            ${source.mirror ? `<span class="stream-card-prop">\u{1FA9E}</span>` : ''}
        `;
    },
    api_input: (source) => {
        const fbColor = rgbArrayToHex(bindableColor(source.fallback_color, [0, 0, 0]));
        const timeoutVal = bindableValue(source.timeout, 5.0).toFixed(1);
        const interpLabel = t('color_strip.api_input.interpolation.' + (source.interpolation || 'linear')) || source.interpolation || 'linear';
        return `
            <span class="stream-card-prop" title="${t('color_strip.api_input.fallback_color')}">
                <span style="display:inline-block;width:14px;height:14px;background:${fbColor};border:1px solid #888;border-radius:2px;vertical-align:middle;margin-right:4px"></span>${fbColor.toUpperCase()}
            </span>
            <span class="stream-card-prop" title="${t('color_strip.api_input.timeout')}">${ICON_TIMER} ${timeoutVal}s</span>
            <span class="stream-card-prop" title="${t('color_strip.api_input.interpolation')}">${escapeHtml(interpLabel)}</span>
        `;
    },
    notification: (source) => {
        const effectLabel = t('color_strip.notification.effect.' + (source.notification_effect || 'flash')) || source.notification_effect || 'flash';
        const durationVal = source.duration_ms || 1500;
        const defColorRgb = bindableColor(source.default_color as any, [255, 255, 255]);
        const defColorHex = rgbArrayToHex(defColorRgb);
        const appCount = source.app_colors ? Object.keys(source.app_colors).length : 0;
        return `
            <span class="stream-card-prop" title="${t('color_strip.notification.effect')}">${ICON_BELL} ${escapeHtml(effectLabel)}</span>
            <span class="stream-card-prop" title="${t('color_strip.notification.duration')}">${ICON_TIMER} ${durationVal}ms</span>
            <span class="stream-card-prop" title="${t('color_strip.notification.default_color')}">
                <span style="display:inline-block;width:14px;height:14px;background:${defColorHex};border:1px solid #888;border-radius:2px;vertical-align:middle;margin-right:4px"></span>${defColorHex.toUpperCase()}
            </span>
            ${appCount > 0 ? `<span class="stream-card-prop">${ICON_PALETTE} ${appCount} ${t('color_strip.notification.app_count')}</span>` : ''}
        `;
    },
    daylight: (source, { clockBadge }) => {
        const useRealTime = source.use_real_time;
        const speedVal = bindableValue(source.speed, 1.0).toFixed(1);
        return `
            <span class="stream-card-prop">${useRealTime ? ICON_CLOCK + ' ' + t('color_strip.daylight.real_time') : ICON_FAST_FORWARD + ' ' + speedVal + 'x'}</span>
            ${clockBadge}
        `;
    },
    candlelight: (source, { clockBadge }) => {
        const hexColor = rgbArrayToHex(bindableColor(source.color, [255, 147, 41]));
        const numCandles = source.num_candles ?? 3;
        return `
            <span class="stream-card-prop" title="${t('color_strip.candlelight.color')}">
                <span style="display:inline-block;width:14px;height:14px;background:${hexColor};border:1px solid #888;border-radius:2px;vertical-align:middle;margin-right:4px"></span>${hexColor.toUpperCase()}
            </span>
            <span class="stream-card-prop">${numCandles} ${t('color_strip.candlelight.num_candles')}</span>
            ${clockBadge}
        `;
    },
    weather: (source, { clockBadge }) => {
        const speedVal = bindableValue(source.speed, 1.0).toFixed(1);
        const tempInfl = bindableValue(source.temperature_influence, 0.5).toFixed(1);
        const ws = (_cachedWeatherSources || []).find((w: any) => w.id === source.weather_source_id);
        const wsName = ws?.name || '\u2014';
        const wsLink = ws
            ? ` stream-card-link" onclick="event.stopPropagation(); navigateToCard('integrations','weather','weather-sources','data-id','${source.weather_source_id}')`
            : '';
        return `
            <span class="stream-card-prop${wsLink}" title="${t('color_strip.weather.source')}">${ICON_LINK_SOURCE} ${escapeHtml(wsName)}</span>
            <span class="stream-card-prop">${ICON_FAST_FORWARD} ${speedVal}x</span>
            <span class="stream-card-prop">${ICON_THERMOMETER} ${tempInfl}</span>
            ${clockBadge}
        `;
    },
    key_colors: (source, { pictureSourceMap }) => {
        const rectCount = (source.rectangles || []).length;
        const mode = source.interpolation_mode || 'average';
        const ps = pictureSourceMap && source.picture_source_id ? pictureSourceMap[source.picture_source_id] : null;
        const psName = ps?.name || '\u2014';
        const psLink = ps
            ? ` stream-card-link" onclick="event.stopPropagation(); navigateToCard('streams','raw','raw-streams','data-stream-id','${source.picture_source_id}')`
            : '';
        return `
            <span class="stream-card-prop${psLink}" title="${t('color_strip.key_colors.picture_source')}">${ICON_LINK_SOURCE} ${escapeHtml(psName)}</span>
            <span class="stream-card-prop">${ICON_PALETTE} ${rectCount} region${rectCount !== 1 ? 's' : ''}</span>
            <span class="stream-card-prop">${mode}</span>
        `;
    },
    math_wave: (source, { clockBadge }) => {
        const waveCount = (source.waves || []).length;
        const speedVal = bindableValue(source.speed, 1.0).toFixed(1);
        const gr = source.gradient_id ? _getGradients().find(g => g.id === source.gradient_id) : null;
        const grName = gr?.name || '\u2014';
        return `
            <span class="stream-card-prop" title="${t('color_strip.math_wave.waves')}">${ICON_ACTIVITY} ${waveCount} wave${waveCount !== 1 ? 's' : ''}</span>
            <span class="stream-card-prop" title="${t('color_strip.math_wave.gradient')}">${ICON_PALETTE} ${escapeHtml(grName)}</span>
            <span class="stream-card-prop">${ICON_FAST_FORWARD} ${speedVal}x</span>
            ${clockBadge}
        `;
    },
    processed: (source) => {
        const inputSrc = ((colorStripSourcesCache.data || []) as any[]).find(s => s.id === source.input_source_id);
        const inputName = inputSrc?.name || source.input_source_id || '\u2014';
        const tplName = source.processing_template_id
            ? (_cachedCSPTemplates.find(t => t.id === source.processing_template_id)?.name || source.processing_template_id)
            : '\u2014';
        return `
            <span class="stream-card-prop" title="${t('color_strip.processed.input')}">${ICON_LINK_SOURCE} ${escapeHtml(inputName)}</span>
            <span class="stream-card-prop" title="${t('color_strip.processed.template')}">${ICON_SPARKLES} ${escapeHtml(tplName)}</span>
        `;
    },
    picture_advanced: (source, { pictureSourceMap }) => {
        const cal = source.calibration ?? {} as Partial<import('../../types.ts').Calibration>;
        const lines = cal.lines || [];
        const totalLeds = lines.reduce((s: any, l: any) => s + (l.led_count || 0), 0);
        const ledCount = (source.led_count > 0) ? source.led_count : totalLeds;
        const psIds: any[] = [...new Set(lines.map((l: any) => l.picture_source_id).filter(Boolean))];
        const psNames = psIds.map((id: any) => {
            const ps = pictureSourceMap && pictureSourceMap[id];
            return ps ? ps.name : id;
        });
        return `
            <span class="stream-card-prop" title="${t('calibration.advanced.lines_title')}">${ICON_MAP_PIN} ${lines.length} ${t('calibration.advanced.lines_title').toLowerCase()}</span>
            ${ledCount ? `<span class="stream-card-prop" title="${t('color_strip.leds')}">${ICON_LED} ${ledCount}</span>` : ''}
            ${psNames.length ? `<span class="stream-card-prop stream-card-prop-full" title="${t('color_strip.picture_source')}">${ICON_LINK_SOURCE} ${escapeHtml(psNames.join(', '))}</span>` : ''}
        `;
    },
};

/* ── Fallback picture renderer ────────────────────────────────── */

function _renderPictureCardProps(source: ColorStripSource, pictureSourceMap: Record<string, any>): string {
    const ps = pictureSourceMap && pictureSourceMap[source.picture_source_id!];
    const srcName = ps ? ps.name : source.picture_source_id || '\u2014';
    const cal = source.calibration ?? {} as Partial<import('../../types.ts').Calibration>;
    const calLeds = (cal.leds_top || 0) + (cal.leds_right || 0) + (cal.leds_bottom || 0) + (cal.leds_left || 0);
    const ledCount = (source.led_count > 0) ? source.led_count : calLeds;
    let psSubTab = 'raw', psSection = 'raw-streams';
    if (ps) {
        if (ps.stream_type === 'static_image') { psSubTab = 'static_image'; psSection = 'static-streams'; }
        else if (ps.stream_type === 'processed') { psSubTab = 'processed'; psSection = 'proc-streams'; }
    }
    return `
        <span class="stream-card-prop stream-card-prop-full${ps ? ' stream-card-link' : ''}" title="${t('color_strip.picture_source')}"${ps ? ` onclick="event.stopPropagation(); navigateToCard('streams','${psSubTab}','${psSection}','data-stream-id','${source.picture_source_id}')"` : ''}>${ICON_LINK_SOURCE} ${escapeHtml(srcName)}</span>
        ${ledCount ? `<span class="stream-card-prop" title="${t('color_strip.leds')}">${ICON_LED} ${ledCount}</span>` : ''}
    `;
}

/* ── Main card builder ────────────────────────────────────────── */

export function createColorStripCard(source: ColorStripSource, pictureSourceMap: Record<string, any>, audioSourceMap: Record<string, any>) {
    // Clock crosslink badge
    const clockObj = source.clock_id ? _cachedSyncClocks.find(c => c.id === source.clock_id) : null;
    const clockBadge = clockObj
        ? `<span class="stream-card-prop stream-card-link" title="${t('color_strip.clock')}" onclick="event.stopPropagation(); navigateToCard('streams','sync','sync-clocks','data-id','${source.clock_id}')">${ICON_CLOCK} ${escapeHtml(clockObj.name)}</span>`
        : source.clock_id ? `<span class="stream-card-prop">${ICON_CLOCK} ${source.clock_id}</span>` : '';

    const isAnimatable = source.source_type === 'static' || source.source_type === 'gradient';
    const anim = isAnimatable && source.animation && source.animation.enabled ? source.animation : null;
    const animBadge = anim
        ? `<span class="stream-card-prop" title="${t('color_strip.animation')}">${ICON_SPARKLES} ${t('color_strip.animation.type.' + anim.type) || anim.type}</span>`
        : '';

    const renderer = CSS_CARD_RENDERERS[source.source_type];
    const propsHtml = renderer
        ? renderer(source, { clockBadge, animBadge, audioSourceMap, pictureSourceMap })
        : _renderPictureCardProps(source, pictureSourceMap);

    const icon = getColorStripIcon(source.source_type);
    const isNotification = source.source_type === 'notification';
    const isPictureKind = !NON_PICTURE_TYPES.has(source.source_type);
    const calibrationBtn = isPictureKind
        ? `<button class="btn btn-icon btn-secondary" onclick="${source.source_type === 'picture_advanced' ? `showAdvancedCalibration('${source.id}')` : `showCSSCalibration('${source.id}')`}" title="${t('calibration.title')}">${ICON_CALIBRATION}</button>`
        : '';
    const overlayBtn = isPictureKind
        ? `<button class="btn btn-icon btn-secondary" onclick="event.stopPropagation(); toggleCSSOverlay('${source.id}')" title="${t('overlay.toggle')}">${ICON_OVERLAY}</button>`
        : '';
    const testNotifyBtn = isNotification
        ? `<button class="btn btn-icon btn-secondary" onclick="event.stopPropagation(); testNotification('${source.id}')" title="${t('color_strip.notification.test')}">${ICON_BELL}</button>`
        : '';
    const notifHistoryBtn = isNotification
        ? `<button class="btn btn-icon btn-secondary" onclick="event.stopPropagation(); showNotificationHistory()" title="${t('color_strip.notification.history.title')}">${ICON_AUTOMATION}</button>`
        : '';
    const isKeyColors = source.source_type === 'key_colors';
    const regionsBtn = isKeyColors
        ? `<button class="btn btn-icon btn-secondary" onclick="event.stopPropagation(); configureKCRegions('${source.id}')" title="${t('color_strip.key_colors.configure_regions')}">${ICON_PATTERN_TEMPLATE}</button>`
        : '';
    const testPreviewBtn = `<button class="btn btn-icon btn-secondary" onclick="event.stopPropagation(); testColorStrip('${source.id}')" title="${t('color_strip.test.title')}">${ICON_TEST}</button>`;

    return wrapCard({
        dataAttr: 'data-css-id',
        id: source.id,
        removeOnclick: `deleteColorStrip('${source.id}')`,
        removeTitle: t('common.delete'),
        content: `
            <div class="card-header">
                <div class="card-title" title="${escapeHtml(source.name)}">
                    ${icon} <span class="card-title-text">${escapeHtml(source.name)}</span>
                </div>
            </div>
            <div class="stream-card-props">
                ${propsHtml}
            </div>
            ${renderTagChips(source.tags)}`,
        actions: `
                <button class="btn btn-icon btn-secondary" onclick="cloneColorStrip('${source.id}')" title="${t('common.clone')}">${ICON_CLONE}</button>
                <button class="btn btn-icon btn-secondary" onclick="showCSSEditor('${source.id}')" title="${t('common.edit')}">${ICON_EDIT}</button>
                ${calibrationBtn}${overlayBtn}${regionsBtn}${testNotifyBtn}${notifHistoryBtn}${testPreviewBtn}`,
    });
}
