// Surface 1 — Sources (upload + ingest review)

function SurfaceSources({ sources, isEmpty = false }) {
  const [items, setItems] = React.useState(sources);
  const [confirm, setConfirm] = React.useState(null); // { source, impact }

  if (isEmpty) return <SourcesEmpty />;

  const impactOf = (source) => ({
    layouts: source.kind === 'doc' ? 6 : source.kind === 'video' ? 3 : 2,
    modules: source.kind === 'doc' ? 3 : source.kind === 'video' ? 2 : 1,
    paragraphs: source.paragraphs || source.transcriptWords || 0
  });

  const handleAction = (action, source) => {
    if (action === 'delete') {
      setConfirm({ source, impact: impactOf(source) });
    }
    // Other actions (view, replace, reingest, download) are no-ops in this prototype.
  };

  const confirmDelete = (keepLayouts) => {
    if (!confirm) return;
    setItems((its) => its.filter((x) => x.id !== confirm.source.id));
    setConfirm(null);
  };

  return (
    <div style={{
      height: '100%', background: 'var(--bg)', overflow: 'hidden',
      display: 'flex', flexDirection: 'column', minHeight: 0
    }} data-screen-label="Surface 1 · Sources">
      <section style={{ display: 'flex', flexDirection: 'column', minHeight: 0, flex: 1 }}>
        {/* Slim drop bar */}
        <div style={{
          margin: '20px 24px 0',
          padding: '12px 16px',
          background: 'var(--surface)', border: '1px dashed var(--border-strong)',
          borderRadius: 'var(--radius-md)',
          display: 'flex', alignItems: 'center', gap: 12
        }}>
          <I.Upload size={16} style={{ color: 'var(--text-muted)' }} />
          <span style={{ fontSize: 13, color: 'var(--text-muted)' }}>Drop more sources here

          </span>
          <div style={{ flex: 1 }} />
          <button className="btn sm primary">Add source</button>
        </div>

        {/* Sources list */}
        <div style={{ padding: '16px 24px', flex: 1, overflowY: 'auto' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
            <h2 style={{ margin: 0, fontSize: 13.5, fontWeight: 600 }}>Sources</h2>
            <span style={{ fontSize: 11, color: 'var(--text-faint)',
              fontFamily: 'var(--font-mono)', background: 'var(--surface-inset)',
              padding: '2px 6px', borderRadius: 4 }}>{items.length}</span>
            <div style={{ flex: 1 }} />
            <button className="btn sm ghost"><I.Filter size={12} />All</button>
          </div>
          <div style={{ display: 'grid', gap: 4 }}>
            {items.map((s) =>
            <SourceRow key={s.id} source={s}
            onAction={(action) => handleAction(action, s)} />
            )}
          </div>
        </div>
      </section>

      {confirm &&
      <DeleteSourceDialog
        source={confirm.source}
        impact={confirm.impact}
        onCancel={() => setConfirm(null)}
        onConfirm={confirmDelete} />
      }
    </div>);

}

function SourceRow({ source: s, onAction }) {
  const KindIcon = { doc: I.FileText, video: I.Film, audio: I.Mic }[s.kind] || I.FileText;
  const [menuOpen, setMenuOpen] = React.useState(false);
  const [menuPos, setMenuPos] = React.useState(null);
  const btnRef = React.useRef(null);

  // Popup is shorter now (only 2 actions → 60 + 2*48 ≈ 156)
  const openMenu = () => {
    if (menuOpen) {setMenuOpen(false);return;}
    const r = btnRef.current && btnRef.current.getBoundingClientRect();
    if (!r) return;
    const w = 240,h = 130;
    const openUp = window.innerHeight - r.bottom < h + 12;
    setMenuPos({
      top: openUp ? Math.max(8, r.top - h - 6) : r.bottom + 6,
      left: Math.min(Math.max(8, r.right - w), window.innerWidth - w - 8),
      width: w
    });
    setMenuOpen(true);
  };

  return (
    <div
      style={{
        display: 'grid',
        gridTemplateColumns: '28px 28px 1fr 110px 80px 40px',
        gap: 12, alignItems: 'center',
        padding: '10px 12px',
        background: 'var(--surface)',
        border: '1px solid var(--border)',
        borderRadius: 'var(--radius)', cursor: 'default'
      }}>
      <span style={{
        width: 24, height: 24, borderRadius: 'var(--radius-sm)',
        background: 'var(--surface-inset)',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        color: 'var(--text-muted)'
      }}><KindIcon size={13} /></span>
      {s.warnings > 0 ?
      <I.AlertTriangle size={14} style={{ color: 'var(--warning)' }} /> :
      <I.Check size={14} style={{ color: 'var(--success)' }} />}
      <span className="truncate" style={{ fontSize: 13, color: 'var(--text)' }}>{s.name}</span>
      <StatusPill status={s.status} />
      <span style={{ fontSize: 11, color: 'var(--text-faint)',
        fontFamily: 'var(--font-mono)', textAlign: 'right' }}>{s.size}</span>
      <button ref={btnRef} className="btn sm ghost"
      title="More actions"
      onClick={openMenu}>
        <I.MoreHorizontal size={12} />
      </button>

      {menuOpen && menuPos &&
      <SourceActionsMenu
        source={s}
        pos={menuPos}
        onClose={() => setMenuOpen(false)}
        onAction={(a) => {setMenuOpen(false);onAction && onAction(a);}} />
      }
    </div>);

}

// ── More-actions popup ($) ────────────────────────────────────────────────────────────────
function SourceActionsMenu({ source, pos, onClose, onAction }) {
  const isDoc = source.kind === 'doc';
  const items = [
  { id: 'download', label: 'Download', icon: 'Save' },
  { id: 'divider' },
  { id: 'delete', label: 'Delete source', icon: 'Trash', danger: true }];

  return (
    <>
      <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 60 }} />
      <div style={{
        position: 'fixed', top: pos.top, left: pos.left, width: pos.width,
        background: 'var(--surface)', border: '1px solid var(--border)',
        borderRadius: 'var(--radius-md)', boxShadow: 'var(--shadow-lg)',
        padding: 6, zIndex: 61
      }}>
        {items.map((it) => {
          if (it.id === 'divider') {
            return <div key="d" style={{ height: 1, background: 'var(--border)',
              margin: '4px 4px' }} />;
          }
          const Ic = I[it.icon] || I.Hash;
          return (
            <button key={it.id} onClick={() => onAction(it.id)}
            style={{
              display: 'grid', gridTemplateColumns: '18px 1fr',
              gap: 10, alignItems: 'flex-start',
              width: '100%', padding: '7px 8px',
              border: 0, borderRadius: 'var(--radius)',
              background: 'transparent', textAlign: 'left',
              cursor: 'default', fontFamily: 'inherit', fontSize: 12.5,
              color: it.danger ? 'var(--error-text)' : 'var(--text)'
            }}
            onMouseOver={(e) => e.currentTarget.style.background = it.danger ?
            'var(--error-bg)' : 'var(--surface-inset)'}
            onMouseOut={(e) => e.currentTarget.style.background = 'transparent'}>
              <Ic size={13} style={{ marginTop: 1,
                color: it.danger ? 'var(--error-text)' : 'var(--text-muted)' }} />
              <div style={{ minWidth: 0 }}>
                <div style={{ fontWeight: 500 }}>{it.label}</div>
                {it.sub &&
                <div style={{ fontSize: 11, color: 'var(--text-faint)',
                  marginTop: 1, lineHeight: 1.35 }}>{it.sub}</div>
                }
              </div>
            </button>);

        })}
      </div>
    </>);

}

