/* EmployeeWizard.jsx — shared wizard primitives + step bodies for the
 * "Add employee" and "Edit employee" flows.
 *
 * Both modals now compose:
 *   1. Basics      (name, title)              [Add only — Edit shows it on its own step]
 *   2. Identity    (slack tokens, avatar id)  [Both]
 *   3. soul.md     (markdown)                 [Both]
 *   4. Connectors  (preset chips + per-conn JSON params, type or upload)
 *   5. Knowledge   (file uploads)
 *   6. Skills      [Add only — drafts + attach existing]
 *   7. Review      (summary)
 *
 * Each step is a pure component; the parent (Add/Edit) owns all state and
 * passes it down. The chrome (`<WizardChrome>`) renders a left-rail step
 * indicator + the active step's body + a footer with Back/Next/Submit.
 */

const { useState: uW1, useEffect: uW2, useMemo: uW3, useRef: uW4 } = React;

// ── Connector catalog with parameter schemas ───────────────────────────
// Each connector declares the JSON shape its config takes. The wizard
// pre-fills the editor with this template so users can either fill in
// the gaps or upload their own .json. Schemas are hints — the editor
// validates parsability, not field presence.
window.CONNECTOR_CATALOG = window.CONNECTOR_CATALOG || [
  { id: 'gdrive',  label: 'Google Drive', schema: {
      service_account_email: '',
      private_key: '',
      shared_folder_ids: [],
    } },
  { id: 'gmail',   label: 'Gmail', schema: {
      oauth_client_id: '',
      oauth_client_secret: '',
      refresh_token: '',
      delegated_user: '',
    } },
  { id: 'gcal',    label: 'Google Calendar', schema: {
      oauth_client_id: '',
      oauth_client_secret: '',
      refresh_token: '',
      calendar_ids: ['primary'],
    } },
  { id: 'slack',   label: 'Slack', schema: {
      bot_token: 'xoxb-…',
      signing_secret: '',
      channels: [],
    } },
  { id: 'notion',  label: 'Notion', schema: {
      integration_token: 'secret_…',
      root_page_ids: [],
    } },
  { id: 'linear',  label: 'Linear', schema: {
      api_key: 'lin_api_…',
      team_keys: [],
    } },
  { id: 'github',  label: 'GitHub', schema: {
      app_id: '',
      private_key_pem: '',
      installation_id: '',
      repos: [],
    } },
  { id: 'zoom',    label: 'Zoom', schema: {
      account_id: '',
      client_id: '',
      client_secret: '',
    } },
];

window.connectorById = (id) =>
  (window.CONNECTOR_CATALOG || []).find(c => c.id === id);

// Pretty-print a JS value as JSON (always 2-space indent).
window.jsonPretty = (v) => {
  try { return JSON.stringify(v, null, 2); } catch { return ''; }
};

// ── Local style helpers (fall back to amInput etc. from the outer file) ─
// These references resolve at call-time because both files load before
// the modals render.
function W_styles() {
  return {
    railWrap: {
      width: 200, flex: 'none', padding: '20px 0 20px 22px',
      borderRight: '1px solid var(--border-subtle)',
      display: 'flex', flexDirection: 'column', gap: 4,
      background: 'var(--c-cream-100)',
    },
    railItem: (active, done) => ({
      display: 'flex', alignItems: 'center', gap: 10,
      padding: '8px 10px', borderRadius: 8, cursor: done ? 'pointer' : 'default',
      background: active ? 'var(--c-cream-50)' : 'transparent',
      border: active ? '1px solid var(--border-subtle)' : '1px solid transparent',
      boxShadow: active ? 'var(--shadow-raised-sm)' : 'none',
      color: active ? 'var(--c-ink-900)' : (done ? 'var(--c-ink-700)' : 'var(--c-ink-500)'),
      fontFamily: "'Suisse Int\\'l', sans-serif", fontSize: 13, fontWeight: 500,
    }),
    railNum: (active, done) => ({
      width: 22, height: 22, borderRadius: 999, flex: 'none',
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      fontFamily: "'JetBrains Mono', monospace", fontSize: 11, fontWeight: 600,
      background: active ? 'var(--c-petrol-800)' : (done ? 'var(--c-cream-50)' : 'transparent'),
      color: active ? '#fff' : (done ? 'var(--c-petrol-800)' : 'var(--c-ink-500)'),
      border: done && !active ? '1.5px solid var(--c-petrol-800)' : (active ? 'none' : '1.5px solid var(--border-subtle)'),
    }),
  };
}

