/**
 * 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, _cachedValueSources,
    _cachedAssets, colorStripSourcesCache, gradientsCache, GradientEntity,
} from '../../core/state.ts';
import { t } from '../../core/i18n.ts';
import {
    getColorStripIcon, getPictureSourceIcon, getValueSourceIcon,
    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 { ModCardOpts } from '../../core/mod-card.ts';
import type { BindableColor, ColorStripSource } from '../../types.ts';
import { bindableValue, bindableColor, bindableColorSourceId } from '../../types.ts';
import { renderTagChips } from '../../core/tag-input.ts';
import { rgbArrayToHex } from '../css-gradient-editor.ts';
import { makeCardIconFields } from '../../core/card-icon.ts';
import { registerIconEntityType, makeSimpleIconAdapter } from '../icon-picker.ts';

registerIconEntityType('color_strip_source', makeSimpleIconAdapter<ColorStripSource>({
    cache: colorStripSourcesCache,
    endpointPrefix: '/color-strip-sources',
    reload: async () => {
        colorStripSourcesCache.invalidate();
        if (typeof window.loadPictureSources === 'function') {
            await window.loadPictureSources();
        }
    },
    typeLabelKey: 'device.icon.entity.color_strip_source',
    typeLabelFallback: 'Color strip',
    cardSelectors: (id) => [`[data-css-id="${CSS.escape(id)}"]`],
    bodyExtras: (rec) => ({ source_type: (rec as any)?.source_type ?? 'single_color' }),
}));

/* ── 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 || [];
}

/**
 * Render a bindable color as either a static color swatch or a crosslink
 * badge to the bound value source. Falls back to the static hex badge
 * when the color is not bound (no `source_id`).
 */
