// CoachLedger — private per-party running observations, visible only to the subject (or god view)
// The key agent mechanism: longitudinal pattern awareness without weaponization.

// Inject pulse keyframe once
(function () {
  if (document.getElementById('ledger-styles')) return;
  const s = document.createElement('style');
  s.id = 'ledger-styles';
  s.textContent = `
    @keyframes ledger-ring {
      0%   { box-shadow: 0 0 0 0 var(--pulse-color, oklch(60% 0.2 30 / 0.7)); }
      70%  { box-shadow: 0 0 0 8px var(--pulse-color, oklch(60% 0.2 30 / 0)); }
      100% { box-shadow: 0 0 0 0 var(--pulse-color, oklch(60% 0.2 30 / 0)); }
    }
    .ledger-pulse { animation: ledger-ring 1.4s ease-out infinite; }
    @keyframes ledger-slide-in {
      from { transform: translateX(100%); opacity: 0; }
      to   { transform: translateX(0);    opacity: 1; }
    }
    .ledger-slide-in { animation: ledger-slide-in 0.22s cubic-bezier(0.25,0.46,0.45,0.94) both; }
    @keyframes ledger-toast {
      0%   { opacity: 0; transform: translateY(8px) scale(0.95); }
      15%  { opacity: 1; transform: translateY(0) scale(1); }
      75%  { opacity: 1; }
      100% { opacity: 0; transform: translateY(-4px); }
    }
    .ledger-toast { animation: ledger-toast 3.5s ease-out both; }
  `;
  document.head.appendChild(s);
})();

// Normalise a raw CG.ledger entry (which uses `text`) into the display shape
function normEntry(e) {
  const text = e.text || '';
  // Only show a separate note when the agent explicitly wrote one distinct from the main text.
  // When both come from the same `text` field, showing both is redundant.
  const distinctNote = (e.note && e.note !== text) ? e.note : '';
  return {
    ...e,
    title: e.title || text || '(observation)',
    note: distinctNote,
    // CG.ledger refs are thread timestamps, not human quotes — skip evidence display
    evidence: e.evidence || [],
    severity: e.severity === 'med' ? 'medium' : (e.severity || 'low'),
    acknowledged: e.acknowledged || false,
    suggested: e.suggested || null,
  };
}

// Detect live mode (same logic as phases-early.jsx)
function isLiveMode() {
  return (CG.hasKey && CG.hasKey() && CG.getMode && CG.getMode() === 'fresh') ||
         !!(CG.session && CG.session.isGuest && CG.session.isGuest());
}

// Hook: load live ledger entries for one seat, re-render on change
function useLiveLedger(seat) {
  const [entries, setEntries] = React.useState([]);
  React.useEffect(() => {
    if (!isLiveMode() || !seat || seat === 'god') return;
    let active = true;
    const load = async () => {
      const raw = await CG.ledger.read(seat);
      if (active) setEntries(raw.map(normEntry));
    };
    load();
    const unsub = CG.ledger.subscribe(() => { if (active) load(); });
    return () => { active = false; unsub(); };
  }, [seat]);
  return entries;
}