// ── WizardChrome — the shell every step renders inside ─────────────────
// Props:
//   title, eyebrow            — header text
//   steps                     — [{ id, label, optional? }]
//   stepIndex, onStep         — controlled current-step index
//   maxReached                — highest index user has visited (for clickable rail)
//   children                  — body of the active step
//   onBack, onNext, onClose, onSubmit
//   nextDisabled, submitDisabled
//   submitLabel, busy, err
function WizardChrome(props) {
  const {
    title, eyebrow, steps, stepIndex, onStep, maxReached,
    children, onBack, onNext, onClose, onSubmit,
    nextDisabled, submitDisabled, submitLabel, busy, err, secondaryNote,
    overlay,
  } = props;
  const S = W_styles();
  const isLast = stepIndex === steps.length - 1;

  return (
    <div onClick={onClose} style={amOverlay}>
      <div onClick={e => e.stopPropagation()} style={{ ...amCard, width: 820, maxWidth: '95vw', position: 'relative' }}>
        <header style={amHeader}>
          <div style={{ flex: 1, minWidth: 0 }}>
            {eyebrow && <div className="eyebrow" style={{ color: 'var(--c-ink-500)', letterSpacing: '1.5px' }}>{eyebrow}</div>}
            <div style={{
              fontFamily: "'Suisse Int\\'l', sans-serif", fontWeight: 700, fontSize: 20,
              letterSpacing: '-0.02em', color: 'var(--c-ink-800)', marginTop: 4,
            }}>{title}</div>
          </div>
          <button onClick={onClose} title="Close" style={amCloseBtn}>
            <img src="../assets/icons/cross.svg" width={11} height={11} alt="Close" />
          </button>
        </header>

        <div style={{ display: 'flex', flex: 1, minHeight: 0 }}>
          {/* Step rail */}
          <nav style={S.railWrap}>
            {steps.map((s, i) => {
              const active = i === stepIndex;
              const done   = i <= maxReached;
              return (
                <div key={s.id}
                  onClick={() => done && onStep(i)}
                  style={S.railItem(active, done)}>
                  <span style={S.railNum(active, done)}>
                    {i < stepIndex || (done && !active) ? '✓' : i + 1}
                  </span>
                  <span style={{ flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                    {s.label}
                  </span>
                  {s.optional && (
                    <span style={{ fontSize: 10, color: 'var(--c-ink-500)', fontWeight: 400 }}>opt.</span>
                  )}
                </div>
              );
            })}
          </nav>

          {/* Body */}
          <div style={{ ...amBody, gap: 14, flex: 1, minWidth: 0 }}>
            {children}
            {err && <span style={amError}>{err}</span>}
          </div>
        </div>

        <footer style={amFooter}>
          {secondaryNote
            ? <span style={{ fontSize: 11, color: 'var(--c-ink-500)' }}>{secondaryNote}</span>
            : <span style={{ fontSize: 11, color: 'var(--c-ink-500)' }}>Step {stepIndex + 1} of {steps.length}</span>}
          <div style={{ flex: 1 }} />
          <button onClick={onClose} style={amGhost}>Cancel</button>
          <button onClick={onBack} disabled={stepIndex === 0 || busy} style={{
            ...amGhost,
            opacity: stepIndex === 0 ? 0.4 : 1,
            cursor: stepIndex === 0 ? 'not-allowed' : 'pointer',
          }}>Back</button>
          {isLast
            ? <button onClick={onSubmit} disabled={submitDisabled || busy} style={amPrimary(submitDisabled || busy)}>
                {busy ? 'Saving…' : (submitLabel || 'Submit')}
              </button>
            : <button onClick={onNext} disabled={nextDisabled || busy} style={amPrimary(nextDisabled || busy)}>
                Next
              </button>}
        </footer>
        {overlay}
      </div>
    </div>
  );
}

// ── BasicsStep — name + title ──────────────────────────────────────────
function BasicsStep({ name, role, setName, setRole }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <SectionHeader
        title="Basics"
        sub="Identifies the employee in admin views and shows up on every skill they own." />
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
        <Field label="Full name" required>
          <input type="text" autoFocus value={name} onChange={e => setName(e.target.value)}
            placeholder="e.g. Riya Banerjee" style={amInput} />
        </Field>
        <Field label="Title" hint="optional">
          <input type="text" value={role} onChange={e => setRole(e.target.value)}
            placeholder="e.g. L&D Coach" style={amInput} />
        </Field>
      </div>
    </div>
  );
}

// ── IdentityStep — slack + avatar identifiers ──────────────────────────
// These are credentials the runtime uses to act *as* this employee in
// Slack and to fetch their avatar. Stored on the employee doc.
function IdentityStep({ identity, setIdentity }) {
  const set = (k) => (e) => setIdentity({ ...identity, [k]: e.target.value });
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <SectionHeader
        title="Identity & credentials"
        sub="The runtime uses these to act as this employee. Slack tokens are scoped to a single workspace; treat them like passwords." />

      <Field label="Slack user token" hint="xoxc-… (kept server-side, encrypted at rest)">
        <input type="password" value={identity.slack_user_token || ''} onChange={set('slack_user_token')}
          placeholder="xoxc-1234567890-…" autoComplete="off"
          style={{ ...amInput, fontFamily: "'JetBrains Mono', monospace", fontSize: 12 }} />
      </Field>

      <Field label="Slack d cookie" hint="from browser session — required alongside the user token">
        <input type="password" value={identity.slack_d_cookie || ''} onChange={set('slack_d_cookie')}
          placeholder="xoxd-…" autoComplete="off"
          style={{ ...amInput, fontFamily: "'JetBrains Mono', monospace", fontSize: 12 }} />
      </Field>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
        <Field label="Slack user ID">
          <input type="text" value={identity.slack_user_id || ''} onChange={set('slack_user_id')}
            placeholder="U01ABCDE234"
            style={{ ...amInput, fontFamily: "'JetBrains Mono', monospace", fontSize: 12 }} />
        </Field>
        <Field label="Avatar user ID" hint="user whose avatar appears in chat">
          <input type="text" value={identity.avatar_user_id || ''} onChange={set('avatar_user_id')}
            placeholder="U02FGHIJ567"
            style={{ ...amInput, fontFamily: "'JetBrains Mono', monospace", fontSize: 12 }} />
        </Field>
      </div>
    </div>
  );
}

