// Meta Ads Flow — Shared Components
// Loaded via <script type="text/babel" src="components.jsx">

// ── Lucide icon helper ─────────────────────────────────────
const Icon = ({ name, size = 16, color = 'currentColor', strokeWidth = 1.6 }) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (ref.current && window.lucide) {
      ref.current.innerHTML = '';
      const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
      svg.setAttribute('width', size);
      svg.setAttribute('height', size);
      svg.setAttribute('viewBox', '0 0 24 24');
      svg.setAttribute('fill', 'none');
      svg.setAttribute('stroke', color);
      svg.setAttribute('stroke-width', strokeWidth);
      svg.setAttribute('stroke-linecap', 'round');
      svg.setAttribute('stroke-linejoin', 'round');
      const iconData = window.lucide[name.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/-(\d)/g, '$1')];
      if (iconData) {
        iconData[2].forEach(([tag, attrs]) => {
          const el = document.createElementNS('http://www.w3.org/2000/svg', tag);
          Object.entries(attrs).forEach(([k, v]) => el.setAttribute(k, v));
          svg.appendChild(el);
        });
      }
      ref.current.appendChild(svg);
    }
  }, [name, size, color, strokeWidth]);
  return <span ref={ref} style={{ display: 'inline-flex', alignItems: 'center', flexShrink: 0 }} />;
};

// ── Button ─────────────────────────────────────────────────
const Btn = ({ children, variant = 'secondary', size = 'md', icon, iconRight, onClick, style: extraStyle, title, disabled }) => {
  const [pressed, setPressed] = React.useState(false);
  const sizes = {
    sm: { h: 28, fs: 11.5, pad: '0 11px', gap: 5, iconSize: 13 },
    md: { h: 34, fs: 12.5, pad: '0 14px', gap: 6, iconSize: 14 },
    lg: { h: 40, fs: 13.5, pad: '0 18px', gap: 7, iconSize: 16 },
  }[size];
  const variants = {
    primary: { background: 'var(--accent-cyan)', color: 'var(--fg-on-accent)', border: '1px solid var(--accent-cyan)' },
    secondary: { background: 'var(--bg-surface)', color: 'var(--fg-primary)', border: '1px solid var(--border)' },
    ghost: { background: 'transparent', color: 'var(--fg-secondary)', border: '1px solid transparent' },
    danger: { background: 'rgba(248,113,113,0.12)', color: 'var(--error)', border: '1px solid rgba(248,113,113,0.25)' },
    accent: {
      background: 'linear-gradient(135deg,#7B4FD4,#2E9DF7)',
      color: '#fff', border: '1px solid transparent',
    },
  };
  return (
    <button
      title={title}
      disabled={disabled}
      onMouseDown={() => setPressed(true)}
      onMouseUp={() => setPressed(false)}
      onMouseLeave={() => setPressed(false)}
      onClick={onClick}
      style={{
        display: 'inline-flex', alignItems: 'center', gap: sizes.gap,
        fontFamily: 'var(--font-body)', fontWeight: 500, letterSpacing: '0.005em',
        borderRadius: 7, cursor: disabled ? 'not-allowed' : 'pointer',
        transition: 'all 150ms var(--ease-out)',
        transform: pressed ? 'scale(0.97)' : 'scale(1)',
        height: sizes.h, fontSize: sizes.fs, padding: sizes.pad,
        opacity: disabled ? 0.5 : 1,
        ...variants[variant], ...extraStyle,
      }}
    >
      {icon && <Icon name={icon} size={sizes.iconSize} />}
      {children}
      {iconRight && <Icon name={iconRight} size={sizes.iconSize} />}
    </button>
  );
};

// ── Badge ──────────────────────────────────────────────────
const Badge = ({ label, color = 'var(--fg-secondary)', bg = 'var(--bg-elevated)', pulse, dot = true }) => (
  <span style={{
    display: 'inline-flex', alignItems: 'center', gap: 5,
    fontFamily: 'var(--font-body)', fontSize: 10, fontWeight: 600,
    letterSpacing: '0.10em', textTransform: 'uppercase',
    padding: '3px 9px', borderRadius: 4, background: bg, color,
  }}>
    {dot && <span style={{
      width: 6, height: 6, borderRadius: '50%', background: color, flexShrink: 0,
      animation: pulse ? 'badgePulse 1.5s ease-in-out infinite' : 'none',
    }} />}
    {label}
  </span>
);