function CoachLedger({ seat, variant = "full", currentPhase, entriesOverride, hideHeader = false }) {
  const S = window.SCENARIO;
  const [openEntry, setOpenEntry] = React.useState(null);
  const [dismissed, setDismissed] = React.useState({});
  const [acked, setAcked] = React.useState({});

  const showA = seat === 'A' || seat === 'god';
  const showB = seat === 'B' || seat === 'god';

  // Prefer real session names over hardcoded scenario names
  const sessionNames = (CG.session && CG.session.getNames && CG.session.getNames()) || {};
  const resolvedName = (partyKey) => sessionNames[partyKey] || S.parties[partyKey].name;

  const getEntries = (partyKey) => {
    if (entriesOverride) return entriesOverride;
    if (isLiveMode()) return []; // live drawer feeds entries via entriesOverride
    return (partyKey === 'A' ? S.ledgerA : S.ledgerB) || [];
  };

  const renderLedger = (party, entries, partyKey) => {
    entries = entries || [];
    const name = resolvedName(partyKey);
    const phaseOrder = ['topic', 'intake', 'opening', 'steelman', 'room', 'callout', 'apology', 'artifact'];
    const grouped = phaseOrder.map(p => ({
      phase: p,
      items: entries.filter(e => e.phase === p && !dismissed[e.id]),
    })).filter(g => g.items.length > 0);

    return (
      <div style={{ flex: 1, minWidth: 0 }}>
        {!hideHeader && (
          <div style={{
            display: 'flex', alignItems: 'center', gap: 8,
            padding: '6px 10px',
            borderBottom: '1.5px solid ' + party.accent,
            marginBottom: 10,
          }}>
            <div style={{ width: 8, height: 8, background: party.accent, borderRadius: '50%' }} />
            <div className="mono" style={{ fontSize: '0.72rem', textTransform: 'uppercase', letterSpacing: '0.06em', color: party.accent }}>
              coach's ledger · {name.toLowerCase()}
            </div>
            <div style={{ flex: 1 }} />
            <span className="mono" style={{ fontSize: '0.65rem', color: 'var(--ink-3)' }}>
              {entries.filter(e => !dismissed[e.id]).length} observations
            </span>
          </div>
        )}

        {!hideHeader && (
          <div className="micro mb-3" style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--ink-3)', lineHeight: 1.4, padding: '0 4px' }}>
            private · only {name.toLowerCase()} sees this · coach adds entries as they notice things. dismiss anything you disagree with; the coach will push back once, then drop it.
          </div>
        )}

        {grouped.map(g => (
          <div key={g.phase} style={{ marginBottom: 16 }}>
            <div className="micro mb-2" style={{ color: 'var(--ink-4)', display: 'flex', alignItems: 'center', gap: 6 }}>
              <span>— {g.phase} —</span>
              {g.phase === currentPhase && <span className="stamp stamp-blue" style={{ fontSize: '0.55rem' }}>current</span>}
            </div>
            {g.items.map(e => (
              <LedgerEntry
                key={e.id}
                entry={{ ...e, acknowledged: acked[e.id] || e.acknowledged }}
                accent={party.accent}
                expanded={openEntry === e.id}
                onToggle={() => setOpenEntry(openEntry === e.id ? null : e.id)}
                onDismiss={() => setDismissed({ ...dismissed, [e.id]: true })}
                onAck={() => setAcked({ ...acked, [e.id]: true })}
              />
            ))}
          </div>
        ))}

        {grouped.length === 0 && (
          <div className="paper-card p-3" style={{ background: 'var(--paper-2)', textAlign: 'center' }}>
            <div className="hand" style={{ color: 'var(--ink-3)', fontSize: '1rem' }}>
              nothing in the ledger yet — or you've dismissed everything.
            </div>
          </div>
        )}
      </div>
    );
  };

  if (variant === "drawer") {
    return <CoachLedgerDrawer seat={seat} />;
  }

  return (
    <div style={{
      display: 'grid',
      gridTemplateColumns: (showA && showB) ? '1fr 1fr' : '1fr',
      gap: 24,
    }}>
      {showA && renderLedger(S.parties.A, getEntries('A'), 'A')}
      {showB && renderLedger(S.parties.B, getEntries('B'), 'B')}
    </div>
  );
}