// ── SoulStep — markdown describing how the employee thinks ────────────
// Also captures response-style and voice-instruction strings, since both
// shape every reply this employee produces and read naturally next to soul.md.
window.DEFAULT_RESPONSE_STYLE = window.DEFAULT_RESPONSE_STYLE
  || 'Respond in short sentences. Do not give intermediate steps when you do a task; at the end just give a success message and a 2-sentence summary of the output.';
window.DEFAULT_VOICE_INSTRUCTIONS = window.DEFAULT_VOICE_INSTRUCTIONS
  || 'Keep your output TTS-friendly; do not use acronyms or symbols as they confuse the TTS. Keep all responses 1-2 sentences as you are continuing an existing conversation.';

function SoulStep({
  soulMd, setSoulMd,
  responseStyle, setResponseStyle,
  voiceInstructions, setVoiceInstructions,
  disabled,
}) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <SectionHeader
        title="soul.md"
        sub="Markdown describing how this employee thinks and works. Prepended to every skill prompt as system context." />
      <textarea value={soulMd} onChange={e => setSoulMd(e.target.value)}
        disabled={disabled}
        placeholder={disabled
          ? 'Loading soul.md…'
          : `# Coaching style\n\nDirect, calm, and curious. Surfaces assumptions before answers.\n\n# Voice\n\nSecond person. No jargon. Cite the rubric when scoring.`}
        style={{ ...amTextarea, minHeight: 220, opacity: disabled ? 0.6 : 1 }} />

      <SectionHeader
        title="Response style"
        sub="Shapes every reply this employee produces — tone, length, whether to show intermediate steps, etc." />
      <textarea value={responseStyle} onChange={e => setResponseStyle(e.target.value)}
        disabled={disabled}
        placeholder={disabled ? 'Loading response style…' : window.DEFAULT_RESPONSE_STYLE}
        style={{ ...amTextarea, minHeight: 110, opacity: disabled ? 0.6 : 1 }} />

      <SectionHeader
        title="Voice instructions"
        sub="Controls TTS behaviour for spoken responses. Keep it phonetic-friendly — no acronyms or symbols." />
      <textarea value={voiceInstructions} onChange={e => setVoiceInstructions(e.target.value)}
        disabled={disabled}
        placeholder={disabled ? 'Loading voice instructions…' : window.DEFAULT_VOICE_INSTRUCTIONS}
        style={{ ...amTextarea, minHeight: 110, opacity: disabled ? 0.6 : 1 }} />
    </div>
  );
}