// ── Pipeline nav (left rail) ──────────────────────────────
// ── Available ad platforms (parent groups for pipeline stages) ──
const AD_PLATFORMS = [
  { id: 'meta',   name: 'Meta Ads',   logo: 'https://www.google.com/s2/favicons?domain=facebook.com&sz=32', color: '#1877F2', sub: 'Facebook · Instagram · Messenger', status: 'active' },
  { id: 'tiktok', name: 'TikTok Ads', logo: 'https://www.google.com/s2/favicons?domain=tiktok.com&sz=32',   color: '#FE2C55', sub: 'TikTok · Spark Ads · Lead Gen',     status: 'coming-soon' },
  { id: 'google', name: 'Google Ads', logo: 'https://www.google.com/s2/favicons?domain=google.com&sz=32',   color: '#4285F4', sub: 'Search · Display · YouTube',        status: 'coming-soon' },
  { id: 'shopee', name: 'Shopee Ads', logo: 'https://www.google.com/s2/favicons?domain=shopee.vn&sz=32',    color: '#EE4D2D', sub: 'Search Ads · Discovery · KOL',      status: 'coming-soon' },
  { id: 'lazada', name: 'Lazada Ads', logo: 'https://www.google.com/s2/favicons?domain=lazada.vn&sz=32',    color: '#0F146D', sub: 'Sponsored · Affiliate · Live',       status: 'coming-soon' },
];

const _svg = (size, children) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
    {children}
  </svg>
);

const NAV_ICONS = {
  product: (s) => _svg(s, <>
    <path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/>
    <path d="M3.29 7 12 12l8.71-5"/><path d="M12 22V12"/>
  </>),
  persona: (s) => _svg(s, <>
    <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
    <circle cx="9" cy="7" r="4"/>
    <path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>
  </>),
  content: (s) => _svg(s, <>
    <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
    <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
  </>),
  ads: (s) => _svg(s, <>
    <path d="M3 11l19-9-9 19-2-8-8-2z"/>
  </>),
  data: (s) => _svg(s, <>
    <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
  </>),
};

const PIPELINE_STAGES = [
  { id: 'product', vi: 'Sản phẩm',             en: 'Product',         color: '#FBBF24' },
  { id: 'persona', vi: 'Chân dung khách hàng', en: 'Customer funnel', color: '#A78BFA' },
  { id: 'content', vi: 'Content & Plans',      en: 'Content plans',   color: '#60A5FA' },
  { id: 'ads',     vi: 'Digital Ads',          en: 'Meta Ads setup',  color: '#F87171' },
  { id: 'data',    vi: 'Ads Data',             en: 'CSV analyzer',    color: '#34D399' },
];