// ── Delete-confirmation dialog ─────────────────────────────────────────────────────────────
function DeleteSourceDialog({ source, impact, onCancel, onConfirm }) {
  const [mode, setMode] = React.useState('keep'); // 'keep' | 'cascade'
  const [typed, setTyped] = React.useState('');
  const cascade = mode === 'cascade';
  const canConfirm = !cascade || typed === source.name;

  return (
    <div onClick={onCancel} style={{
      position: 'fixed', inset: 0, zIndex: 70,
      background: 'color-mix(in oklab, var(--text) 35%, transparent)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: 560, maxWidth: '100%',
        background: 'var(--surface)', border: '1px solid var(--border)',
        borderRadius: 'var(--radius-md)', boxShadow: 'var(--shadow-xl)',
        overflow: 'hidden'
      }}>
        {/* Header */}
        <div style={{ padding: '16px 20px', borderBottom: '1px solid var(--border)',
          display: 'flex', alignItems: 'center', gap: 10 }}>
          <span style={{ width: 30, height: 30, borderRadius: 8,
            background: 'var(--error-bg)', color: 'var(--error-text)',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
            <I.AlertTriangle size={15} />
          </span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <h2 style={{ margin: 0, fontSize: 14.5, fontWeight: 600 }}>
              Delete source?
            </h2>
            <div className="truncate" style={{ fontSize: 12, color: 'var(--text-muted)',
              fontFamily: 'var(--font-mono)' }} title={source.name}>
              {source.name}
            </div>
          </div>
          <button className="btn sm ghost" onClick={onCancel}
          style={{ width: 28, height: 28, padding: 0, justifyContent: 'center' }}>
            <I.X size={13} />
          </button>
        </div>

        {/* Body */}
        <div style={{ padding: '16px 20px', display: 'flex', flexDirection: 'column', gap: 14 }}>

          {/* Impact panel */}
          <div style={{
            padding: '12px 14px',
            background: 'var(--warning-bg)',
            border: '1px solid color-mix(in oklab, var(--warning) 30%, transparent)',
            borderRadius: 'var(--radius-md)'
          }}>
            <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--warning-text)',
              marginBottom: 6 }}>
              This source is referenced by your course
            </div>
            <ul style={{ margin: 0, paddingLeft: 18, fontSize: 12,
              color: 'var(--warning-text)', lineHeight: 1.7 }}>
              <li><strong>{impact.layouts}</strong> layout{impact.layouts === 1 ? '' : 's'} use
                content extracted from this source
                (across <strong>{impact.modules}</strong> module{impact.modules === 1 ? '' : 's'})</li>
              <li>{impact.paragraphs.toLocaleString()} ingested paragraphs / words will be unlinked</li>
              <li>Coverage of your course's source-claim map will drop accordingly</li>
            </ul>
          </div>

          {/* Mode selector */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <DSDOption
              checked={mode === 'keep'}
              onSelect={() => setMode('keep')}
              title="Delete source only — keep layouts"
              desc={`The ${impact.layouts} dependent layout${impact.layouts === 1 ? '' : 's'} stay in the Course architecture, but lose their source link. They’ll appear with a “missing source” warning until you re-bind them.`} />
            <DSDOption
              checked={mode === 'cascade'}
              onSelect={() => setMode('cascade')}
              danger
              title={`Delete source AND remove its ${impact.layouts} layout${impact.layouts === 1 ? '' : 's'}`}
              desc="Permanently removes the source and every layout it originated. Cannot be undone." />
          </div>

          {/* Type-to-confirm for destructive mode */}
          {cascade &&
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              <label style={{ fontSize: 11.5, fontWeight: 600, color: 'var(--text-muted)',
              letterSpacing: '.02em' }}>
                Type <span style={{ fontFamily: 'var(--font-mono)',
                background: 'var(--surface-inset)', padding: '1px 5px',
                borderRadius: 3 }}>{source.name}</span> to confirm
              </label>
              <input value={typed} onChange={(e) => setTyped(e.target.value)} className="field"
            placeholder={source.name}
            style={{ height: 34, fontFamily: 'var(--font-mono)', fontSize: 12 }} />
            </div>
          }
        </div>

        {/* Footer */}
        <div style={{ padding: '12px 20px', borderTop: '1px solid var(--border)',
          background: 'var(--surface-inset)',
          display: 'flex', alignItems: 'center', gap: 8 }}>
          <span style={{ fontSize: 11, color: 'var(--text-faint)' }}>
            <I.Info size={11} /> Course architecture remains the source of truth.
          </span>
          <div style={{ flex: 1 }} />
          <button className="btn sm" onClick={onCancel}>Cancel</button>
          <button onClick={() => canConfirm && onConfirm(mode === 'keep')}
          disabled={!canConfirm}
          className="btn sm"
          style={{
            background: 'var(--error)',
            color: '#fff',
            border: 0,
            opacity: canConfirm ? 1 : 0.5
          }}>
            <I.Trash size={12} />
            {cascade ?
            `Delete source + ${impact.layouts} layout${impact.layouts === 1 ? '' : 's'}` :
            'Delete source only'}
          </button>
        </div>
      </div>
    </div>);

}