// ── ConnectorsStep — choose connectors + edit each one's JSON params ──
// `connectors` is an array of { id, params } objects, kept ordered by
// the order in which the user enabled them.
function ConnectorsStep({ connectors, setConnectors }) {
  const catalog = window.CONNECTOR_CATALOG || [];
  const [activeId, setActiveId] = uW1(connectors[0]?.id || null);

  const has = (id) => connectors.some(c => c.id === id);
  const toggle = (id) => {
    if (has(id)) {
      const next = connectors.filter(c => c.id !== id);
      setConnectors(next);
      if (activeId === id) setActiveId(next[0]?.id || null);
    } else {
      const cat = window.connectorById(id);
      const initial = window.jsonPretty(cat?.schema || {});
      const next = [...connectors, { id, params: initial }];
      setConnectors(next);
      setActiveId(id);
    }
  };

  const updateParams = (id, params) =>
    setConnectors(connectors.map(c => c.id === id ? { ...c, params } : c));

  const active = connectors.find(c => c.id === activeId);
  const activeCat = active && window.connectorById(active.id);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <SectionHeader
        title="Connectors"
        sub="Tools this employee can read from when running their skills. Each connector takes a JSON config — type it in or upload a .json file." />

      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
        {catalog.map(c => {
          const on = has(c.id);
          return (
            <button key={c.id} type="button" onClick={() => toggle(c.id)} style={{
              height: 32, padding: '0 12px', borderRadius: 999,
              background: on ? 'var(--c-petrol-800)' : 'var(--c-cream-50)',
              color: on ? '#fff' : 'var(--c-ink-700)',
              border: on ? '1.5px solid var(--c-petrol-800)' : '1.5px solid var(--border-subtle)',
              fontFamily: "'Suisse Int\\'l', sans-serif", fontWeight: 500, fontSize: 12,
              cursor: 'pointer', boxShadow: on ? 'var(--shadow-raised-sm)' : 'none',
              display: 'inline-flex', alignItems: 'center', gap: 6,
            }}>
              {on && <span style={{ fontSize: 11 }}>✓</span>}
              {c.label}
            </button>
          );
        })}
      </div>

      {connectors.length === 0
        ? <EmptyHint text="Pick a connector above to configure its parameters." />
        : (
          <div style={{
            display: 'grid', gridTemplateColumns: '180px 1fr', gap: 0,
            border: '1px solid var(--border-subtle)', borderRadius: 12, overflow: 'hidden',
            background: 'var(--c-cream-50)', minHeight: 280,
          }}>
            {/* Sub-tabs for each enabled connector */}
            <div style={{
              display: 'flex', flexDirection: 'column', gap: 2,
              padding: 6, borderRight: '1px solid var(--border-subtle)',
              background: 'var(--c-cream-100)',
            }}>
              {connectors.map(c => {
                const cat = window.connectorById(c.id);
                const on = c.id === activeId;
                const valid = isValidJson(c.params);
                return (
                  <button key={c.id} type="button" onClick={() => setActiveId(c.id)} style={{
                    display: 'flex', alignItems: 'center', gap: 8, padding: '8px 10px',
                    borderRadius: 8, border: '1px solid transparent',
                    background: on ? 'var(--c-cream-50)' : 'transparent',
                    boxShadow: on ? 'var(--shadow-raised-sm)' : 'none',
                    color: on ? 'var(--c-ink-900)' : 'var(--c-ink-700)',
                    fontFamily: "'Suisse Int\\'l', sans-serif", fontSize: 12, fontWeight: 500,
                    cursor: 'pointer', textAlign: 'left',
                  }}>
                    <span style={{
                      width: 8, height: 8, borderRadius: 999, flex: 'none',
                      background: valid ? 'var(--c-petrol-800)' : 'var(--c-terra-600, #C65A42)',
                    }} />
                    <span style={{ flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {cat?.label || c.id}
                    </span>
                  </button>
                );
              })}
            </div>

            {/* Editor for the active connector */}
            <div style={{ padding: 12, display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
              {active ? (
                <ConnectorParamsEditor
                  connectorId={active.id}
                  label={activeCat?.label || active.id}
                  value={active.params}
                  onChange={(v) => updateParams(active.id, v)}
                  schema={activeCat?.schema} />
              ) : (
                <EmptyHint text="Pick a connector from the left to edit its params." />
              )}
            </div>
          </div>
        )}
    </div>
  );
}

// ── ConnectorParamsEditor — JSON textarea + .json upload + reset ──────
function ConnectorParamsEditor({ connectorId, label, value, onChange, schema }) {
  const [parseErr, setParseErr] = uW1(null);
  const [dragging, setDragging] = uW1(false);
  const fileRef = uW4(null);

  uW2(() => {
    if (!value || !value.trim()) { setParseErr(null); return; }
    try { JSON.parse(value); setParseErr(null); }
    catch (e) { setParseErr(String(e.message || e)); }
  }, [value]);

  const onUpload = async (file) => {
    if (!file) return;
    if (!/\.json$/i.test(file.name) && file.type !== 'application/json') {
      setParseErr(`Expected a .json file, got "${file.name}".`);
      return;
    }
    const text = await file.text();
    try {
      const obj = JSON.parse(text);
      onChange(window.jsonPretty(obj));
    } catch (e) {
      setParseErr(`Could not parse ${file.name}: ${e.message || e}`);
      onChange(text);
    }
  };

  const onDrop = async (e) => {
    e.preventDefault(); setDragging(false);
    const f = e.dataTransfer?.files?.[0];
    if (f) onUpload(f);
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, flexWrap: 'wrap', minWidth: 0 }}>
          <span style={{ ...amLabel, fontSize: 13 }}>{label} parameters</span>
          <span style={{
            fontFamily: "'JetBrains Mono', monospace", fontSize: 10, color: 'var(--c-ink-500)',
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
          }}>
            {connectorId}.json
          </span>
        </div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <button type="button" onClick={() => onChange(window.jsonPretty(schema || {}))}
            style={{
              height: 28, padding: '0 10px', borderRadius: 8,
              background: 'transparent', color: 'var(--c-ink-700)',
              border: '1px solid var(--border-subtle)', cursor: 'pointer',
              fontFamily: "'Suisse Int\\'l', sans-serif", fontSize: 11,
              whiteSpace: 'nowrap',
            }}>Reset to template</button>
          <button type="button" onClick={() => fileRef.current?.click()}
            style={{
              height: 28, padding: '0 10px', borderRadius: 8,
              background: 'var(--c-cream-50)', color: 'var(--c-petrol-800)',
              border: '1.5px solid var(--c-petrol-800)', cursor: 'pointer',
              fontFamily: "'Suisse Int\\'l', sans-serif", fontSize: 11, fontWeight: 500,
              display: 'inline-flex', alignItems: 'center', gap: 6,
              whiteSpace: 'nowrap',
            }}>
            Upload .json
          </button>
          <input type="file" accept=".json,application/json" ref={fileRef}
            onChange={(e) => onUpload(e.target.files?.[0])} style={{ display: 'none' }} />
        </div>
      </div>

      <div
        onDragOver={(e) => { e.preventDefault(); setDragging(true); }}
        onDragLeave={() => setDragging(false)}
        onDrop={onDrop}
        style={{
          border: dragging ? '1.5px dashed var(--c-petrol-800)' : '1.5px solid var(--border-subtle)',
          borderRadius: 12, background: dragging ? 'var(--c-cream-100)' : 'var(--c-cream-50)',
          boxShadow: 'var(--shadow-inset-input)',
          position: 'relative',
        }}>
        <textarea value={value} onChange={e => onChange(e.target.value)}
          placeholder={window.jsonPretty(schema || {})}
          spellCheck={false}
          style={{
            width: '100%', minHeight: 220, padding: '12px 14px',
            background: 'transparent', border: 'none', outline: 'none',
            fontFamily: "'JetBrains Mono', monospace", fontSize: 12, lineHeight: 1.5,
            color: 'var(--c-ink-900)', resize: 'vertical', boxSizing: 'border-box',
          }} />
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 11 }}>
        {parseErr
          ? <span style={{ color: 'var(--c-terra-600, #C65A42)' }}>⚠ {parseErr}</span>
          : <span style={{ color: 'var(--c-ink-500)' }}>Valid JSON · drop a .json file anywhere on this box to replace.</span>}
      </div>
    </div>
  );
}

