// Fabric page — cross-source entity view (Overview / Graph / Timeline)
// Hero: Acme Corp resolved from 6 sources.

const { useState: useFb, useEffect: useFbE, useRef: useFbR, useMemo: useFbM } = React;

function FabricPage({ openEntity, reveal, onRevealConsumed }) {
  const [tab, setTab] = useFb('overview');
  const [pulse, setPulse] = useFb(0);
  const [snapshot, setSnapshot] = useFb(null);
  const [snapLoading, setSnapLoading] = useFb(false);
  const [snapError, setSnapError] = useFb(null);
  const [reloadNonce, setReloadNonce] = useFb(0);

  // Heartbeat to animate "live" indicators
  useFbE(() => {
    const t = setInterval(() => setPulse(p => p + 1), 2800);
    return () => clearInterval(t);
  }, []);

  // Load graph snapshot once (and on reload). Also subscribe to live
  // updates pushed by applyMutationResponse / applyAskGraphDelta so edits
  // performed inside the side panel (or elsewhere in the app) reflect in
  // the graph view without a manual refresh.
  useFbE(() => {
    let cancelled = false;
    setSnapLoading(true);
    setSnapError(null);
    fetchGraphSnapshotCached()
      .then(s => { if (!cancelled) { setSnapshot(s); setSnapLoading(false); } })
      .catch(e => { if (!cancelled) { setSnapError(e.message || String(e)); setSnapLoading(false); } });
    const unsub = typeof subscribeGraphSnapshot === 'function'
      ? subscribeGraphSnapshot(s => { if (!cancelled) setSnapshot(s); })
      : null;
    return () => { cancelled = true; if (unsub) unsub(); };
  }, [reloadNonce]);

  const reloadSnapshot = () => { invalidateGraphSnapshot(); setReloadNonce(n => n + 1); };

  // A learned-concept reveal request (from the global toast) forces the graph
  // tab so the reveal sequence has a canvas to play on.
  useFbE(() => {
    if (reveal && reveal.ids && reveal.ids.length) setTab('graph');
  }, [reveal && reveal.token]);

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
      {/* Hero header — the Acme entity card */}
      <FabricHero pulse={pulse} setTab={setTab} tab={tab} />

      {/* Tab content */}
      <div style={{ flex: 1, overflowY: 'auto' }}>
        {tab === 'overview' && <OverviewTab pulse={pulse} openEntity={openEntity} />}
        {tab === 'graph'    && <GraphTab snapshot={snapshot} loading={snapLoading} error={snapError} reload={reloadSnapshot} openEntity={openEntity} reveal={reveal} onRevealConsumed={onRevealConsumed} />}
        {tab === 'timeline' && <TimelineTab pulse={pulse} />}
        {tab === 'catalog'  && <CatalogTab snapshot={snapshot} loading={snapLoading} error={snapError} reload={reloadSnapshot} openEntity={openEntity} />}
      </div>
    </div>
  );
}

// --------- HERO HEADER ---------