function DSDOption({ checked, onSelect, title, desc, danger }) {
  return (
    <button onClick={onSelect}
    style={{
      display: 'grid', gridTemplateColumns: '18px 1fr', gap: 12,
      alignItems: 'flex-start',
      padding: '12px 14px', textAlign: 'left',
      background: checked ?
      danger ? 'var(--error-bg)' : 'var(--accent-bg)' :
      'var(--surface)',
      border: '1px solid',
      borderColor: checked ?
      danger ? 'var(--error)' : 'var(--accent-border)' :
      'var(--border)',
      borderRadius: 'var(--radius-md)',
      cursor: 'default', fontFamily: 'inherit', color: 'var(--text)',
      width: '100%'
    }}>
      <span style={{
        width: 16, height: 16, borderRadius: '50%',
        border: '2px solid',
        borderColor: checked ?
        danger ? 'var(--error)' : 'var(--accent)' :
        'var(--border-strong)',
        marginTop: 2,
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center'
      }}>
        {checked && <span style={{ width: 8, height: 8, borderRadius: '50%',
          background: danger ? 'var(--error)' : 'var(--accent)' }} />}
      </span>
      <div>
        <div style={{ fontSize: 13, fontWeight: 600,
          color: danger ? 'var(--error-text)' : 'var(--text)' }}>{title}</div>
        <div style={{ fontSize: 11.5, color: 'var(--text-muted)', marginTop: 3,
          lineHeight: 1.45 }}>{desc}</div>
      </div>
    </button>);

}