// ── Platform selector (parent header above the 5 stages) ──
const PlatformSelector = ({ value, onChange, lang }) => {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [open]);
  const T = (vi, en) => lang === 'en' ? en : vi;
  const platform = AD_PLATFORMS.find(p => p.id === value) || AD_PLATFORMS[0];

  return (
    <div ref={ref} className="platform-selector-wrap">
      <div className="platform-eyebrow">{T('Nền tảng', 'Platform')}</div>
      <button className={`platform-selector ${open ? 'open' : ''}`} onClick={() => setOpen(o => !o)}>
        <div className="platform-icon" style={{ background: `${platform.color}22` }}>
          <img src={platform.logo} alt={platform.name} style={{ width: 18, height: 18, borderRadius: 4, display: 'block' }} />
        </div>
        <div className="platform-meta">
          <span className="platform-name">{platform.name}</span>
          <span className="platform-sub">{platform.sub}</span>
        </div>
        <Icon name="chevron-down" size={13} color="var(--fg-muted)" />
      </button>

      {open && (
        <div className="platform-dropdown">
          <div className="platform-dropdown-head">
            {T('Chọn nền tảng quảng cáo', 'Choose ad platform')}
          </div>
          {AD_PLATFORMS.map(p => {
            const isActive = p.id === value;
            const disabled = p.status === 'coming-soon';
            return (
              <button
                key={p.id}
                className={`platform-dropdown-item ${isActive ? 'active' : ''} ${disabled ? 'disabled' : ''}`}
                onClick={() => {
                  if (disabled) return;
                  onChange(p.id);
                  setOpen(false);
                }}
              >
                <div className="platform-icon" style={{ background: `${p.color}22` }}>
                  <img src={p.logo} alt={p.name} style={{ width: 16, height: 16, borderRadius: 3, display: 'block' }} />
                </div>
                <div className="platform-meta">
                  <span className="platform-name">{p.name}</span>
                  <span className="platform-sub">{p.sub}</span>
                </div>
                {p.status === 'coming-soon' && (
                  <span className="platform-badge">{T('Sắp ra mắt', 'Soon')}</span>
                )}
                {p.status === 'beta' && (
                  <span className="platform-badge beta">Beta</span>
                )}
                {isActive && <Icon name="check" size={13} color="var(--accent-cyan)" />}
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
};

const PipelineNav = ({ current, onNav, sectionStatus, productName, onOpenSettings, lang, platform, onPlatformChange }) => {
  const T = (vi, en) => lang === 'en' ? en : vi;
  return (
  <aside className="pipeline-nav">
    <div className="pipeline-brand" onClick={() => onNav('product')} style={{ cursor: 'pointer' }}>
      <img src="assets/logo.png" alt="Loij Flow" className="pipeline-brand-logo" />
      <span className="pipeline-brand-name">LOIJ FLOW</span>
    </div>

    <PlatformSelector value={platform} onChange={onPlatformChange} lang={lang} />

    <div className="pipeline-list">
      <div className="pipeline-line" />
      {PIPELINE_STAGES.map((s, i) => {
        const status = sectionStatus[s.id];
        const active = current === s.id;
        const saved  = status === 'saved';
        const activePlatform = AD_PLATFORMS.find(p => p.id === platform);
        const dotBg    = active ? s.color : `${s.color}22`;
        const dotBorder= (active || saved) ? s.color : `${s.color}55`;
        const dotColor = active ? '#0a0b0f' : s.color;
        return (
          <div
            key={s.id}
            className={`pipeline-step ${active ? 'active' : ''} ${saved ? 'done' : ''}`}
            onClick={() => onNav(s.id)}
          >
            <div className="pipeline-dot" style={{ background: dotBg, borderColor: dotBorder, color: dotColor, boxShadow: active ? `0 0 0 4px ${s.color}28` : 'none' }}>
              {s.id === 'ads' && activePlatform
                ? <img src={activePlatform.logo} alt={activePlatform.name} style={{ width: 14, height: 14, borderRadius: 3, display: 'block' }} />
                : NAV_ICONS[s.id](14)
              }
              {saved && !active && (
                <span style={{ position: 'absolute', bottom: -3, right: -3, width: 10, height: 10, borderRadius: '50%', background: s.color, display: 'flex', alignItems: 'center', justifyContent: 'center', border: '1.5px solid var(--bg-base)' }}>
                  <svg width="6" height="6" viewBox="0 0 24 24" fill="none" stroke="#0a0b0f" strokeWidth="3.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                </span>
              )}
            </div>
            <div className="pipeline-meta">
              <span className="pipeline-label-vi" style={saved && !active ? { color: s.color } : {}}>{lang === 'en' ? s.en : s.vi}</span>
            </div>
            <span className={`pipeline-status-dot ${status || 'empty'}`} />
          </div>
        );
      })}

      <div style={{ height: 14 }} />

      <div
        className={`pipeline-step map ${current === 'map' ? 'active' : ''}`}
        onClick={() => onNav('map')}
        style={{ borderTop: '1px dashed var(--border-subtle)', marginTop: 6, paddingTop: 14 }}
      >
        <div className="pipeline-dot">
          <Icon name="git-fork" size={13} />
        </div>
        <div className="pipeline-meta">
          <span className="pipeline-label-vi">{T('Map tổng', 'Workflow canvas')}</span>
        </div>
        <span className="pipeline-status-dot saved" />
      </div>
    </div>

  </aside>
  );
};

// ── Theme & Lang switchers (topbar) ───────────────────────
const ThemeToggle = ({ dark, onToggle, lang }) => (
  <button
    className="topbar-icon-btn"
    onClick={onToggle}
    title={lang === 'en' ? (dark ? 'Light mode' : 'Dark mode') : (dark ? 'Chế độ sáng' : 'Chế độ tối')}
  >
    <Icon name={dark ? 'sun' : 'moon'} size={14} />
  </button>
);

const LangSwitcher = ({ lang, onChange }) => {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [open]);
  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button className="topbar-lang-pill" onClick={() => setOpen(o => !o)}>
        <Icon name="globe" size={13} />
        <span className="flag">{lang === 'en' ? 'EN' : 'VI'}</span>
        <Icon name="chevron-down" size={11} color="var(--fg-muted)" />
      </button>
      {open && (
        <div className="lang-dropdown">
          {[
            { id: 'vi', label: 'Tiếng Việt', flag: 'VI' },
            { id: 'en', label: 'English',    flag: 'EN' },
          ].map(opt => (
            <button
              key={opt.id}
              className={`lang-dropdown-item ${lang === opt.id ? 'active' : ''}`}
              onClick={() => { onChange(opt.id); setOpen(false); }}
            >
              <span className="lang-dropdown-flag">{opt.flag}</span>
              <span style={{ flex: 1 }}>{opt.label}</span>
              {lang === opt.id && <Icon name="check" size={12} color="var(--accent-cyan)" />}
            </button>
          ))}
        </div>
      )}
    </div>
  );
};

// ── Screen header ─────────────────────────────────────────
const ScreenHead = ({ eyebrow, vi, en, status, right }) => (
  <div className="screen-head">
    <div className="screen-head-left">
      {eyebrow && <span className="screen-eyebrow">{eyebrow}</span>}
      <h1 className="screen-title">
        {vi}
        {en && <span className="en">{en}</span>}
      </h1>
    </div>
    <div className="screen-head-right">
      {status && <Badge {...status} />}
      {right}
    </div>
  </div>
);

// ── Style chip ────────────────────────────────────────────
const StyleChip = ({ value, onEdit, onClear }) => {
  const isEmpty = !value || !value.trim();
  return (
    <div className="style-chip">
      <div className="style-chip-label">
        <Icon name="palette" size={15} color="var(--accent-purple)" />
        <div className="style-chip-label-text">
          <span className="vi">Style</span>
          <span className="en">Saved tone</span>
        </div>
      </div>
      <div className={`style-chip-content ${isEmpty ? 'empty' : ''}`}>
        {isEmpty
          ? 'Chưa có style. Prompt đầu tiên của bạn sẽ được lưu thành style cho các lần sau.'
          : value}
      </div>
      <div className="style-chip-actions">
        <Btn variant="ghost" size="sm" icon="pencil" onClick={onEdit} title="Edit style">Edit</Btn>
        {!isEmpty && <Btn variant="ghost" size="sm" icon="x" onClick={onClear} title="Clear style" />}
      </div>
    </div>
  );
};

// ── Claude-style prompt input ─────────────────────────────
// Compact dropdown used inside the prompt input toolbar
const PromptDropdown = ({ icon, color, label, value, options, onChange }) => {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [open]);
  const selected = options.find(o => o.id === value) || options[0];
  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button type="button" className="prompt-pill" onClick={() => setOpen(o => !o)}>
        {selected.svgIcon
          ? <span style={{ color: selected.color || color || 'var(--fg-secondary)', display: 'inline-flex', alignItems: 'center' }}>{selected.svgIcon(11)}</span>
          : icon && <Icon name={icon} size={11} color={color || 'var(--fg-secondary)'} />
        }
        <span className="prompt-pill-label">{label}</span>
        <span className="prompt-pill-value">{selected.label}</span>
        <Icon name="chevron-down" size={10} color="var(--fg-muted)" />
      </button>
      {open && (
        <div className="prompt-dropdown">
          {options.map(o => (
            <button
              key={o.id}
              className={`prompt-dropdown-item ${o.id === value ? 'active' : ''}`}
              onClick={() => { onChange(o.id); setOpen(false); }}
            >
              {(o.svgIcon || o.icon) && (
                <span
                  className="prompt-dropdown-icon"
                  style={{ background: `${o.color || color || '#66FCF1'}22`, color: o.color || color || 'var(--accent-cyan)' }}
                >
                  {o.svgIcon ? o.svgIcon(12) : <Icon name={o.icon} size={11} />}
                </span>
              )}
              <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', minWidth: 0, flex: 1 }}>
                <span className="prompt-dropdown-label">{o.label}</span>
                {o.sub  && <span className="prompt-dropdown-sub">{o.sub}</span>}
                {o.good && <span className="prompt-dropdown-sub">Best for: {o.good}</span>}
              </div>
              {o.id === value && <Icon name="check" size={12} color="var(--accent-cyan)" />}
            </button>
          ))}
        </div>
      )}
    </div>
  );
};

// Multi-select dropdown for cross-section "linked sources"
const SourceLinker = ({ available, linked, onToggle, currentKind }) => {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [open]);
  const linkable = Object.entries(available).filter(([k]) => k !== currentKind);
  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button type="button" className="prompt-pill" onClick={() => setOpen(o => !o)}>
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="var(--accent-cyan)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
        <span className="prompt-pill-label">Link</span>
        <span className="prompt-pill-value">{linked.length || '0'} source{linked.length === 1 ? '' : 's'}</span>
        <Icon name="chevron-down" size={10} color="var(--fg-muted)" />
      </button>
      {open && (
        <div className="prompt-dropdown" style={{ width: 240 }}>
          <div className="prompt-dropdown-head">Liên kết với section khác</div>
          {linkable.map(([k, info]) => {
            const isLinked = linked.includes(k);
            return (
              <button
                key={k}
                className={`prompt-dropdown-item ${isLinked ? 'active' : ''}`}
                onClick={() => onToggle(k)}
              >
                <span className="prompt-dropdown-icon" style={{ background: `${info.color}22`, color: info.color }}>
                  {NAV_ICONS[k] ? NAV_ICONS[k](12) : <Icon name={info.icon} size={11} />}
                </span>
                <span className="prompt-dropdown-label" style={{ flex: 1, textAlign: 'left' }}>{info.label}</span>
                <div style={{
                  width: 14, height: 14, borderRadius: 4,
                  border: `1.5px solid ${isLinked ? 'var(--accent-cyan)' : 'var(--border-strong)'}`,
                  background: isLinked ? 'var(--accent-cyan)' : 'transparent',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>
                  {isLinked && (
                    <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="var(--fg-on-accent)" strokeWidth="3.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                  )}
                </div>
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
};

const PromptInput = ({
  placeholder, value, onChange, onSubmit, running,
  suggestions = [], hero, runLabel = 'Generate',
  currentKind, linkedSources = [], onToggleSource,
  model = 'claude-sonnet-4.5', onModelChange,
  styleId = 'normal', onStyleChange,
  onAttachmentsChange,
}) => {
  const [attachments, setAttachments] = React.useState([]);
  const [dragOver, setDragOver] = React.useState(false);
  const fileRef = React.useRef(null);

  const submit = () => {
    if (running || !value || !value.trim()) return;
    onSubmit(attachments);
  };
  const onKey = (e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault(); submit();
    }
  };

  const formatSize = (b) => b < 1024 ? `${b}B` : b < 1048576 ? `${(b/1024).toFixed(0)}KB` : `${(b/1048576).toFixed(1)}MB`;

  const handleFiles = (files) => {
    Array.from(files).forEach(file => {
      const base = { id: `att-${Date.now()}-${Math.random().toString(36).slice(2)}`, name: file.name, type: file.type, size: file.size, dataUrl: null, textContent: null };
      const isImage = file.type.startsWith('image/');
      const isText  = file.type.startsWith('text/') || /\.(txt|csv|md|json|js|jsx|ts|tsx|html|css)$/i.test(file.name);
      const reader  = new FileReader();
      if (isImage) {
        reader.onload = (e) => setAttachments(prev => { const next = [...prev, { ...base, dataUrl: e.target.result }]; onAttachmentsChange && onAttachmentsChange(next); return next; });
        reader.readAsDataURL(file);
      } else if (isText) {
        reader.onload = (e) => setAttachments(prev => { const next = [...prev, { ...base, textContent: e.target.result }]; onAttachmentsChange && onAttachmentsChange(next); return next; });
        reader.readAsText(file);
      } else {
        setAttachments(prev => { const next = [...prev, base]; onAttachmentsChange && onAttachmentsChange(next); return next; });
      }
    });
  };

  const removeAttachment = (id) => {
    setAttachments(prev => {
      const next = prev.filter(a => a.id !== id);
      onAttachmentsChange && onAttachmentsChange(next);
      return next;
    });
  };

  const getFileIcon = (type) => {
    if (type.startsWith('image/')) return 'image';
    if (type.includes('pdf')) return 'file-text';
    if (type.includes('csv') || type.includes('excel') || type.includes('spreadsheet')) return 'table-2';
    if (type.includes('word') || type.includes('document')) return 'file-text';
    return 'paperclip';
  };

  return (
    <div className="prompt-shell">
      {hero && (
        <div className="prompt-hero-title">
          <span className="vi">
            {hero.vi[0]}
            <span className="accent"> {hero.vi[1]}</span>
            {hero.vi[2]}
          </span>
          <span className="en">{hero.en}</span>
        </div>
      )}
      <div
        className={`prompt-card${dragOver ? ' drag-over' : ''}`}
        onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
        onDragLeave={() => setDragOver(false)}
        onDrop={(e) => { e.preventDefault(); setDragOver(false); if (e.dataTransfer.files.length) handleFiles(e.dataTransfer.files); }}
      >
        {linkedSources.length > 0 && (
          <div className="prompt-sources-row">
            <span className="prompt-sources-label">
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="var(--accent-cyan)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
              Linked
            </span>
            {linkedSources.map(k => {
              const info = LINKABLE_SOURCES[k];
              if (!info) return null;
              return (
                <span key={k} className="prompt-source-chip" style={{ borderColor: `${info.color}55` }}>
                  <span style={{ color: info.color, display: 'inline-flex', alignItems: 'center' }}>
                    {NAV_ICONS[k] ? NAV_ICONS[k](11) : null}
                  </span>
                  {info.label}
                  <button
                    type="button"
                    className="prompt-source-chip-x"
                    onClick={() => onToggleSource && onToggleSource(k)}
                  >
                    <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
                  </button>
                </span>
              );
            })}
          </div>
        )}
        {attachments.length > 0 && (
          <div className="prompt-attachments-row">
            {attachments.map(a => (
              <div key={a.id} className="prompt-attachment-chip">
                {a.dataUrl
                  ? <img src={a.dataUrl} alt={a.name} className="prompt-attachment-thumb" />
                  : <span className="prompt-attachment-icon"><Icon name={getFileIcon(a.type)} size={12} /></span>
                }
                <span className="prompt-attachment-name">{a.name}</span>
                <span className="prompt-attachment-size">{formatSize(a.size)}</span>
                <button type="button" className="prompt-attachment-x" onClick={() => removeAttachment(a.id)}>
                  <Icon name="x" size={10} />
                </button>
              </div>
            ))}
          </div>
        )}
        <textarea
          className="prompt-textarea"
          placeholder={placeholder}
          value={value}
          onChange={(e) => onChange(e.target.value)}
          onKeyDown={onKey}
          rows={3}
        />
        <div className="prompt-row">
          <div className="btn-row" style={{ gap: 6 }}>
            <button
              type="button"
              className="prompt-attach-btn"
              title="Đính kèm file hoặc hình ảnh"
              onClick={() => fileRef.current && fileRef.current.click()}
            >
              <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
                <line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
              </svg>
              {attachments.length > 0 && <span className="prompt-attach-count">{attachments.length}</span>}
            </button>
            <input
              ref={fileRef}
              type="file"
              multiple
              accept="image/*,.pdf,.csv,.xlsx,.xls,.doc,.docx,.txt"
              style={{ display: 'none' }}
              onChange={(e) => { if (e.target.files.length) handleFiles(e.target.files); e.target.value = ''; }}
            />
            {onStyleChange && (
              <PromptDropdown
                icon="palette" color="var(--accent-purple)"
                label="Style" value={styleId}
                options={STYLE_PRESETS.map(s => ({ id: s.id, label: s.label, sub: s.sub }))}
                onChange={onStyleChange}
              />
            )}
            {onModelChange && (
              <PromptDropdown
                icon="cpu" color="var(--accent-blue)"
                label="AI" value={model}
                options={AI_MODELS.map(m => ({ id: m.id, label: m.label, icon: m.icon, svgIcon: m.svgIcon, color: m.color, good: m.good }))}
                onChange={onModelChange}
              />
            )}
            {onToggleSource && (
              <SourceLinker
                available={LINKABLE_SOURCES}
                linked={linkedSources}
                onToggle={onToggleSource}
                currentKind={currentKind}
              />
            )}
          </div>
          <div className="btn-row">
            <span className="run-shortcut">⌘ + ↵</span>
            <Btn
              variant="primary" size="md"
              icon={running ? 'loader' : 'sparkles'}
              onClick={submit}
              disabled={running}
              style={running ? { opacity: 0.85 } : null}
            >
              {running ? 'Đang chạy…' : runLabel}
            </Btn>
          </div>
        </div>
      </div>
    </div>
  );
};

// ── Output card wrapper ───────────────────────────────────
const OutputCard = ({ icon, title, sub, children, actions }) => (
  <div className="output-card fade-in">
    <div className="output-head">
      <div className="output-head-title">
        <span className="icon-wrap"><Icon name={icon} size={14} /></span>
        <div>
          <h3>{title}</h3>
          {sub && <div style={{ fontSize: 11, color: 'var(--fg-muted)', marginTop: 2 }}>{sub}</div>}
        </div>
      </div>
      <div className="output-head-actions">
        {actions || (
          <React.Fragment>
            <Btn variant="ghost" size="sm" icon="pencil" title="Edit" />
            <Btn variant="ghost" size="sm" icon="copy" title="Copy" />
            <Btn variant="ghost" size="sm" icon="download" title="Export" />
          </React.Fragment>
        )}
      </div>
    </div>
    <div className="output-body">{children}</div>
  </div>
);

// ── Loading skeleton ──────────────────────────────────────
const LoadingSkeleton = ({ lines = 4, label = 'Generating…' }) => (
  <div className="output-card">
    <div className="output-head">
      <div className="output-head-title">
        <span className="icon-wrap" style={{
          background: 'rgba(102,252,241,0.10)',
        }}>
          <span style={{
            width: 12, height: 12,
            border: '1.5px solid var(--accent-cyan)',
            borderTopColor: 'transparent',
            borderRadius: '50%',
            animation: 'spin 700ms linear infinite',
          }} />
        </span>
        <div>
          <h3>{label}</h3>
          <div style={{ fontSize: 11, color: 'var(--fg-muted)', marginTop: 2 }}>
            Streaming with style preset · est. 6s
          </div>
        </div>
      </div>
    </div>
    <div className="output-body">
      {Array.from({ length: lines }).map((_, i) => (
        <div
          key={i}
          className="skeleton-line"
          style={{ width: `${85 - i * 11}%`, height: i === 0 ? 16 : 12 }}
        />
      ))}
    </div>
  </div>
);

// ── Version rail (right side) ─────────────────────────────
const VersionRail = ({ versions, activeId, onPick }) => {
  const [showCount, setShowCount] = React.useState(5);

  React.useEffect(() => { setShowCount(5); }, [versions]);

  const realVersions = React.useMemo(
    () => [...versions].filter(v => !v._demo).reverse(),
    [versions]
  );
  const displayed  = realVersions.slice(0, showCount);
  const remaining  = realVersions.length - showCount;

  return (
    <aside className="version-rail">
      <div className="version-rail-head">
        <span className="version-rail-head-title">Version history</span>
        <span style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--fg-muted)' }}>
          {realVersions.length}
        </span>
      </div>
      <div className="version-rail-list">
        {realVersions.length === 0 ? (
          <div style={{ padding: '24px 12px', textAlign: 'center', color: 'var(--fg-muted)', fontSize: 11.5, lineHeight: 1.7 }}>
            Chưa có version nào.<br />
            Submit prompt để bắt đầu.
          </div>
        ) : (
          <React.Fragment>
            {displayed.map((v, i) => {
              const num   = realVersions.length - i;
              const label = v.label || v.prompt || `Version ${num}`;
              const time  = v.ts    || v.time   || '';
              return (
                <div
                  key={v.id}
                  className={`version-item ${v.id === activeId ? 'active' : ''}`}
                  onClick={() => onPick(v.id)}
                >
                  <div className="version-item-head">
                    <span className="version-item-num">v{num}</span>
                    {v.id === activeId && <span className="version-item-active-tag">Active</span>}
                  </div>
                  <div className="version-item-preview">{label}</div>
                  <span className="version-item-time">{time}</span>
                </div>
              );
            })}
            {remaining > 0 && (
              <button
                onClick={() => setShowCount(c => c + 5)}
                style={{
                  width: '100%', padding: '9px 12px',
                  background: 'none', border: 'none',
                  borderTop: '1px solid var(--border)',
                  color: 'var(--fg-muted)', fontSize: 11.5,
                  cursor: 'pointer', textAlign: 'center',
                  fontFamily: 'var(--font-body)',
                }}
              >
                Xem thêm ({remaining})
              </button>
            )}
          </React.Fragment>
        )}
      </div>
    </aside>
  );
};

// ── Context source bar — shows linked sections + version picker ──────
const ContextSourceBar = ({ linkedSources, allSections, overrides, onOverrideChange }) => {
  const [openKey, setOpenKey] = React.useState(null);
  const barRef = React.useRef(null);

  React.useEffect(() => {
    if (!openKey) return;
    const h = (e) => { if (barRef.current && !barRef.current.contains(e.target)) setOpenKey(null); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [openKey]);

  if (!linkedSources || linkedSources.length === 0) return null;

  return (
    <div ref={barRef} style={{
      display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 6,
      marginTop: 10, padding: '9px 14px',
      background: 'rgba(102,252,241,0.04)',
      border: '1px solid rgba(102,252,241,0.12)',
      borderRadius: 8,
    }}>
      <span style={{ fontSize: 11, color: 'var(--fg-muted)', fontWeight: 500, display: 'flex', alignItems: 'center', gap: 4, marginRight: 2 }}>
        <Icon name="database" size={11} color="var(--accent-cyan)" />
        Context nguồn:
      </span>
      {linkedSources.map(key => {
        const info = LINKABLE_SOURCES[key];
        if (!info) return null;
        const sec = allSections && allSections[key];
        const realVers = sec ? [...(sec.versions || [])].filter(v => !v._demo).reverse() : [];
        const overrideId = overrides && overrides[key];
        const activeVer = overrideId
          ? realVers.find(v => v.id === overrideId)
          : (sec ? (realVers.find(v => v.id === sec.activeId) || realVers[0]) : null);
        const hasContent = activeVer && activeVer.output && activeVer.output._text;
        const isOpen = openKey === key;
        const vLabel = activeVer ? (activeVer.label || activeVer.prompt || '') : '';

        return (
          <div key={key} style={{ position: 'relative' }}>
            <button
              type="button"
              onClick={() => setOpenKey(isOpen ? null : key)}
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 5,
                padding: '4px 9px', borderRadius: 6,
                border: `1px solid ${hasContent ? `${info.color}55` : 'var(--border)'}`,
                background: hasContent ? `${info.color}11` : 'var(--bg-elevated)',
                cursor: 'pointer', fontSize: 11.5,
                color: hasContent ? info.color : 'var(--fg-muted)',
              }}
            >
              <Icon name={info.icon} size={11} color={info.color} />
              <span>{info.label}</span>
              {hasContent ? (
                <span style={{ opacity: 0.65, fontSize: 10, maxWidth: 90, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                  {overrideId ? '★ ' : ''}{vLabel.slice(0, 18)}{vLabel.length > 18 ? '…' : ''}
                </span>
              ) : (
                <span style={{ opacity: 0.5, fontSize: 10 }}>Chưa có data</span>
              )}
              {realVers.length > 0 && <Icon name="chevron-down" size={9} color="var(--fg-muted)" />}
            </button>
            {isOpen && realVers.length > 0 && (
              <div style={{
                position: 'absolute', top: 'calc(100% + 4px)', left: 0, zIndex: 100,
                minWidth: 250, maxHeight: 220, overflowY: 'auto',
                background: 'var(--bg-surface)', border: '1px solid var(--border)',
                borderRadius: 8, boxShadow: '0 8px 24px rgba(0,0,0,0.35)',
              }}>
                <div style={{ padding: '7px 12px 6px', fontSize: 11, color: 'var(--fg-muted)', borderBottom: '1px solid var(--border)', fontWeight: 500 }}>
                  Chọn version — {info.label}
                </div>
                {realVers.map((v, i) => {
                  const isSelected = overrideId ? overrideId === v.id : (v.id === (sec && sec.activeId) || (!sec.activeId && i === 0));
                  const rowLabel = v.label || v.prompt || `Version ${realVers.length - i}`;
                  return (
                    <button
                      key={v.id}
                      type="button"
                      onClick={() => { onOverrideChange(key, v.id); setOpenKey(null); }}
                      style={{
                        width: '100%', textAlign: 'left',
                        padding: '7px 12px', border: 'none',
                        background: isSelected ? 'rgba(102,252,241,0.07)' : 'transparent',
                        cursor: 'pointer',
                        color: isSelected ? 'var(--accent-cyan)' : 'var(--fg-primary)',
                        display: 'flex', alignItems: 'flex-start', gap: 7,
                      }}
                    >
                      <span style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--fg-muted)', marginTop: 2, flexShrink: 0 }}>
                        v{realVers.length - i}
                      </span>
                      <span style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 11.5, marginBottom: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{rowLabel}</div>
                        <div style={{ fontSize: 10, color: 'var(--fg-muted)' }}>{v.ts || v.time || ''}</div>
                      </span>
                      {isSelected && <Icon name="check" size={12} color="var(--accent-cyan)" />}
                    </button>
                  );
                })}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
};

// Export all
window.buildAttachmentContext = (attachments) => {
  if (!attachments || attachments.length === 0) return '';
  const parts = attachments.map(a => {
    if (a.textContent) return `\n--- File: ${a.name} ---\n${a.textContent}\n---`;
    if (a.dataUrl)     return `\n[Hình ảnh đính kèm: ${a.name}]`;
    return `\n[File đính kèm: ${a.name}]`;
  });
  return '\n\nNội dung file đính kèm từ người dùng:' + parts.join('');
};

Object.assign(window, {
  Icon, Btn, Badge, PIPELINE_STAGES, AD_PLATFORMS, PipelineNav, ScreenHead,
  StyleChip, PromptInput, PromptDropdown, SourceLinker,
  OutputCard, LoadingSkeleton, VersionRail, ContextSourceBar,
  ThemeToggle, LangSwitcher, PlatformSelector,
});