function FabricHero({ pulse, tab, setTab }) {
  return (
    <div style={{
      borderBottom: '1px solid var(--border-subtle)',
      padding: '24px 32px 0',
      background: 'var(--bg)',
    }}>
      {/* Row 1: icon + title + actions (actions wrap below on narrow) */}
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 20, flexWrap: 'wrap' }}>
        {/* Entity icon */}
        <div style={{
          width: 56, height: 56, borderRadius: 12,
          background: 'linear-gradient(135deg, #E7F4F2, #D4ECE8)',
          border: '1px solid var(--border-subtle)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          flexShrink: 0,
          position: 'relative',
        }}>
          <span style={{ fontSize: 22, fontWeight: 600, color: '#0A5A54' }}>A</span>
          <span style={{
            position: 'absolute', bottom: -5, right: -5,
            padding: '1px 6px', borderRadius: 10,
            background: 'var(--surface)', border: '1px solid var(--border)',
            fontSize: 10, fontWeight: 500, color: 'var(--text-secondary)',
          }}>Org</span>
        </div>

        {/* Title column */}
        <div style={{ flex: 1, minWidth: 260 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap', rowGap: 4 }}>
            <h1 className="t-h1" style={{ margin: 0, whiteSpace: 'nowrap' }}>{ACME.name}</h1>
            <span className="mono-sm mono text-tertiary" style={{ whiteSpace: 'nowrap' }}>{ACME.domain}</span>
            <LivePulse pulse={pulse} />
          </div>
          <div className="t-body text-secondary" style={{ marginTop: 6 }}>
            {ACME.industry} · {ACME.employees} employees · {ACME.arr} ARR · Owner <span style={{ color: 'var(--text-primary)' }}>{ACME.owner}</span>
          </div>
        </div>

        {/* Actions (wrap to new row on narrow viewports) */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8, alignItems: 'flex-end', flexShrink: 0 }}>
          <HealthBadge health={ACME.health} note={ACME.healthNote} />
          <div style={{ display: 'flex', gap: 6 }}>
            <button className="btn sm">Open in CRM <Icons.External size={11}/></button>
            <button className="btn sm ghost"><Icons.More size={13}/></button>
          </div>
        </div>
      </div>

      {/* Row 2: aliases */}
      <div style={{ display: 'flex', gap: 6, marginTop: 14, flexWrap: 'wrap', alignItems: 'center' }}>
        <span className="t-micro text-tertiary" style={{ whiteSpace: 'nowrap' }}>ALSO KNOWN AS</span>
        {ACME.aka.map(a => (
          <span key={a} className="chip" style={{ fontSize: 11, whiteSpace: 'nowrap' }}>{a}</span>
        ))}
      </div>

      {/* Row 3: source strip */}
      <div style={{ marginTop: 14, display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
        <span className="t-micro text-tertiary" style={{ whiteSpace: 'nowrap' }}>RESOLVED FROM</span>
        {ACME.sources.map((s, i) => (
          <SourcePill key={s.id} src={s} pulse={pulse} index={i} />
        ))}
        <span className="t-micro text-tertiary" style={{ whiteSpace: 'nowrap' }}>
          · {ACME.sources.length} sources · continuously
        </span>
      </div>

      {/* Tabs */}
      <div style={{ marginTop: 20, display: 'flex', gap: 4, flexWrap: 'wrap' }}>
        {[
          ['overview', 'Overview'],
          ['graph',    'Graph'],
          ['timeline', 'Merged timeline'],
          ['catalog',  'Catalog'],
        ].map(([id, label]) => (
          <button key={id} onClick={() => setTab(id)} style={{
            padding: '10px 14px',
            fontSize: 13, fontWeight: tab === id ? 500 : 400,
            color: tab === id ? 'var(--text-primary)' : 'var(--text-secondary)',
            borderBottom: `2px solid ${tab === id ? 'var(--accent)' : 'transparent'}`,
            marginBottom: -1,
            whiteSpace: 'nowrap',
          }}>{label}</button>
        ))}
      </div>
    </div>
  );
}

function SourcePill({ src, pulse, index }) {
  const v = VENDORS[src.vendor];
  const live = (pulse % ACME.sources.length) === index;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      padding: '3px 8px 3px 4px',
      borderRadius: 20,
      background: 'var(--surface)',
      border: '1px solid var(--border-subtle)',
      fontSize: 11, fontWeight: 500,
      color: 'var(--text-primary)',
      whiteSpace: 'nowrap',
      flexShrink: 0,
    }}>
      <VendorLogo vendor={src.vendor} size={16} radius={4} />
      {v.name}
      {live && (
        <span style={{
          width: 6, height: 6, borderRadius: '50%',
          background: 'var(--success)',
          animation: 'parclePulse 1.6s ease-out',
        }}/>
      )}
    </span>
  );
}

function HealthBadge({ health, note }) {
  const conf = {
    good:      { label: 'Healthy',   bg: 'var(--success-bg)', fg: 'var(--success)', dot: 'var(--success)' },
    attention: { label: 'Attention', bg: 'var(--warning-bg)', fg: 'var(--warning)', dot: 'var(--warning)' },
    risk:      { label: 'At risk',   bg: 'var(--error-bg)',   fg: 'var(--error)',   dot: 'var(--error)' },
  }[health];
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: 8,
      padding: '5px 12px', borderRadius: 999,
      background: conf.bg, color: conf.fg,
      fontSize: 12, fontWeight: 500,
      whiteSpace: 'nowrap',
    }} title={note}>
      <span style={{ width: 6, height: 6, borderRadius: '50%', background: conf.dot, flexShrink: 0 }}/>
      {conf.label}
      <span style={{ fontWeight: 400, opacity: 0.85 }}>· {note}</span>
    </div>
  );
}

function LivePulse({ pulse }) {
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 11, color: 'var(--text-tertiary)', whiteSpace: 'nowrap' }}>
      <span key={pulse} style={{
        width: 7, height: 7, borderRadius: '50%',
        background: 'var(--success)',
        animation: 'parclePulse 1.6s ease-out',
      }}/>
      Last signal {ACME.lastSignal}
    </span>
  );
}

// --------- OVERVIEW TAB ---------