function SourceDetailPanel({ source }) {
  if (!source) return null;
  return (
    <aside style={{
      borderLeft: '1px solid var(--border)', background: 'var(--surface)',
      display: 'flex', flexDirection: 'column', minHeight: 0
    }}>
      <div style={{ padding: '14px 18px', borderBottom: '1px solid var(--border)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-muted)',
            letterSpacing: '.06em', textTransform: 'uppercase' }}>Source detail</span>
          <div style={{ flex: 1 }} />
          <button className="btn sm ghost"><I.RefreshCw size={12} />Re-ingest</button>
        </div>
        <h3 style={{ margin: '6px 0 4px', fontSize: 15, fontWeight: 600, color: 'var(--text)' }}>
          {source.name}
        </h3>
        <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>
          {source.size} · uploaded {source.uploaded}{source.duration ? ` · ${source.duration}` : ''}
        </div>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', padding: '14px 18px' }}>
        {/* PII warning */}
        {source.warnings > 0 && source.status === 'ready' &&
        <div style={{
          padding: 12, marginBottom: 14,
          background: 'var(--warning-bg)', border: '1px solid var(--warning)',
          borderRadius: 'var(--radius-md)'
        }}>
            <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8 }}>
              <I.AlertTriangle size={14} style={{ color: 'var(--warning-text)', marginTop: 2 }} />
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--warning-text)', marginBottom: 4 }}>
                  Personal information detected
                </div>
                <p style={{ margin: '0 0 8px', fontSize: 12, color: 'var(--warning-text)', lineHeight: 1.5 }}>
                  Found {source.warnings} instance(s) of names + email + phone number in this source.
                </p>
                <div style={{ display: 'flex', gap: 6 }}>
                  <button className="btn sm primary" style={{ height: 26 }}>Approve as-is</button>
                  <button className="btn sm" style={{ height: 26 }}>Redact</button>
                  <button className="btn sm ghost danger" style={{ height: 26 }}>Remove</button>
                </div>
              </div>
            </div>
          </div>
        }

        {/* Failed state */}
        {source.status === 'failed' &&
        <div style={{
          padding: 12, marginBottom: 14,
          background: 'var(--error-bg)', borderRadius: 'var(--radius-md)'
        }}>
            <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8 }}>
              <I.AlertCircle size={14} style={{ color: 'var(--error-text)', marginTop: 2 }} />
              <div>
                <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--error-text)' }}>
                  Ingest failed
                </div>
                <p style={{ margin: '4px 0 0', fontSize: 12, color: 'var(--error-text)' }}>
                  Document parser encountered 3 unrecognised macros from Word 2019.
                  <a href="#" style={{ marginLeft: 6 }}>View log</a>
                </p>
              </div>
            </div>
          </div>
        }

        {/* Stats */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 14 }}>
          {source.paragraphs && <StatTile label="Paragraphs" value={source.paragraphs} />}
          {source.transcriptWords && <StatTile label="Transcript words" value={source.transcriptWords.toLocaleString()} />}
          {source.anecdotes != null && <StatTile label="Anecdotes detected" value={source.anecdotes} />}
          {source.duration && <StatTile label="Duration" value={source.duration} mono />}
        </div>

        {/* Parsed content */}
        {source.kind === 'doc' && source.status === 'ready' && <ParsedDoc />}
        {(source.kind === 'video' || source.kind === 'audio') && source.status === 'ready' && <Transcript />}

        {/* Ingesting */}
        {source.status === 'ingesting' && <IngestProgress progress={source.progress} />}
      </div>
    </aside>);

}

function StatTile({ label, value, mono }) {
  return (
    <div style={{
      padding: '8px 10px',
      background: 'var(--surface-inset)', borderRadius: 'var(--radius)'
    }}>
      <div style={{ fontSize: 11, color: 'var(--text-muted)' }}>{label}</div>
      <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text)',
        fontFamily: mono ? 'var(--font-mono)' : 'inherit' }}>{value}</div>
    </div>);

}