function _bindableColorBadge(
    color: BindableColor | undefined,
    fallback: number[],
    title: string,
): string {
    const sourceId = bindableColorSourceId(color);
    if (sourceId) {
        const vs = (_cachedValueSources || []).find(v => v.id === sourceId);
        if (vs) {
            return `<span class="stream-card-prop stream-card-link" title="${title}" onclick="event.stopPropagation(); navigateToCard('streams','value','value-sources','data-id','${sourceId}')">${getValueSourceIcon(vs.source_type)} ${escapeHtml(vs.name)}</span>`;
        }
        return `<span class="stream-card-prop" title="${title}">${ICON_LINK_SOURCE} ${escapeHtml(sourceId)}</span>`;
    }
    const hex = rgbArrayToHex(bindableColor(color, fallback));
    return `<span class="stream-card-prop" title="${title}">
                <span style="display:inline-block;width:14px;height:14px;background:${hex};border:1px solid #888;border-radius:2px;vertical-align:middle;margin-right:4px"></span>${hex.toUpperCase()}
            </span>`;
}

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([
    'single_color', 'gradient', '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> = {
    single_color: (source, { clockBadge, animBadge }) => {
        const colorBadge = _bindableColorBadge(source.color, [255, 255, 255], t('color_strip.single_color'));
        return `
            ${colorBadge}
            ${animBadge}
            ${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 fbBadge = _bindableColorBadge(source.fallback_color, [0, 0, 0], t('color_strip.api_input.fallback_color'));
        const timeoutVal = bindableValue(source.timeout, 5.0).toFixed(1);
        const interpLabel = t('color_strip.api_input.interpolation.' + (source.interpolation || 'linear')) || source.interpolation || 'linear';
        return `
            ${fbBadge}
            <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;
        const soundBadge = (() => {
            if (!source.sound_asset_id) return '';
            const assetId = source.sound_asset_id;
            const asset = (_cachedAssets || []).find(a => a.id === assetId);
            const name = asset?.name || assetId;
            const linkCls = asset ? ' stream-card-link' : '';
            const onclick = asset
                ? ` onclick="event.stopPropagation(); navigateToCard('streams','assets','assets','data-id','${assetId}')"`
                : '';
            return `<span class="stream-card-prop${linkCls}" title="${t('color_strip.notification.sound')}"${onclick}>${ICON_MUSIC} ${escapeHtml(name)}</span>`;
        })();
        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>
            ${soundBadge}
            ${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 colorBadge = _bindableColorBadge(source.color, [255, 147, 41], t('color_strip.candlelight.color'));
        const numCandles = source.num_candles ?? 3;
        return `
            ${colorBadge}
            <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 ────────────────────────────────────────── */

const STRIP_BADGE: Record<string, string> = {
    single_color: 'STRIP · COLOR',
    gradient: 'STRIP · GRD',
    effect: 'STRIP · FX',
    composite: 'STRIP · COMP',
    mapped: 'STRIP · MAP',
    audio: 'STRIP · AUDIO',
    api_input: 'STRIP · API',
    notification: 'STRIP · NOTIF',
    daylight: 'STRIP · DAY',
    candlelight: 'STRIP · CANDLE',
    weather: 'STRIP · WEATHER',
    key_colors: 'STRIP · KEY',
    math_wave: 'STRIP · WAVE',
    processed: 'STRIP · OUT',
    picture_advanced: 'STRIP · CALIB',
};

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 === 'single_color' || 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 ledCount = (source as any).led_count || 0;
    const badgeText = STRIP_BADGE[source.source_type] || 'STRIP · MAPPED';
    const metaText = ledCount ? `${ledCount} px` : (source.source_type || '').replace(/_/g, ' ');

    const isNotification = source.source_type === 'notification';
    const isPictureKind = !NON_PICTURE_TYPES.has(source.source_type);
    const isKeyColors = source.source_type === 'key_colors';

    const iconActions: any[] = [];
    iconActions.push({ icon: ICON_TEST, onclick: `event.stopPropagation(); testColorStrip('${source.id}')`, title: t('color_strip.test.title') });
    if (isPictureKind) {
        const calibrationOnclick = source.source_type === 'picture_advanced'
            ? `showAdvancedCalibration('${source.id}')`
            : `showCSSCalibration('${source.id}')`;
        iconActions.push({ icon: ICON_CALIBRATION, onclick: calibrationOnclick, title: t('calibration.title') });
        iconActions.push({ icon: ICON_OVERLAY, onclick: `event.stopPropagation(); toggleCSSOverlay('${source.id}')`, title: t('overlay.toggle') });
    }
    if (isKeyColors) {
        iconActions.push({ icon: ICON_PATTERN_TEMPLATE, onclick: `event.stopPropagation(); configureKCRegions('${source.id}')`, title: t('color_strip.key_colors.configure_regions') });
    }
    if (isNotification) {
        iconActions.push({ icon: ICON_BELL, onclick: `event.stopPropagation(); testNotification('${source.id}')`, title: t('color_strip.notification.test') });
        iconActions.push({ icon: ICON_AUTOMATION, onclick: `event.stopPropagation(); showNotificationHistory()`, title: t('color_strip.notification.history.title') });
    }
    iconActions.push({ icon: ICON_EDIT, onclick: `showCSSEditor('${source.id}')`, title: t('common.edit') });

    const mod: ModCardOpts = {
        head: {
            badge: { text: badgeText },
            name: source.name,
            metaHtml: escapeHtml(metaText),
            leds: ['off'],
            ...makeCardIconFields('color_strip_source', source.id, source),
            menu: {
                duplicateOnclick: `cloneColorStrip('${source.id}')`,
                hideOnclick: `toggleCardHidden('color-strips','${source.id}')`,
                deleteOnclick: `deleteColorStrip('${source.id}')`,
            },
        },
        body: {
            extraHtml: propsHtml ? `<div class="stream-card-props">${propsHtml}</div>` : undefined,
        },
        foot: {
            patchState: 'idle',
            patchLabel: t('patch.strip'),
            iconActions,
        },
    };
    const cardHtml = wrapCard({ dataAttr: 'data-css-id', id: source.id, mod });
    const tagsHtml = renderTagChips(source.tags);
    return tagsHtml ? cardHtml.replace(/<\/div>\s*$/, `${tagsHtml}</div>`) : cardHtml;
}