function OverviewTab({ pulse, openEntity }) {
  return (
    <div style={{ padding: '24px 32px', display: 'flex', flexDirection: 'column', gap: 24 }}>
      {/* Aggregate stats strip */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 }}>
        {Object.entries(FABRIC_METRICS).map(([k, m]) => (
          <div key={k} className="card" style={{ padding: 16 }}>
            <div className="t-micro text-tertiary">
              {k === 'entities'  ? 'Entities indexed' :
               k === 'relations' ? 'Relationships inferred' :
               k === 'resolved'  ? 'Cross-source merges' :
                                   'Learnings this month'}
            </div>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginTop: 6 }}>
              <span style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.01em' }}>
                {typeof m.total === 'number' ? m.total.toLocaleString() : m.total}
              </span>
              <span className="mono-sm mono" style={{ color: 'var(--success)' }}>{m.delta}</span>
            </div>
            <div style={{ marginTop: 10 }}>
              <Sparkline data={m.spark} color="var(--accent)" height={28} filled />
            </div>
          </div>
        ))}
      </div>

      {/* Two columns: left = merge evidence, right = people */}
      <div style={{ display: 'grid', gridTemplateColumns: '1.15fr 1fr', gap: 20 }}>

        {/* Merge evidence card */}
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <div style={{ padding: '14px 18px', borderBottom: '1px solid var(--border-subtle)', display: 'flex', alignItems: 'center', gap: 10 }}>
            <Icons.Sparkle size={14} style={{ color: 'var(--accent)' }}/>
            <div style={{ fontSize: 13, fontWeight: 500 }}>How we resolved this entity</div>
            <div style={{ flex: 1 }}/>
            <span className="chip success" style={{ fontSize: 11 }}>
              <span className="dot"/> Confidence 0.97
            </span>
          </div>
          <div style={{ padding: 6 }}>
            {ACME.sources.map((s, i) => (
              <MergeRow key={s.id} src={s} first={i === 0}/>
            ))}
          </div>
          <div style={{ padding: '10px 18px', borderTop: '1px solid var(--border-subtle)', display: 'flex', alignItems: 'center', gap: 10, background: 'var(--bg)' }}>
            <Icons.Check size={13} style={{ color: 'var(--success)' }}/>
            <span className="t-small text-secondary" style={{ flex: 1 }}>
              All matches auto-accepted. No review queue items for this entity.
            </span>
            <button className="btn sm ghost">View merge log</button>
          </div>
        </div>

        {/* People at Acme */}
        <div className="card" style={{ padding: 0 }}>
          <div style={{ padding: '14px 18px', borderBottom: '1px solid var(--border-subtle)', display: 'flex', alignItems: 'center' }}>
            <div style={{ fontSize: 13, fontWeight: 500 }}>People at Acme Corp</div>
            <div style={{ flex: 1 }}/>
            <span className="mono-sm mono text-tertiary">{ACME.people.length} resolved</span>
          </div>
          <div>
            {ACME.people.map(p => (
              <button key={p.name} onClick={() => openEntity && openEntity({ id: 'p_' + p.name, type: 'Person', label: p.name, sub: p.role })} style={{
                width: '100%', display: 'flex', alignItems: 'center', gap: 12,
                padding: '10px 18px', textAlign: 'left',
                borderBottom: '1px solid var(--border-subtle)',
              }}>
                <Avatar name={p.name} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13, fontWeight: 500 }}>{p.name} {p.primary && <span className="chip accent" style={{ marginLeft: 6, fontSize: 10 }}>Champion</span>}</div>
                  <div className="mono-sm mono text-tertiary">{p.role}</div>
                </div>
                <div style={{ display: 'flex', gap: 2 }}>
                  {p.sources.map(s => <VendorLogo key={s} vendor={s} size={16} radius={3} />)}
                </div>
                <Icons.ChevronR size={12} style={{ color: 'var(--text-tertiary)' }}/>
              </button>
            ))}
          </div>
        </div>
      </div>

      {/* Recent learnings (mini stream) */}
      <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
        <div style={{ padding: '14px 18px', borderBottom: '1px solid var(--border-subtle)', display: 'flex', alignItems: 'center', gap: 10 }}>
          <span style={{
            width: 7, height: 7, borderRadius: '50%',
            background: 'var(--success)', animation: 'parclePulseLoop 2s ease-out infinite',
          }}/>
          <div style={{ fontSize: 13, fontWeight: 500 }}>What the system learned about Acme — last 24h</div>
          <div style={{ flex: 1 }}/>
          <span className="mono-sm mono text-tertiary">8 events</span>
        </div>
        <div>
          {LEARNING_STREAM.slice(0, 4).map(l => <LearningRowMini key={l.id} row={l}/>)}
        </div>
      </div>
    </div>
  );
}