function LedgerEntry({ entry, accent, expanded, onToggle, onDismiss, onAck }) {
  const sevColor = {
    high: 'var(--red)',
    medium: 'oklch(60% 0.15 85)',
    low: 'var(--ink-3)',
    '—': 'var(--green)',
  }[entry.severity];
  const kindGlyph = {
    pattern: '◎',
    strength: '✦',
    opening: '↗',
  }[entry.kind] || '·';

  return (
    <div
      onClick={onToggle}
      className="paper-card"
      style={{
        padding: '10px 12px', marginBottom: 8,
        cursor: 'pointer',
        borderLeft: `3px solid ${sevColor}`,
        background: expanded ? 'var(--paper-2)' : 'var(--paper)',
        transition: 'background 0.2s',
        position: 'relative',
      }}
    >
      <div style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
        <div style={{
          fontFamily: 'var(--font-mono)', fontSize: '0.9rem',
          color: sevColor, marginTop: 1,
          width: 14, textAlign: 'center',
        }}>{kindGlyph}</div>

        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8 }}>
            <div style={{ fontWeight: 500, fontSize: '0.9rem', lineHeight: 1.3 }}>
              {entry.title}
            </div>
            <div className="mono" style={{ fontSize: '0.6rem', color: 'var(--ink-4)', whiteSpace: 'nowrap' }}>
              {entry.kind === 'strength' ? 'strength' : entry.severity === '—' ? '' : entry.severity}
              {entry.acknowledged && <span style={{ marginLeft: 6, color: 'var(--green)' }}>✓ ack'd</span>}
            </div>
          </div>

          {entry.note && (
            <div style={{ fontSize: '0.85rem', lineHeight: 1.45, color: 'var(--ink-2)', marginTop: 4 }}>
              {entry.note}
            </div>
          )}

          {expanded && (
            <div className="fade-up" style={{ marginTop: 10 }}>
              {entry.evidence && entry.evidence.length > 0 && (
                <>
                  <div className="micro mb-1">evidence · cited across the conversation</div>
                  <ul style={{ margin: '0 0 10px 16px', padding: 0, fontSize: '0.78rem', lineHeight: 1.5, color: 'var(--ink-3)' }}>
                    {entry.evidence.map((ev, i) => (
                      <li key={i}>{ev}</li>
                    ))}
                  </ul>
                </>
              )}
              {entry.suggested && (
                <div style={{
                  padding: '8px 10px',
                  background: 'oklch(96% 0.04 95)',
                  borderLeft: '2px solid oklch(75% 0.1 95)',
                  marginBottom: 10,
                  fontStyle: 'italic',
                  fontSize: '0.82rem',
                  lineHeight: 1.5,
                }}>
                  <span className="hand" style={{ color: 'oklch(40% 0.1 90)', fontSize: '1rem' }}>suggestion · </span>
                  {entry.suggested}
                </div>
              )}
              <div style={{ display: 'flex', gap: 6 }}>
                <button className="btn btn-sm" onClick={(e) => { e.stopPropagation(); if (onAck) onAck(); }}>
                  {entry.acknowledged ? "acknowledged" : "acknowledge"}
                </button>
                <button className="btn btn-sm btn-ghost" onClick={(e) => { e.stopPropagation(); onDismiss(); }}>
                  dismiss · i disagree
                </button>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

function CoachLedgerDrawer({ seat }) {
  const [open, setOpen] = React.useState(false);
  const [seenCount, setSeenCount] = React.useState(0);
  const [toasts, setToasts] = React.useState([]); // [{ id, text }]
  const S = window.SCENARIO;

  const live = isLiveMode();

  // Live entries from CG.ledger
  const liveEntries = useLiveLedger(live ? seat : null);

  // Canned fallback
  const cannedEntries = live ? [] : ((seat === 'A' ? S.ledgerA : S.ledgerB) || []);

  const entries = live ? liveEntries : cannedEntries;
  const newCount = Math.max(0, entries.length - seenCount);

  // Toast on new entry while drawer is closed
  const prevLenRef = React.useRef(entries.length);
  React.useEffect(() => {
    const prev = prevLenRef.current;
    prevLenRef.current = entries.length;
    if (entries.length > prev && !open) {
      const newest = entries[entries.length - 1];
      const text = newest ? `coach noticed: ${newest.title}` : 'coach added an observation';
      const id = Date.now();
      setToasts(t => [...t, { id, text }]);
      setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 3600);
    }
  }, [entries.length, open]);

  // Mark all seen when drawer opens
  const handleOpen = () => {
    setOpen(true);
    setSeenCount(entries.length);
  };

  if (seat === 'god') return null;

  const party = seat === 'A' ? S.parties.A : S.parties.B;
  const sessionNames = (CG.session && CG.session.getNames && CG.session.getNames()) || {};
  const myName = sessionNames[seat] || party.name;
  const pending = entries.filter(e => !e.acknowledged);
  const hasNew = newCount > 0;

  return (
    <>
      {/* Toast notifications — bottom-left to avoid clashing with button */}
      <div style={{ position: 'fixed', bottom: 80, left: 18, zIndex: 60, display: 'flex', flexDirection: 'column', gap: 6, pointerEvents: 'none' }}>
        {toasts.map(t => (
          <div key={t.id} className="ledger-toast paper-card" style={{
            padding: '8px 12px',
            fontSize: '0.78rem',
            borderLeft: `3px solid ${party.accent}`,
            background: 'var(--paper)',
            boxShadow: '0 2px 10px rgba(0,0,0,0.15)',
            maxWidth: 260,
            color: 'var(--ink-2)',
          }}>
            {t.text}
          </div>
        ))}
      </div>

      {/* Drawer toggle button */}
      <button
        onClick={handleOpen}
        className={`paper-card${hasNew ? ' ledger-pulse' : ''}`}
        style={{
          position: 'fixed', bottom: 18, right: 18, zIndex: 40,
          padding: '10px 14px',
          display: 'flex', alignItems: 'center', gap: 10,
          cursor: 'pointer',
          border: `1px solid ${hasNew ? party.accent : party.accent}`,
          borderWidth: hasNew ? 2 : 1,
          background: 'var(--paper)',
          boxShadow: '0 4px 12px oklch(30% 0.05 60 / 0.2)',
          '--pulse-color': party.accent,
        }}
      >
        <div style={{
          width: 8, height: 8, borderRadius: '50%',
          background: party.accent,
          flexShrink: 0,
        }} />
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
          <div className="mono" style={{ fontSize: '0.65rem', textTransform: 'uppercase', letterSpacing: '0.06em', color: party.accent }}>
            coach's ledger · private
          </div>
          <div style={{ fontSize: '0.78rem', color: 'var(--ink-2)', display: 'flex', alignItems: 'center', gap: 6 }}>
            {entries.length} observations · {pending.length} pending
            {hasNew && (
              <span style={{
                background: party.accent,
                color: 'var(--paper)',
                borderRadius: 8,
                padding: '1px 6px',
                fontSize: '0.65rem',
                fontFamily: 'var(--font-mono)',
                fontWeight: 600,
              }}>
                {newCount} new
              </span>
            )}
          </div>
        </div>
      </button>

      {open && (
        <div
          style={{
            position: 'fixed', inset: 0, zIndex: 50,
            background: 'rgba(35, 30, 22, 0.4)',
            display: 'flex', justifyContent: 'flex-end',
          }}
          onClick={() => setOpen(false)}
        >
          <div
            className="ledger-slide-in"
            onClick={e => e.stopPropagation()}
            style={{
              width: 480, maxWidth: '90vw', height: '100%',
              background: 'var(--paper)', padding: '24px 22px',
              overflowY: 'auto',
              boxShadow: '-4px 0 20px rgba(0,0,0,0.15)',
              borderLeft: `3px solid ${party.accent}`,
            }}
          >
            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 14 }}>
              <div>
                <div className="tiny" style={{ color: party.accent }}>coach's ledger · private · only you see this</div>
                <div style={{ fontSize: '1.1rem', fontWeight: 500 }}>what i've noticed, {myName.toLowerCase()}</div>
              </div>
              <button className="btn btn-sm btn-ghost" onClick={() => setOpen(false)}>close</button>
            </div>
            <CoachLedger seat={seat} variant="full" entriesOverride={entries} hideHeader={true} />
          </div>
        </div>
      )}
    </>
  );
}

Object.assign(window, { CoachLedger, CoachLedgerDrawer, LedgerEntry });
