// Generic in-canvas preview for schema-driven widgets. Slotted into
// CanvasNode's renderContent() `default` case, so ONLY widgets with no bespoke
// renderer reach it — the 39 existing widgets keep their own components.
//
// Each schema widget provides a small preview function here keyed by type.
// A widget with no entry falls back to a labeled placeholder (still fully
// selectable/movable/stylable — the placeholder is only the visual preview;
// the real frontend HTML is produced by the PHP generic element).
import React from 'react';
import type { CBNode, IconValue } from '../types';
import { getWidgetDef } from './registry';
import { RenderIconValue } from '../components/panels/IconPickerModal';

function str(v: unknown, fallback = ''): string { return v == null ? fallback : String(v); }
function num(v: unknown, fallback: number): number { const n = parseFloat(String(v)); return Number.isFinite(n) ? n : fallback; }

const PREVIEWS: Record<string, (c: Record<string, unknown>) => React.ReactNode> = {
  // Effects
  marquee: (c) => (
    <div style={{ overflow: 'hidden', whiteSpace: 'nowrap' }}>
      <span style={{ display: 'inline-block', paddingRight: num(c.itemSpacing, 0) || undefined }}>{str(c.text, 'Scrolling text…')}</span>
      <span style={{ marginLeft: 40, color: '#94a3b8', fontSize: 11 }}>(scrolls on frontend)</span>
    </div>
  ),
  'typewriter-text': (c) => {
    const first = str(c.words, 'Typewriter').split(/\r?\n/)[0] || 'Typewriter';
    const glyph = ({ bar: '|', underscore: '_', block: '▌' } as Record<string, string>)[str(c.cursorStyle, 'bar')] ?? '|';
    const cc = str(c.cursorColor);
    const cw = num(c.cursorWidth, 0);
    const caretStyle: React.CSSProperties = { opacity: 0.7 };
    if (cc) caretStyle.color = cc;
    if (cw > 0 && str(c.cursorStyle, 'bar') === 'bar') {
      caretStyle.display = 'inline-block'; caretStyle.width = cw; caretStyle.overflow = 'hidden';
      caretStyle.color = 'transparent'; caretStyle.background = cc || 'currentColor';
    }
    return <div style={{ fontWeight: 700 }}>{first}<span style={caretStyle}>{glyph}</span></div>;
  },
  'shape-divider': (c) => {
    const paths: Record<string, string> = {
      wave: 'M0,40 C300,90 900,-10 1200,40 L1200,120 L0,120 Z',
      tilt: 'M0,120 L1200,0 L1200,120 Z', curve: 'M0,120 C400,0 800,0 1200,120 Z', triangle: 'M0,120 L600,0 L1200,120 Z',
    };
    const wPct = num(c.widthPct, 100);
    const width = wPct > 100 && wPct <= 300 ? `${wPct}%` : '100%';
    return (
      <div style={{ overflow: width !== '100%' ? 'hidden' : undefined, lineHeight: 0 }}>
        <svg viewBox="0 0 1200 120" preserveAspectRatio="none" style={{ width, height: num(c.height, 60), display: 'block', fill: str(c.color, '#1a1a1a'), transform: c.flip ? 'scaleX(-1)' : undefined }}>
          <path d={paths[str(c.shape, 'wave')] || paths.wave} />
        </svg>
      </div>
    );
  },

  // Social
  'social-share': (c) => {
    const shape = str(c.shape, 'rounded');
    const outline = c.style === 'outline';

    // NEW model: icon+link+color items. When present, render icons.
    let items: Array<{ icon?: unknown; link?: string; color?: string }> = [];
    try { const v = JSON.parse(str(c.items, '[]')); if (Array.isArray(v)) items = v; } catch { /* none */ }
    if (items.length) {
      // Style-tab controls (mirror the PHP renderer so the preview matches).
      // dims4 turns a bare-number shorthand ("8" / "1 2 3 4") into px lengths.
      const dims4 = (v: string): string => {
        const parts = str(v).trim().split(/\s+/).filter(Boolean);
        if (!parts.length || parts.some((p) => isNaN(Number(p)))) return '';
        return parts.slice(0, 4).map((p) => `${Number(p)}px`).join(' ');
      };
      const sizeN = num(c.iconSize, NaN);
      const padN = num(c.iconPadding, NaN);
      const spacing = num(c.iconSpacing, 8);
      const rowsGap = num(c.iconRowsGap, 0);
      const bType = str(c.iconBorderType, 'default');
      const bWidth = dims4(str(c.iconBorderWidth));
      const bColor = str(c.iconBorderColor);
      const rdLen = dims4(str(c.iconBorderRadius));
      const radius = rdLen || (shape === 'square' ? '0' : shape === 'rounded' ? '8px' : '50%');
      const customMode = str(c.iconColorMode) === 'custom';
      const customColor = str(c.iconColor);
      const hoverCls = str(c.hoverAnimation, 'none') !== 'none' ? `jalpi-hvr jalpi-hvr--${str(c.hoverAnimation)}` : undefined;
      const box: React.CSSProperties = !isNaN(sizeN) || !isNaN(padN)
        ? { ...(isNaN(padN) ? {} : { padding: `${padN}em` }), ...(isNaN(sizeN) ? {} : { fontSize: `${sizeN}px` }) }
        : { width: 40, height: 40 };
      return (
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: `${rowsGap}px ${spacing}px` }}>
          {items.map((it, i) => {
            const col = (customMode && customColor) ? customColor : (str(it.color) || '#1a1a1a');
            const borderCss: React.CSSProperties = bType === 'none'
              ? { border: 'none' }
              : bType === 'default'
                ? { border: `2px solid ${col}` }
                : { borderStyle: bType, borderColor: bColor || col, borderWidth: bWidth || '1px' };
            return (
              <span key={i} className={hoverCls} style={{ ...box, ...borderCss, borderRadius: radius, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', background: outline ? 'transparent' : col, color: outline ? col : '#fff', lineHeight: 1 }}>
                <RenderIconValue value={it.icon as string | IconValue | undefined} style={{ width: '1em', height: '1em', display: 'inline-flex' }} />
              </span>
            );
          })}
        </div>
      );
    }

    const radius = shape === 'square' ? '0' : shape === 'rounded' ? '8px' : '50%';

    // LEGACY model: share networks (comma string). Preserved so existing
    // Social Share content still previews correctly.
    const colors: Record<string, string> = { facebook: '#1877f2', twitter: '#1da1f2', linkedin: '#0a66c2', whatsapp: '#25d366', youtube: '#ff0000', email: '#6b7280', pinterest: '#e60023', telegram: '#0088cc', reddit: '#ff4500' };
    const glyphs: Record<string, string> = { facebook: 'f', twitter: '𝕏', linkedin: 'in', whatsapp: '⌾', youtube: '▶', email: '✉', pinterest: 'P', telegram: '➤', reddit: 'r' };
    const custom: Record<string, string> = {};
    str(c.networkColors).split(',').forEach((p) => { const [k, v] = p.split(':').map((s) => s.trim()); if (k && v) custom[k] = v; });
    const nets = str(c.networks, '').split(',').map((s) => s.trim()).filter((n) => colors[n]);
    if (!nets.length) return <div style={{ color: '#94a3b8', fontSize: 13 }}>Add a social icon</div>;
    return (
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
        {nets.map((n, i) => {
          const col = custom[n] || colors[n];
          return <span key={`${n}-${i}`} style={{ width: 40, height: 40, borderRadius: radius, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, fontSize: 15, border: `2px solid ${col}`, background: outline ? 'transparent' : col, color: outline ? col : '#fff' }}>{glyphs[n]}</span>;
        })}
      </div>
    );
  },
  'whatsapp-button': (c) => (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, padding: '10px 18px', borderRadius: 8, background: str(c.bgColor, '#25d366'), color: '#fff', fontWeight: 600 }}>
      💬 {str(c.label, 'Chat on WhatsApp')}{c.floating ? ' (floating)' : ''}
    </span>
  ),
  'youtube-gallery': (c) => {
    const lines = str(c.videos).split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
    const ids = lines.map((l) => {
      if (/^[A-Za-z0-9_-]{11}$/.test(l)) return l;
      const m = l.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|shorts\/)|youtu\.be\/)([A-Za-z0-9_-]{11})/);
      return m ? m[1] : '';
    }).filter(Boolean);
    const cols = Math.max(1, Math.min(6, num(c.columns, 3)));
    if (!ids.length) return <div style={{ color: '#94a3b8', fontSize: 13 }}>Add YouTube video IDs / URLs</div>;
    return (
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${cols}, 1fr)`, gap: 12 }}>
        {ids.map((id, i) => (
          <span key={i} style={{ position: 'relative', display: 'block', aspectRatio: '16/9', borderRadius: 8, overflow: 'hidden', background: '#000' }}>
            <img src={`https://i.ytimg.com/vi/${id}/hqdefault.jpg`} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', opacity: 0.85 }} />
            <span style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 28, color: '#fff' }}>▶</span>
          </span>
        ))}
      </div>
    );
  },
  'facebook-feed': (c) => (
    <div style={{ border: '1px solid #dbe4ef', borderRadius: 8, padding: 16, color: '#385898', maxWidth: 500 }}>
      <strong>📘 Facebook Post</strong>
      <div style={{ fontSize: 12, color: '#64748b', marginTop: 4, wordBreak: 'break-all' }}>{str(c.postUrl) || 'Add a public Facebook post URL — embeds on the live page.'}</div>
    </div>
  ),
  'instagram-feed': (c) => (
    <div style={{ border: '1px solid #eadcec', borderRadius: 8, padding: 16, color: '#c13584', maxWidth: 500 }}>
      <strong>📸 Instagram Post</strong>
      <div style={{ fontSize: 12, color: '#64748b', marginTop: 4, wordBreak: 'break-all' }}>{str(c.postUrl) || 'Add a public Instagram post URL — embeds on the live page.'}</div>
    </div>
  ),

  // Icon Box — renders the actual chosen icon via the shared icon renderer.
  'icon-box': (c) => {
    const size = Math.max(8, Math.min(400, num(c.size, 48)));
    const align = (str(c.align, 'center') as 'left' | 'center' | 'right');
    const color = str(c.color) || undefined;
    return (
      <div className="jalpi-iconbox-preview" style={{ textAlign: align, ['--jalpi-ib-size' as string]: `${size}px` }}>
        <span style={{ display: 'inline-flex', width: size, height: size, color }}>
          <RenderIconValue value={c.icon as string | IconValue | undefined} style={{ width: size, height: size, display: 'inline-flex' }} />
        </span>
        {str(c.title) ? <div style={{ fontWeight: 700, marginTop: 8 }}>{str(c.title)}</div> : null}
        {str(c.text) ? <div style={{ marginTop: 4, opacity: 0.85 }}>{str(c.text)}</div> : null}
      </div>
    );
  },
};

export function GenericRenderer({ node, inlineStyles }: { node: CBNode; inlineStyles: React.CSSProperties }) {
  const def = getWidgetDef(node.type);
  const content = (node.content ?? {}) as Record<string, unknown>;
  const preview = PREVIEWS[node.type];

  return (
    <div style={inlineStyles}>
      {preview ? preview(content) : (
        <div style={{ padding: '12px 16px', border: '1px dashed #cbd5e1', borderRadius: 6, color: '#64748b', fontSize: 13 }}>
          <strong>{def?.label ?? node.type}</strong>
          <div style={{ fontSize: 11, marginTop: 2 }}>{def?.description ?? 'Widget preview'}</div>
        </div>
      )}
    </div>
  );
}