function isValidJson(s) {
  if (!s || !s.trim()) return true; // empty is allowed
  try { JSON.parse(s); return true; } catch { return false; }
}

// ── Tiny shared bits ───────────────────────────────────────────────────
function SectionHeader({ title, sub }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
      <div style={{
        fontFamily: "'Suisse Int\\'l', sans-serif", fontWeight: 600, fontSize: 15,
        color: 'var(--c-ink-900)',
      }}>{title}</div>
      {sub && <div style={{ fontSize: 12, color: 'var(--c-ink-500)', maxWidth: 560 }}>{sub}</div>}
    </div>
  );
}

function Field({ label, hint, required, children }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
      <label style={amLabel}>
        {label}
        {required && <span style={{ color: 'var(--c-terra-600, #C65A42)' }}> *</span>}
        {hint && <span style={{ color: 'var(--c-ink-500)', fontWeight: 400 }}> ({hint})</span>}
      </label>
      {children}
    </div>
  );
}

function EmptyHint({ text }) {
  return (
    <div style={{
      padding: '20px 16px', textAlign: 'center', fontSize: 12, color: 'var(--c-ink-500)',
      border: '1px dashed var(--border-subtle)', borderRadius: 12,
      background: 'var(--c-cream-50)',
    }}>{text}</div>
  );
}