function ParsedDoc() {
  return (
    <>
      <SectHeader>Parsed paragraphs</SectHeader>
      <div style={{ display: 'grid', gap: 4 }}>
        {[
        'Sarah had been dreading the conversation all week.',
        'She thought about the SBI framework — Situation, Behavior, Impact — that her own manager had used three years earlier.',
        'Jordan had missed two deadlines and the team was beginning to whisper.',
        '"When you missed the API spec review on Tuesday…"'].
        map((t, i) =>
        <div key={i} style={{
          padding: '6px 10px', borderLeft: '2px solid var(--accent)',
          background: 'var(--surface-inset)', fontSize: 12, color: 'var(--text-muted)',
          lineHeight: 1.5, borderRadius: '0 4px 4px 0'
        }}>"{t}"</div>
        )}
      </div>
    </>);

}

function Transcript() {
  return (
    <>
      <SectHeader>Transcript</SectHeader>
      <div style={{ display: 'grid', gap: 4 }}>
        {[
        { t: '00:00', s: 'Welcome. I\'ve been a leadership coach for fifteen years now.' },
        { t: '00:12', s: 'And one thing I\'ve learned — most difficult conversations aren\'t about big things.' },
        { t: '00:24', s: 'They\'re about small things that have been allowed to compound.' }].
        map((seg, i) =>
        <div key={i} style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: 8,
          padding: '6px 0', fontSize: 12 }}>
            <span style={{ fontFamily: 'var(--font-mono)', color: 'var(--text-faint)',
            fontSize: 11 }}>{seg.t}</span>
            <span style={{ color: 'var(--text-muted)', lineHeight: 1.5 }}>{seg.s}</span>
          </div>
        )}
      </div>
    </>);

}

function IngestProgress({ progress = 0.62 }) {
  return (
    <div style={{
      padding: 16, textAlign: 'center',
      background: 'var(--surface-inset)', borderRadius: 'var(--radius-md)'
    }}>
      <I.Loader size={22} className="spin" style={{ color: 'var(--accent)' }} />
      <div style={{ marginTop: 8, fontSize: 13, fontWeight: 500 }}>Ingesting document…</div>
      <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 2 }}>
        Extracting paragraphs, building section graph, detecting anecdotes.
      </div>
      <div style={{ height: 4, background: 'var(--surface)', borderRadius: 2,
        marginTop: 12, overflow: 'hidden' }}>
        <div style={{ height: '100%', width: `${progress * 100}%`, background: 'var(--accent)',
          transition: 'width 400ms ease' }} />
      </div>
    </div>);

}

function SectHeader({ children }) {
  return <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-faint)',
    letterSpacing: '.06em', textTransform: 'uppercase', margin: '0 0 8px' }}>{children}</div>;
}

// ── Empty state for first-visit Sources ─────────────────────────────────────
function SourcesEmpty() {
  return (
    <div style={{
      height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center',
      background: 'var(--bg)', padding: 32
    }} data-screen-label="Surface 1 · Sources (empty)">
      <div style={{
        maxWidth: 560, padding: '64px 48px', textAlign: 'center',
        background: 'var(--surface)', border: '2px dashed var(--border-strong)',
        borderRadius: 'var(--radius-lg)'
      }}>
        <div style={{
          width: 64, height: 64, margin: '0 auto 20px',
          background: 'var(--accent-bg)', borderRadius: '50%',
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          color: 'var(--accent)'
        }}><I.Upload size={28} /></div>
        <h1 style={{ margin: '0 0 8px', fontSize: 19, fontWeight: 600 }}>
          Drop your Topic's source material
        </h1>
        <p style={{ margin: '0 0 24px', fontSize: 14, color: 'var(--text-muted)', lineHeight: 1.55 }}>
          Word docs, videos, or audio. Dynamo Authoring will ingest and prepare for rewriting.
        </p>
        <div style={{ display: 'flex', justifyContent: 'center', gap: 8, marginBottom: 16 }}>
          <button className="btn primary"><I.Upload size={14} />Choose files</button>
          <button className="btn">Paste from clipboard</button>
        </div>
        <div style={{ fontSize: 11.5, color: 'var(--text-faint)' }}>
          Accepts <code>.docx</code> · <code>.mp4</code> · <code>.mov</code> · <code>.webm</code> · <code>.mp3</code> · <code>.wav</code> · <code>.m4a</code>
        </div>
      </div>
    </div>);

}

Object.assign(window, { SurfaceSources, SourcesEmpty });