/**
 * HumanApprovalModal.jsx
 * Owns: rendering a blocking modal for human-in-the-loop agent action approval.
 * Does NOT own: routing, session state, or multi-modal orchestration.
 *
 * AG-UI events consumed:
 *   STATE_DELTA  { detail: { path, value } }  — listens for pending approval state
 *
 * AG-UI events emitted:
 *   USER_MESSAGE  { detail: { role:'human', content, approvalId, decision } }
 *
 * Props:
 *   approval: {
 *     id: string,
 *     title: string,          // action title shown in header
 *     agentName?: string,     // agent name shown below title
 *     description: string,    // plain-language what the agent wants to do
 *     previewMode: 'text'|'diff'|'json',  // how to render the preview block
 *     preview: string,        // content for the preview block
 *     highStakes?: boolean,   // shows warning styling
 *   }
 *   onApprove: (approvalId: string) => void
 *   onReject:  (approvalId: string, reason?: string) => void
 *   onEdit:    (approvalId: string, editedPayload: string) => void
 *   theme?: 'light'|'dark'
 *
 * Zero dependencies beyond React.
 */

const { useState, useEffect, useRef, useCallback } = React;

// ─── Design tokens ─────────────────────────────────────────────────────────────
const TOKENS = {
  light: {
    backdrop: 'rgba(0,0,0,0.45)',
    bg: '#FFFFFF',
    border: '#E8E6E0',
    borderStrong: '#D0CEC8',
    fg: '#1a1a1a',
    fgMuted: '#666',
    fgSubtle: '#999',
    accent: '#F5A623',
    success: '#22c55e',
    successBg: 'rgba(34,197,94,0.08)',
    successBorder: 'rgba(34,197,94,0.25)',
    error: '#ef4444',
    errorBg: 'rgba(239,68,68,0.08)',
    errorBorder: 'rgba(239,68,68,0.25)',
    warning: '#f59e0b',
    warningBg: 'rgba(245,158,11,0.08)',
    warningBorder: 'rgba(245,158,11,0.3)',
    codeBg: '#F3F2EF',
    codeText: '#2d2d2d',
    diffAdd: '#dcfce7',
    diffAddText: '#166534',
    diffRemove: '#fee2e2',
    diffRemoveText: '#991b1b',
    btnPrimary: '#22c55e',
    btnPrimaryHover: '#16a34a',
    btnSecondary: '#FFFFFF',
    btnTertiary: 'transparent',
    inputBg: '#FAFAF8',
    shadow: '0 24px 64px rgba(0,0,0,0.18), 0 4px 16px rgba(0,0,0,0.08)',
    avatarBg: '#F3F2EF',
  },
  dark: {
    backdrop: 'rgba(0,0,0,0.65)',
    bg: '#1C1C1A',
    border: '#2E2E2C',
    borderStrong: '#3E3E3A',
    fg: '#F0EEE8',
    fgMuted: '#888',
    fgSubtle: '#555',
    accent: '#F5A623',
    success: '#4ade80',
    successBg: 'rgba(74,222,128,0.08)',
    successBorder: 'rgba(74,222,128,0.25)',
    error: '#f87171',
    errorBg: 'rgba(248,113,113,0.08)',
    errorBorder: 'rgba(248,113,113,0.25)',
    warning: '#fbbf24',
    warningBg: 'rgba(251,191,36,0.08)',
    warningBorder: 'rgba(251,191,36,0.3)',
    codeBg: '#141413',
    codeText: '#c9c5bc',
    diffAdd: '#14532d',
    diffAddText: '#86efac',
    diffRemove: '#7f1d1d',
    diffRemoveText: '#fca5a5',
    btnPrimary: '#4ade80',
    btnPrimaryHover: '#22c55e',
    btnSecondary: '#2A2A28',
    btnTertiary: 'transparent',
    inputBg: '#111110',
    shadow: '0 24px 64px rgba(0,0,0,0.6), 0 4px 16px rgba(0,0,0,0.3)',
    avatarBg: '#2E2E2C',
  },
};