function MergeRow({ src, first }) {
  const v = VENDORS[src.vendor];
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 12px', borderRadius: 8 }}>
      <VendorLogo vendor={src.vendor} size={28} radius={7}/>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, fontWeight: 500 }}>
          {v.name} · <span className="text-secondary" style={{ fontWeight: 400 }}>{src.kind}</span>
          {src.primary && <span className="chip accent" style={{ marginLeft: 8, fontSize: 10 }}>Primary</span>}
        </div>
        <div className="mono-sm mono text-tertiary" style={{ marginTop: 2 }}>
          Resolved by: {src.resolvedBy}
        </div>
      </div>
      <div style={{ textAlign: 'right' }}>
        <div className="mono-sm mono" style={{ fontSize: 12, fontWeight: 500 }}>{src.count}</div>
        <ConfBar conf={src.conf}/>
      </div>
    </div>
  );
}

function ConfBar({ conf }) {
  const pct = Math.round(conf * 100);
  const color = conf >= 0.95 ? 'var(--success)' : conf >= 0.85 ? 'var(--accent)' : 'var(--warning)';
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, marginTop: 2 }}>
      <div style={{ width: 36, height: 3, borderRadius: 2, background: 'var(--border-subtle)' }}>
        <div style={{ width: `${pct}%`, height: '100%', borderRadius: 2, background: color }}/>
      </div>
      <span className="mono-sm mono" style={{ fontSize: 10, color: 'var(--text-tertiary)' }}>{conf.toFixed(2)}</span>
    </div>
  );
}

function Avatar({ name, size = 28 }) {
  const initials = name.split(' ').map(p => p[0]).slice(0, 2).join('');
  const hash = name.charCodeAt(0) + name.charCodeAt(1);
  const hues = [200, 25, 260, 140, 340, 180, 50];
  const hue = hues[hash % hues.length];
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%',
      background: `hsl(${hue} 45% 88%)`,
      color: `hsl(${hue} 40% 32%)`,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontSize: 11, fontWeight: 600, flexShrink: 0,
    }}>{initials}</div>
  );
}

function LearningRowMini({ row }) {
  const statusStyle = {
    auto:          { chip: 'accent',  text: 'Auto' },
    applied:       { chip: 'success', text: 'Applied' },
    needs_review:  { chip: 'warning', text: 'Review' },
  }[row.status];
  return (
    <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, padding: '12px 18px', borderBottom: '1px solid var(--border-subtle)' }}>
      <div style={{ width: 28, flexShrink: 0 }}>
        <LearningGlyph type={row.type} />
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, fontWeight: 500 }}>{row.title}</div>
        <div className="t-small text-secondary" style={{ marginTop: 2 }}>{row.body}</div>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4, flexShrink: 0 }}>
        <span className="mono-sm mono text-tertiary">{row.t}</span>
        <span className={`chip ${statusStyle.chip}`} style={{ fontSize: 10 }}>{statusStyle.text}</span>
      </div>
    </div>
  );
}

function LearningGlyph({ type }) {
  const conf = {
    entity:   { color: '#0F766E', icon: <path d="M12 3l9 6v6l-9 6-9-6V9z M3 9l9 6 9-6 M12 15v6"/> },
    relation: { color: '#4F46E5', icon: <path d="M5 12h14 M13 6l6 6-6 6"/> },
    rerank:   { color: '#B45309', icon: <path d="M7 4v16 M13 8v12 M19 12v8 M3 20h18"/> },
    // dedupe subsumes what used to be a separate `synonym` type — both are concept merges
    dedupe:   { color: '#475569', icon: <><path d="M9 9h10v10H9z"/><path d="M5 5h10v10H5z" fill="rgba(71,85,105,0.12)"/></> },
    gap:      { color: '#B91C1C', icon: <path d="M12 3l10 18H2z M12 10v5M12 18v.5"/> },
    schema:   { color: '#6B21A8', icon: <><path d="M4 4h16v5H4z"/><path d="M4 11h16v5H4z"/><path d="M4 18h16v3H4z"/></> },
  }[type] || { color: '#6B7280', icon: <circle cx="12" cy="12" r="4"/> };
  return (
    <div style={{
      width: 28, height: 28, borderRadius: 7,
      background: conf.color + '18',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}>
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke={conf.color} strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
        {conf.icon}
      </svg>
    </div>
  );
}

Object.assign(window, { FabricPage });