// ── SkillEnvStep — n×2 table of env vars injected into every skill run ─
// State shape is a list of `{ key, value }` rows (preserves order + lets the
// user have an in-progress empty row). Convert to/from a flat object at the
// modal boundary.
window.skillEnvObjectToRows = (obj) => {
  if (!obj || typeof obj !== 'object') return [];
  return Object.keys(obj).map(k => ({ key: k, value: typeof obj[k] === 'string' ? obj[k] : String(obj[k] ?? '') }));
};
window.skillEnvRowsToObject = (rows) => {
  const out = {};
  for (const r of (rows || [])) {
    const k = (r.key || '').trim();
    if (!k) continue;
    out[k] = typeof r.value === 'string' ? r.value : '';
  }
  return out;
};

function SkillEnvStep({ rows, setRows, disabled }) {
  const [reveal, setReveal] = uW1({}); // index -> bool

  const setRow = (i, patch) =>
    setRows(rows.map((r, idx) => idx === i ? { ...r, ...patch } : r));
  const removeRow = (i) =>
    setRows(rows.filter((_, idx) => idx !== i));
  const addRow = () =>
    setRows([...rows, { key: '', value: '' }]);
  const toggleReveal = (i) =>
    setReveal(prev => ({ ...prev, [i]: !prev[i] }));

  // Duplicate-key detection — last write wins server-side, so flag it visibly.
  const seen = {};
  const dupKey = {};
  rows.forEach((r, i) => {
    const k = (r.key || '').trim();
    if (!k) return;
    if (seen[k] !== undefined) { dupKey[i] = true; dupKey[seen[k]] = true; }
    else seen[k] = i;
  });

  const cellInput = {
    height: 36, padding: '0 10px', borderRadius: 8,
    background: 'var(--c-cream-50)',
    border: '1.5px solid var(--border-subtle)',
    boxShadow: 'var(--shadow-inset-input)',
    fontFamily: "'JetBrains Mono', monospace", fontSize: 12,
    color: 'var(--c-ink-900)',
    width: '100%', outline: 'none', boxSizing: 'border-box',
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <SectionHeader
        title="Skill environment"
        sub="Key/value pairs injected as environment variables when this employee runs a skill. Use for API keys and connection strings the skills read via os.environ." />

      {rows.length === 0 ? (
        <EmptyHint text={disabled ? 'Loading skill env…' : 'No env vars yet. Click “Add variable” to start.'} />
      ) : (
        <div style={{
          display: 'grid', gridTemplateColumns: 'minmax(180px, 1fr) minmax(220px, 2fr) auto',
          gap: 8, alignItems: 'center',
        }}>
          <div style={{ ...amLabel, fontSize: 12, color: 'var(--c-ink-500)' }}>Key</div>
          <div style={{ ...amLabel, fontSize: 12, color: 'var(--c-ink-500)' }}>Value</div>
          <div />

          {rows.map((r, i) => {
            const dup = !!dupKey[i];
            const shown = !!reveal[i];
            return (
              <React.Fragment key={i}>
                <input
                  type="text"
                  value={r.key}
                  disabled={disabled}
                  onChange={e => setRow(i, { key: e.target.value })}
                  placeholder="API_KEY_NAME"
                  style={{
                    ...cellInput,
                    border: dup
                      ? '1.5px solid var(--c-terra-600, #C65A42)'
                      : cellInput.border,
                    opacity: disabled ? 0.6 : 1,
                  }} />
                <div style={{ display: 'flex', gap: 6, minWidth: 0 }}>
                  <input
                    type={shown ? 'text' : 'password'}
                    value={r.value}
                    disabled={disabled}
                    onChange={e => setRow(i, { value: e.target.value })}
                    placeholder="value"
                    autoComplete="off"
                    style={{ ...cellInput, opacity: disabled ? 0.6 : 1 }} />
                  <button type="button" onClick={() => toggleReveal(i)} disabled={disabled}
                    title={shown ? 'Hide value' : 'Show value'}
                    style={{
                      height: 36, padding: '0 10px', borderRadius: 8,
                      background: 'transparent', color: 'var(--c-ink-700)',
                      border: '1px solid var(--border-subtle)',
                      cursor: disabled ? 'not-allowed' : 'pointer',
                      fontFamily: "'Suisse Int\\'l', sans-serif", fontSize: 11,
                      whiteSpace: 'nowrap',
                    }}>{shown ? 'Hide' : 'Show'}</button>
                </div>
                <button type="button" onClick={() => removeRow(i)} disabled={disabled}
                  title="Remove"
                  style={{
                    height: 36, width: 36, borderRadius: 8,
                    background: 'transparent', color: 'var(--c-terra-600, #C65A42)',
                    border: '1px solid var(--border-subtle)',
                    cursor: disabled ? 'not-allowed' : 'pointer',
                    fontFamily: "'Suisse Int\\'l', sans-serif", fontSize: 14, fontWeight: 600,
                  }}>×</button>
              </React.Fragment>
            );
          })}
        </div>
      )}

      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <button type="button" onClick={addRow} disabled={disabled}
          style={{
            height: 32, padding: '0 12px', borderRadius: 8,
            background: 'var(--c-cream-50)', color: 'var(--c-petrol-800)',
            border: '1.5px solid var(--c-petrol-800)',
            cursor: disabled ? 'not-allowed' : 'pointer',
            fontFamily: "'Suisse Int\\'l', sans-serif", fontWeight: 500, fontSize: 12,
            opacity: disabled ? 0.6 : 1,
          }}>+ Add variable</button>
        {Object.keys(dupKey).length > 0 && (
          <span style={{ fontSize: 11, color: 'var(--c-terra-600, #C65A42)' }}>
            Duplicate keys highlighted — only the last value will be saved.
          </span>
        )}
      </div>
    </div>
  );
}

// Expose to the global scope so AddModals.jsx can compose them.
Object.assign(window, {
  WizardChrome, BasicsStep, IdentityStep, SoulStep, ConnectorsStep, SkillEnvStep,
  ConnectorParamsEditor, SectionHeader, Field, EmptyHint, isValidJson,
});