// ─── CSS injected once ─────────────────────────────────────────────────────────
const HAM_STYLES = `
@keyframes ham-backdrop-in { from{opacity:0} to{opacity:1} }
@keyframes ham-modal-in { from{opacity:0;transform:scale(0.96) translateY(6px)} to{opacity:1;transform:scale(1) translateY(0)} }
@keyframes ham-spin { to { transform: rotate(360deg); } }
@keyframes ham-shake { 0%,100%{transform:translateX(0)} 20%,60%{transform:translateX(-4px)} 40%,80%{transform:translateX(4px)} }
`;

let _hamStylesInjected = false;
function injectHAMStyles() {
  if (_hamStylesInjected || typeof document === 'undefined') return;
  const el = document.createElement('style');
  el.textContent = HAM_STYLES;
  document.head.appendChild(el);
  _hamStylesInjected = true;
}

// ─── Preview: diff renderer ────────────────────────────────────────────────────
function DiffPreview({ content, tok }) {
  const lines = (content || '').split('\n');
  return (
    <div style={{
      fontFamily: 'monospace', fontSize: '0.78rem', lineHeight: 1.6,
      borderRadius: 8, overflow: 'hidden', border: `1px solid ${tok.border}`,
    }}>
      {lines.map((line, i) => {
        let bg = 'transparent';
        let color = tok.codeText;
        let prefix = ' ';
        if (line.startsWith('+')) { bg = tok.diffAdd; color = tok.diffAddText; prefix = '+'; }
        else if (line.startsWith('-')) { bg = tok.diffRemove; color = tok.diffRemoveText; prefix = '-'; }
        return (
          <div key={i} style={{
            background: bg, color, padding: '0 12px',
            whiteSpace: 'pre-wrap', wordBreak: 'break-all',
          }}>
            {line}
          </div>
        );
      })}
    </div>
  );
}

// ─── Preview: JSON renderer ────────────────────────────────────────────────────
function JsonPreview({ content, tok }) {
  let formatted = content;
  try { formatted = JSON.stringify(JSON.parse(content), null, 2); } catch {}
  return (
    <div style={{
      background: tok.codeBg, border: `1px solid ${tok.border}`,
      borderRadius: 8, padding: '0.875rem 1rem',
      maxHeight: 240, overflow: 'auto',
    }}>
      <pre style={{
        margin: 0, fontFamily: 'monospace', fontSize: '0.78rem',
        lineHeight: 1.6, color: tok.codeText,
        whiteSpace: 'pre-wrap', wordBreak: 'break-all',
      }}>
        {formatted}
      </pre>
    </div>
  );
}

// ─── Preview: text renderer ────────────────────────────────────────────────────
function TextPreview({ content, tok }) {
  return (
    <div style={{
      background: tok.codeBg, border: `1px solid ${tok.border}`,
      borderRadius: 8, padding: '0.875rem 1rem',
    }}>
      <p style={{
        margin: 0, fontFamily: "'Space Grotesk', sans-serif",
        fontSize: '0.875rem', lineHeight: 1.65, color: tok.fg,
        whiteSpace: 'pre-wrap',
      }}>
        {content}
      </p>
    </div>
  );
}

// ─── Preview dispatcher ────────────────────────────────────────────────────────
function PreviewBlock({ mode, content, tok }) {
  if (mode === 'diff') return <DiffPreview content={content} tok={tok} />;
  if (mode === 'json') return <JsonPreview content={content} tok={tok} />;
  return <TextPreview content={content} tok={tok} />;
}

// ─── Spinner ───────────────────────────────────────────────────────────────────
function Spinner({ color, size = 14 }) {
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%',
      border: `2px solid ${color}40`,
      borderTopColor: color,
      animation: 'ham-spin 0.65s linear infinite',
      flexShrink: 0,
    }} />
  );
}

// ─── Agent avatar ──────────────────────────────────────────────────────────────
function AgentAvatar({ name, tok }) {
  const initials = (name || 'A').slice(0, 2).toUpperCase();
  return (
    <div style={{
      width: 36, height: 36, borderRadius: '50%',
      background: tok.avatarBg, border: `1.5px solid ${tok.border}`,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontSize: '0.75rem', fontWeight: 700, color: tok.fgMuted,
      flexShrink: 0,
    }}>
      {initials}
    </div>
  );
}

// ─── HumanApprovalModal ────────────────────────────────────────────────────────
/**
 * Main component. Renders a blocking overlay modal.
 * Backdrop click does NOT dismiss — approval is an explicit choice.
 * Keyboard: Enter = approve, Esc = reject, Cmd/Ctrl+E = edit.
 */
function HumanApprovalModal({ approval, onApprove, onReject, onEdit, theme = 'light' }) {
  injectHAMStyles();
  const tok = TOKENS[theme] || TOKENS.light;

  // ui state
  const [mode, setMode] = useState('review'); // 'review' | 'reject' | 'edit'
  const [reason, setReason] = useState('');
  const [editedPayload, setEditedPayload] = useState(approval?.preview || '');
  const [loading, setLoading] = useState(null); // 'approve'|'reject'|'edit'|null
  const [shaking, setShaking] = useState(false);

  const reasonRef = useRef(null);
  const editRef = useRef(null);

  // focus textarea when entering reject/edit mode
  useEffect(() => {
    if (mode === 'reject' && reasonRef.current) reasonRef.current.focus();
    if (mode === 'edit' && editRef.current) editRef.current.focus();
  }, [mode]);

  // keyboard shortcuts
  useEffect(() => {
    function onKey(e) {
      if (loading) return;

      // Cmd/Ctrl+E → edit
      if ((e.metaKey || e.ctrlKey) && e.key === 'e') {
        e.preventDefault();
        setMode('edit');
        setEditedPayload(approval?.preview || '');
        return;
      }

      // Esc → if in edit/reject mode, go back; otherwise reject
      if (e.key === 'Escape') {
        e.preventDefault();
        if (mode === 'edit' || mode === 'reject') {
          setMode('review');
        } else {
          handleReject();
        }
        return;
      }

      // Enter (not in textarea) → approve
      if (e.key === 'Enter' && e.target.tagName !== 'TEXTAREA') {
        e.preventDefault();
        if (mode === 'review') handleApprove();
        return;
      }
    }
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [mode, loading, approval, reason, editedPayload]);

  function handleApprove() {
    if (loading) return;
    setLoading('approve');
    Promise.resolve(onApprove && onApprove(approval?.id)).finally(() => setLoading(null));
  }

  function handleReject() {
    if (loading) return;
    if (mode !== 'reject') { setMode('reject'); return; }
    setLoading('reject');
    Promise.resolve(onReject && onReject(approval?.id, reason)).finally(() => setLoading(null));
  }

  function handleEdit() {
    if (loading) return;
    if (mode !== 'edit') {
      setMode('edit');
      setEditedPayload(approval?.preview || '');
      return;
    }
    setLoading('edit');
    Promise.resolve(onEdit && onEdit(approval?.id, editedPayload)).finally(() => setLoading(null));
  }

  // prevent backdrop dismiss — just shake slightly to signal intent
  function handleBackdropClick() {
    if (shaking) return;
    setShaking(true);
    setTimeout(() => setShaking(false), 400);
  }

  if (!approval) return null;

  const isHighStakes = approval.highStakes === true;

  return (
    <div
      style={{
        position: 'fixed', inset: 0,
        background: tok.backdrop,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        zIndex: 9999, padding: '1rem',
        animation: 'ham-backdrop-in 0.18s ease',
      }}
      onClick={handleBackdropClick}
    >
      {/* Modal panel */}
      <div
        style={{
          background: tok.bg,
          border: `1px solid ${tok.border}`,
          borderRadius: 16,
          boxShadow: tok.shadow,
          width: '100%', maxWidth: 520,
          maxHeight: '90vh', overflowY: 'auto',
          fontFamily: "'Space Grotesk', sans-serif",
          animation: `ham-modal-in 0.22s cubic-bezier(0.34,1.56,0.64,1)${shaking ? ', ham-shake 0.35s ease' : ''}`,
        }}
        onClick={e => e.stopPropagation()}
      >
        {/* High-stakes warning banner */}
        {isHighStakes && (
          <div style={{
            background: tok.warningBg, borderBottom: `1px solid ${tok.warningBorder}`,
            padding: '0.625rem 1.5rem',
            display: 'flex', alignItems: 'center', gap: 8,
            borderRadius: '16px 16px 0 0',
          }}>
            <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
              <path d="M7 1.5L13 12.5H1L7 1.5Z" stroke={tok.warning} strokeWidth="1.5" strokeLinejoin="round"/>
              <path d="M7 5.5V8" stroke={tok.warning} strokeWidth="1.5" strokeLinecap="round"/>
              <circle cx="7" cy="10.25" r="0.75" fill={tok.warning}/>
            </svg>
            <span style={{ fontSize: '0.75rem', fontWeight: 600, color: tok.warning }}>
              High-stakes action — review carefully before approving
            </span>
          </div>
        )}

        {/* Header */}
        <div style={{
          padding: '1.25rem 1.5rem 1rem',
          borderBottom: `1px solid ${tok.border}`,
          display: 'flex', alignItems: 'flex-start', gap: '0.875rem',
        }}>
          <AgentAvatar name={approval.agentName || 'Agent'} tok={tok} />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{
              fontSize: '0.7rem', fontWeight: 600, letterSpacing: '0.07em',
              textTransform: 'uppercase', color: tok.fgSubtle, marginBottom: 3,
            }}>
              Approval required
            </div>
            <h2 style={{
              fontSize: '1.0rem', fontWeight: 700, color: tok.fg,
              lineHeight: 1.3, margin: 0, marginBottom: approval.agentName ? 4 : 0,
            }}>
              {approval.title}
            </h2>
            {approval.agentName && (
              <div style={{ fontSize: '0.8rem', color: tok.fgMuted }}>
                Requested by <strong style={{ color: tok.fg }}>{approval.agentName}</strong>
              </div>
            )}
          </div>
          {/* keyboard hint */}
          <div style={{
            fontSize: '0.65rem', color: tok.fgSubtle,
            display: 'flex', flexDirection: 'column', gap: 3, alignItems: 'flex-end',
            flexShrink: 0, paddingTop: 2,
          }}>
            <span><kbd style={kbdStyle(tok)}>↵</kbd> approve</span>
            <span><kbd style={kbdStyle(tok)}>Esc</kbd> reject</span>
            <span><kbd style={kbdStyle(tok)}>⌘E</kbd> edit</span>
          </div>
        </div>

        {/* Body */}
        <div style={{ padding: '1.25rem 1.5rem' }}>
          {/* Description */}
          <p style={{
            fontSize: '0.9rem', lineHeight: 1.65,
            color: tok.fg, margin: 0, marginBottom: '1.125rem',
          }}>
            {approval.description}
          </p>

          {/* Preview block */}
          {approval.preview && (
            <div style={{ marginBottom: mode !== 'review' ? '1.125rem' : 0 }}>
              <div style={{
                fontSize: '0.7rem', fontWeight: 700, letterSpacing: '0.06em',
                textTransform: 'uppercase', color: tok.fgSubtle, marginBottom: 6,
              }}>
                {approval.previewMode === 'diff' ? 'Changes' :
                 approval.previewMode === 'json' ? 'Payload' : 'Details'}
              </div>
              {mode === 'edit' ? (
                <textarea
                  ref={editRef}
                  value={editedPayload}
                  onChange={e => setEditedPayload(e.target.value)}
                  style={{
                    width: '100%', minHeight: 160,
                    background: tok.inputBg, border: `1.5px solid ${tok.accent}`,
                    borderRadius: 8, padding: '0.75rem',
                    fontFamily: 'monospace', fontSize: '0.78rem', lineHeight: 1.6,
                    color: tok.fg, resize: 'vertical',
                    outline: 'none', boxSizing: 'border-box',
                  }}
                />
              ) : (
                <PreviewBlock mode={approval.previewMode} content={approval.preview} tok={tok} />
              )}
            </div>
          )}

          {/* Reason textarea (reject mode) */}
          {mode === 'reject' && (
            <div style={{ marginTop: '1.125rem' }}>
              <div style={{
                fontSize: '0.7rem', fontWeight: 700, letterSpacing: '0.06em',
                textTransform: 'uppercase', color: tok.fgSubtle, marginBottom: 6,
              }}>
                Reason (optional)
              </div>
              <textarea
                ref={reasonRef}
                value={reason}
                onChange={e => setReason(e.target.value)}
                placeholder="Why are you rejecting this action?"
                rows={3}
                style={{
                  width: '100%', background: tok.inputBg,
                  border: `1.5px solid ${tok.error}`,
                  borderRadius: 8, padding: '0.75rem',
                  fontFamily: "'Space Grotesk', sans-serif",
                  fontSize: '0.875rem', lineHeight: 1.55,
                  color: tok.fg, resize: 'vertical',
                  outline: 'none', boxSizing: 'border-box',
                }}
              />
            </div>
          )}
        </div>

        {/* Action footer */}
        <div style={{
          padding: '0.875rem 1.5rem 1.375rem',
          borderTop: `1px solid ${tok.border}`,
          display: 'flex', gap: '0.625rem', flexWrap: 'wrap',
          justifyContent: 'flex-end',
        }}>
          {/* Tertiary: Edit & Approve */}
          {mode !== 'reject' && (
            <button
              onClick={handleEdit}
              disabled={!!loading}
              style={{
                ...baseBtnStyle(),
                background: tok.btnTertiary,
                border: `1.5px solid ${tok.border}`,
                color: tok.fgMuted,
                marginRight: 'auto',
              }}
            >
              {loading === 'edit'
                ? <><Spinner color={tok.fgMuted} /> Saving…</>
                : mode === 'edit' ? '✓ Save & Approve' : '✎ Edit & Approve'
              }
            </button>
          )}

          {/* Secondary: Reject */}
          <button
            onClick={handleReject}
            disabled={!!loading}
            style={{
              ...baseBtnStyle(),
              background: tok.btnSecondary,
              border: `1.5px solid ${mode === 'reject' ? tok.error : tok.borderStrong}`,
              color: mode === 'reject' ? tok.error : tok.fgMuted,
            }}
          >
            {loading === 'reject'
              ? <><Spinner color={tok.error} /> Rejecting…</>
              : mode === 'reject' ? '✕ Confirm Reject' : 'Reject'
            }
          </button>

          {/* Cancel (from reject/edit mode) */}
          {(mode === 'reject' || mode === 'edit') && (
            <button
              onClick={() => setMode('review')}
              disabled={!!loading}
              style={{
                ...baseBtnStyle(),
                background: tok.btnSecondary,
                border: `1.5px solid ${tok.border}`,
                color: tok.fgMuted,
              }}
            >
              Cancel
            </button>
          )}

          {/* Primary: Approve */}
          {mode !== 'reject' && (
            <button
              onClick={handleApprove}
              disabled={!!loading}
              style={{
                ...baseBtnStyle(),
                background: tok.btnPrimary,
                border: `1.5px solid ${tok.btnPrimary}`,
                color: '#fff',
                fontWeight: 700,
              }}
            >
              {loading === 'approve'
                ? <><Spinner color="#fff" /> Approving…</>
                : '✓ Approve'
              }
            </button>
          )}
        </div>
      </div>
    </div>
  );
}

// ─── HumanApprovalContainer (AG-UI auto-wiring) ───────────────────────────────
/**
 * Listens for STATE_DELTA events where state path indicates a pending approval.
 * Emits USER_MESSAGE events with approval decision.
 *
 * Props:
 *   eventSource: EventTarget | EventEmitter
 *   approvalStatePath?: string   — state path prefix to watch (default: 'pendingApproval')
 *   theme?: 'light'|'dark'
 */
function HumanApprovalContainer({
  eventSource,
  approvalStatePath = 'pendingApproval',
  theme = 'light',
  onApprove,
  onReject,
  onEdit,
}) {
  const [approval, setApproval] = useState(null);

  useEffect(() => {
    if (!eventSource) return;

    function onStateDelta(e) {
      const d = e.detail || e;
      if (!d.path || !d.path.startsWith(approvalStatePath)) return;
      if (d.value && d.value.id) {
        setApproval(d.value);
      } else if (d.value === null) {
        setApproval(null);
      }
    }

    if (typeof eventSource.addEventListener === 'function') {
      eventSource.addEventListener('STATE_DELTA', onStateDelta);
      return () => eventSource.removeEventListener('STATE_DELTA', onStateDelta);
    }
    if (typeof eventSource.on === 'function') {
      eventSource.on('STATE_DELTA', onStateDelta);
      return () => eventSource.off('STATE_DELTA', onStateDelta);
    }
  }, [eventSource, approvalStatePath]);

  function emitDecision(decision, id, payload) {
    if (!eventSource) return;
    const event = new CustomEvent('USER_MESSAGE', {
      detail: { role: 'human', content: `[Approval: ${decision}]`, approvalId: id, decision, payload },
    });
    if (typeof eventSource.dispatchEvent === 'function') eventSource.dispatchEvent(event);
    else if (typeof eventSource.emit === 'function') eventSource.emit('USER_MESSAGE', event.detail);
  }

  function handleApprove(id) {
    emitDecision('approved', id);
    setApproval(null);
    if (onApprove) onApprove(id);
  }
  function handleReject(id, reason) {
    emitDecision('rejected', id, { reason });
    setApproval(null);
    if (onReject) onReject(id, reason);
  }
  function handleEdit(id, editedPayload) {
    emitDecision('approved_with_edit', id, { editedPayload });
    setApproval(null);
    if (onEdit) onEdit(id, editedPayload);
  }

  if (!approval) return null;

  return (
    <HumanApprovalModal
      approval={approval}
      onApprove={handleApprove}
      onReject={handleReject}
      onEdit={handleEdit}
      theme={theme}
    />
  );
}

// ─── Helpers ──────────────────────────────────────────────────────────────────
function baseBtnStyle() {
  return {
    display: 'inline-flex', alignItems: 'center', gap: 6,
    padding: '0.5rem 1.125rem', borderRadius: 9,
    fontSize: '0.875rem', fontWeight: 600,
    fontFamily: "'Space Grotesk', sans-serif",
    cursor: 'pointer', transition: 'opacity 0.15s, transform 0.1s',
    lineHeight: 1,
  };
}

function kbdStyle(tok) {
  return {
    fontFamily: 'monospace', fontSize: '0.65rem',
    background: tok.avatarBg, border: `1px solid ${tok.border}`,
    borderRadius: 4, padding: '1px 4px', color: tok.fgMuted,
    display: 'inline-block',
  };
}

// ─── Exports ──────────────────────────────────────────────────────────────────
if (typeof module !== 'undefined') {
  module.exports = { HumanApprovalModal, HumanApprovalContainer };
}
