// src/features/admin/ui/admin-leads-bot-settings.jsx
//
// Two admin panels that sit at the top of the Teacher Leads tab:
//   1. Bot Instructions — edit the bot's tone/goal (persona block). Saves to
//      outreach_settings.bot_instructions. The fixed JSON reply format + the
//      stop-rules are NOT here and can't be broken from this UI.
//   2. Test Sandbox — type AS THE TEACHER and watch the bot reply. Purely
//      ephemeral: nothing is written to the database, no SMS is sent. The test
//      uses the CURRENT textarea value, so unsaved edits are testable.
//
// Data: window.ADMIN_DATA.teacherLeads (data-teacher-leads.js).
// Styling: reuses TF_TONE from admin-teacher-finder.jsx.
// Load order: AFTER admin-teacher-finder.jsx (for TF_TONE), BEFORE admin-leads.jsx
// (which mounts <window.AdminLeadsBotSettings /> at the top of the tab).

const MAX_INSTRUCTIONS_LEN = 4000;

// State -> short label for the sandbox badge. Mirrors lead-bot's next_state set.
const TEST_STATE_LABEL = {
  greeted: 'Greeted',
  awaiting_rate: 'Awaiting rate',
  awaiting_availability: 'Awaiting availability',
  awaiting_location: 'Awaiting location',
  ready_for_review: 'Ready for review',
  opted_out: 'Opted out',
  dead: 'Dead',
  human_requested: 'Human requested',
  outreach_sent: 'Outreach sent',
  paused: 'Paused',
};

// ── Panel 1: Bot Instructions ─────────────────────────────────────────────
const BotInstructionsPanel = ({ value, onChange, savedValue, onSaved }) => {
  const [busy, setBusy] = React.useState(false);
  const [status, setStatus] = React.useState(''); // '' | 'saved' | 'error'
  const [errMsg, setErrMsg] = React.useState('');

  const dirty = value !== savedValue;
  const tooLong = value.length > MAX_INSTRUCTIONS_LEN;
  const empty = value.trim().length === 0;

  const save = async () => {
    if (!dirty || empty || tooLong) return;
    setBusy(true); setStatus(''); setErrMsg('');
    try {
      const saved = await window.ADMIN_DATA.teacherLeads.saveBotInstructions(value.trim());
      onSaved(saved);
      setStatus('saved');
    } catch (e) {
      setStatus('error');
      setErrMsg(e.message || 'Save failed');
    } finally {
      setBusy(false);
    }
  };

  return (
    <div style={{
      background: TF_TONE.card, border: `1px solid ${TF_TONE.border}`,
      borderRadius: 14, padding: '16px 18px', marginBottom: 16,
    }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
        <h3 style={{ margin: 0, fontSize: 15, fontWeight: 700, color: TF_TONE.text }}>Bot instructions</h3>
        <span style={{ fontSize: 11, color: tooLong ? TF_TONE.danger : TF_TONE.muted }}>
          {value.length} / {MAX_INSTRUCTIONS_LEN}
        </span>
      </div>
      <div style={{ fontSize: 12, color: TF_TONE.muted, marginTop: 4, marginBottom: 10, lineHeight: 1.45 }}>
        This controls the bot's tone and goal. The reply format and stop-rules are fixed and can't be broken from here.
      </div>
      <textarea
        value={value}
        onChange={(e) => { onChange(e.target.value); if (status) setStatus(''); }}
        rows={12}
        spellCheck={true}
        style={{
          width: '100%', boxSizing: 'border-box', resize: 'vertical',
          fontFamily: "'SF Mono', ui-monospace, Menlo, monospace", fontSize: 12.5, lineHeight: 1.5,
          color: TF_TONE.text, background: TF_TONE.page,
          border: `1px solid ${tooLong ? TF_TONE.danger : TF_TONE.border}`,
          borderRadius: 10, padding: '11px 12px',
        }}
      />
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginTop: 10, flexWrap: 'wrap' }}>
        <button
          onClick={save}
          disabled={busy || !dirty || empty || tooLong}
          style={{
            background: (!dirty || empty || tooLong) ? TF_TONE.border : TF_TONE.text,
            color: (!dirty || empty || tooLong) ? TF_TONE.muted : '#fff',
            border: 'none', borderRadius: 999, padding: '9px 20px',
            fontSize: 12.5, fontWeight: 700, fontFamily: "'Plus Jakarta Sans', sans-serif",
            cursor: (busy || !dirty || empty || tooLong) ? 'default' : 'pointer',
            letterSpacing: '0.02em',
          }}
        >{busy ? 'Saving…' : 'Save instructions'}</button>
        {status === 'saved' && !dirty && <span style={{ fontSize: 12, color: TF_TONE.success, fontWeight: 600 }}>Saved.</span>}
        {status === 'error' && <span style={{ fontSize: 12, color: TF_TONE.danger, fontWeight: 600 }}>Error: {errMsg}</span>}
        {empty && <span style={{ fontSize: 12, color: TF_TONE.danger }}>Instructions can't be empty.</span>}
        {tooLong && <span style={{ fontSize: 12, color: TF_TONE.danger }}>Too long — trim to {MAX_INSTRUCTIONS_LEN} characters.</span>}
        {dirty && status !== 'error' && !empty && !tooLong && (
          <span style={{ fontSize: 12, color: TF_TONE.muted }}>Unsaved changes.</span>
        )}
      </div>
    </div>
  );
};

// ── Panel 2: Test Sandbox ─────────────────────────────────────────────────
// `getInstructions` returns the CURRENT textarea value so unsaved edits apply.
const TestSandboxPanel = ({ getInstructions }) => {
  const [transcript, setTranscript] = React.useState([]); // [{ direction, body }]
  const [input, setInput] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [waiting, setWaiting] = React.useState(false);     // waiting on Telegram approval
  const [err, setErr] = React.useState('');
  const [lastExtract, setLastExtract] = React.useState(null); // { newState, extracted }
  const scrollRef = React.useRef(null);

  React.useEffect(() => {
    if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  }, [transcript, busy, waiting]);

  const reset = () => {
    setTranscript([]); setInput(''); setErr(''); setLastExtract(null);
  };

  // Poll the test pending row until Joe decides on Telegram (or we time out).
  // Returns { status, finalText }. Polls every 2.5s for up to ~5 min.
  const pollApproval = async (pendingId) => {
    const sleep = (ms) => new Promise(r => setTimeout(r, ms));
    for (let i = 0; i < 120; i++) {
      await sleep(2500);
      let p;
      try { p = await window.ADMIN_DATA.teacherLeads.pollBotTest(pendingId); }
      catch (_) { continue; } // transient; keep polling
      if (p.status === 'sent') return { status: 'sent', finalText: p.finalText, decidedVia: p.decidedVia };
      if (p.status === 'rejected') return { status: 'rejected' };
      if (p.status === 'gone') return { status: 'gone' };
      // 'pending' / 'approved' (claimed, not yet finalized) -> keep waiting
    }
    return { status: 'timeout' };
  };

  const send = async () => {
    const text = input.trim();
    if (!text || busy || waiting) return;
    setErr('');
    // Append the teacher's message locally; this transcript is what we send.
    const priorTranscript = transcript.slice();
    const nextTranscript = [...priorTranscript, { direction: 'inbound', body: text }];
    setTranscript(nextTranscript);
    setInput('');
    setBusy(true);
    try {
      const res = await window.ADMIN_DATA.teacherLeads.testBotTurn({
        instructions: getInstructions(),
        transcript: priorTranscript, // history before this message
        latestInbound: text,
      });
      setLastExtract({ newState: res.newState, extracted: res.extracted });

      // APPROVAL MODE: the draft went to Telegram. Show a waiting marker and
      // poll for Joe's Approve / Edit / Reject before adding the bot bubble.
      if (res.pendingApproval) {
        setBusy(false);
        setWaiting(true);
        setTranscript(t => [...t, { direction: 'system', body: '📲 Draft sent to your Telegram — open it and tap Approve, Edit, or Reject.' }]);
        const decision = await pollApproval(res.pendingId);
        if (decision.status === 'sent') {
          const tag = decision.decidedVia === 'edited' ? ' (your edit)' : '';
          setTranscript(t => [...t, { direction: 'outbound', body: decision.finalText || '' }, { direction: 'system', body: `✓ Approved on Telegram${tag}.` }]);
        } else if (decision.status === 'rejected') {
          setTranscript(t => [...t, { direction: 'system', body: '✕ You rejected this reply on Telegram — nothing sent.' }]);
        } else if (decision.status === 'timeout') {
          setTranscript(t => [...t, { direction: 'system', body: '⏳ No decision yet (timed out after 5 min). Approve on Telegram and send another message to continue.' }]);
        } else {
          setTranscript(t => [...t, { direction: 'system', body: 'That draft is no longer available.' }]);
        }
        setWaiting(false);
        return;
      }

      if (res.approvalSkipped) {
        setTranscript(t => [...t, { direction: 'system', body: '(approval is ON but Telegram isn\'t configured — showing the draft inline)' }]);
      }
      if (res.reply && res.reply.trim()) {
        setTranscript(t => [...t, { direction: 'outbound', body: res.reply.trim() }]);
      } else {
        // Empty reply is a valid outcome (opted_out / dead). Show a marker.
        setTranscript(t => [...t, { direction: 'system', body: '(bot stayed silent)' }]);
      }
    } catch (e) {
      setErr(e.message || 'Test failed');
    } finally {
      setBusy(false);
    }
  };

  const onKeyDown = (e) => {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); }
  };

  const ex = lastExtract && lastExtract.extracted ? lastExtract.extracted : null;
  const rateStr = ex && ex.rate_cents != null
    ? `$${(ex.rate_cents / 100).toFixed(ex.rate_cents % 100 === 0 ? 0 : 2)}/hr`
    : (ex && ex.rate_raw ? ex.rate_raw : null);

  const roleStyle = {
    inbound:  { align: 'flex-end',   bg: 'oklch(95% 0.04 250)', border: 'oklch(85% 0.05 250)', label: 'You (as teacher)' },
    outbound: { align: 'flex-start', bg: '#fff',                border: TF_TONE.border,        label: 'Bot' },
    system:   { align: 'center',     bg: 'oklch(96% 0.01 265)', border: TF_TONE.border,        label: 'System' },
  };

  return (
    <div style={{
      background: TF_TONE.card, border: `1px solid ${TF_TONE.border}`,
      borderRadius: 14, padding: '16px 18px', marginBottom: 16,
    }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
        <h3 style={{ margin: 0, fontSize: 15, fontWeight: 700, color: TF_TONE.text }}>Test sandbox</h3>
        <button
          onClick={reset}
          disabled={busy || transcript.length === 0}
          style={{
            background: 'transparent', border: `1px solid ${TF_TONE.border}`, borderRadius: 8,
            padding: '5px 11px', fontSize: 11, fontWeight: 600,
            color: transcript.length === 0 ? TF_TONE.border : TF_TONE.muted,
            cursor: (busy || transcript.length === 0) ? 'default' : 'pointer',
            fontFamily: "'Plus Jakarta Sans', sans-serif",
          }}
        >Reset conversation</button>
      </div>
      <div style={{ fontSize: 12, color: TF_TONE.muted, marginTop: 4, marginBottom: 10, lineHeight: 1.45 }}>
        Type as if you're the teacher and watch the bot reply. Uses the instructions above (even unsaved edits). When "Approve replies on Telegram first" is ON, the draft is sent to your Telegram to Approve / Edit / Reject — exactly like a real teacher reply, so you can practice the whole flow. It never creates a lead or texts a real teacher.
      </div>

      <div
        ref={scrollRef}
        style={{
          background: TF_TONE.page, border: `1px solid ${TF_TONE.border}`, borderRadius: 10,
          padding: '12px', minHeight: 120, maxHeight: 320, overflowY: 'auto',
          display: 'flex', flexDirection: 'column', gap: 8,
        }}
      >
        {transcript.length === 0 && (
          <div style={{ fontSize: 12, color: TF_TONE.muted, textAlign: 'center', padding: '24px 0' }}>
            No messages yet. Send a message below to start a test conversation.
          </div>
        )}
        {transcript.map((m, i) => {
          const r = roleStyle[m.direction] || roleStyle.system;
          return (
            <div key={i} style={{ display: 'flex', justifyContent: r.align }}>
              <div style={{ maxWidth: '80%', background: r.bg, border: `1px solid ${r.border}`, borderRadius: 10, padding: '7px 11px' }}>
                <div style={{ fontSize: 9, fontWeight: 700, color: TF_TONE.muted, textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 2 }}>{r.label}</div>
                <div style={{ fontSize: 13, color: TF_TONE.text, whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>{m.body}</div>
              </div>
            </div>
          );
        })}
        {busy && (
          <div style={{ display: 'flex', justifyContent: 'flex-start' }}>
            <div style={{ fontSize: 12, color: TF_TONE.muted, fontStyle: 'italic', padding: '4px 8px' }}>Bot is thinking…</div>
          </div>
        )}
        {waiting && (
          <div style={{ display: 'flex', justifyContent: 'flex-start' }}>
            <div style={{ fontSize: 12, color: 'oklch(40% 0.14 300)', fontWeight: 600, padding: '4px 8px' }}>Waiting for your Telegram approval…</div>
          </div>
        )}
      </div>

      {lastExtract && (
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 10, alignItems: 'center' }}>
          <span style={{
            display: 'inline-block', padding: '3px 10px', borderRadius: 999, fontSize: 10, fontWeight: 700,
            letterSpacing: '0.03em', background: 'oklch(92% 0.03 265)', color: 'oklch(35% 0.04 265)',
          }}>State: {TEST_STATE_LABEL[lastExtract.newState] || lastExtract.newState || '—'}</span>
          {rateStr && (
            <span style={{ display: 'inline-block', padding: '3px 10px', borderRadius: 999, fontSize: 10, fontWeight: 700, background: 'oklch(90% 0.10 145)', color: 'oklch(33% 0.12 145)' }}>Rate: {rateStr}</span>
          )}
          {ex && ex.availability && (
            <span style={{ display: 'inline-block', padding: '3px 10px', borderRadius: 999, fontSize: 10, fontWeight: 700, background: 'oklch(90% 0.10 90)', color: 'oklch(35% 0.10 80)' }}>Avail: {ex.availability}</span>
          )}
        </div>
      )}

      {err && <div style={{ fontSize: 12, color: TF_TONE.danger, marginTop: 8 }}>Error: {err}</div>}

      <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={onKeyDown}
          placeholder={waiting ? 'Approve on Telegram to continue…' : 'Type a message as the teacher…'}
          disabled={busy || waiting}
          style={{
            flex: 1, minWidth: 0, boxSizing: 'border-box',
            border: `1px solid ${TF_TONE.border}`, borderRadius: 999, padding: '10px 16px',
            fontSize: 13, fontFamily: "'Plus Jakarta Sans', sans-serif", color: TF_TONE.text, background: '#fff',
          }}
        />
        <button
          onClick={send}
          disabled={busy || waiting || !input.trim()}
          style={{
            flexShrink: 0, background: (busy || waiting || !input.trim()) ? TF_TONE.border : TF_TONE.text,
            color: (busy || waiting || !input.trim()) ? TF_TONE.muted : '#fff',
            border: 'none', borderRadius: 999, padding: '10px 20px',
            fontSize: 12.5, fontWeight: 700, fontFamily: "'Plus Jakarta Sans', sans-serif",
            cursor: (busy || waiting || !input.trim()) ? 'default' : 'pointer',
          }}
        >Send</button>
      </div>
    </div>
  );
};

// ── Container: loads + holds the editable instructions, feeds both panels ──
// ── Panel 3: Learned guidance (the bot's self-improving knowledge base) ────
// This document is maintained automatically: every time you Edit a drafted
// reply on Telegram, the bot distills the lesson (what it got wrong, what you
// changed, and why) and folds it in here. Over many corrections it accumulates
// your voice + judgement, so the bot needs fewer corrections over time. You can
// also edit or clear it by hand.
const LearnedGuidancePanel = () => {
  const [guidance, setGuidance] = React.useState('');
  const [saved, setSaved] = React.useState('');
  const [loading, setLoading] = React.useState(true);
  const [busy, setBusy] = React.useState(false);
  const [status, setStatus] = React.useState('');
  const [errMsg, setErrMsg] = React.useState('');

  const load = React.useCallback(async () => {
    setLoading(true); setStatus('');
    try {
      const cfg = await window.ADMIN_DATA.teacherLeads.getBotConfig();
      setGuidance(cfg.guidance || ''); setSaved(cfg.guidance || '');
    } catch (e) {
      setErrMsg(e.message || 'Failed to load');
    } finally {
      setLoading(false);
    }
  }, []);
  React.useEffect(() => { load(); }, [load]);

  const dirty = guidance !== saved;
  const save = async () => {
    if (!dirty) return;
    setBusy(true); setStatus(''); setErrMsg('');
    try {
      const g = await window.ADMIN_DATA.teacherLeads.saveBotGuidance(guidance);
      setSaved(g); setGuidance(g); setStatus('saved');
    } catch (e) {
      setStatus('error'); setErrMsg(e.message || 'Save failed');
    } finally {
      setBusy(false);
    }
  };

  return (
    <div style={{
      background: TF_TONE.card, border: `1px solid ${TF_TONE.border}`,
      borderRadius: 14, padding: '16px 18px', marginBottom: 16,
    }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
        <h3 style={{ margin: 0, fontSize: 15, fontWeight: 700, color: TF_TONE.text }}>Bot knowledge base</h3>
        <button
          onClick={load}
          disabled={loading || busy}
          style={{
            background: 'transparent', border: `1px solid ${TF_TONE.border}`, borderRadius: 8,
            padding: '5px 11px', fontSize: 11, fontWeight: 600, color: TF_TONE.muted,
            cursor: (loading || busy) ? 'default' : 'pointer', fontFamily: "'Plus Jakarta Sans', sans-serif",
          }}
        >{loading ? 'Loading…' : 'Refresh'}</button>
      </div>
      <div style={{ fontSize: 12, color: TF_TONE.muted, marginTop: 4, marginBottom: 10, lineHeight: 1.45 }}>
        Rules the bot has learned from your Telegram edits. Every time you Edit a reply, it works out what it got wrong and what you wanted, and adds the lesson here — so it gets better automatically. You can also edit or clear it by hand.
      </div>
      {loading ? (
        <div style={{ fontSize: 12, color: TF_TONE.muted, padding: '8px 0' }}>Loading…</div>
      ) : (
        <React.Fragment>
          <textarea
            value={guidance}
            onChange={(e) => { setGuidance(e.target.value); if (status) setStatus(''); }}
            rows={10}
            placeholder="(empty — it fills in as you correct the bot's replies on Telegram)"
            spellCheck={true}
            style={{
              width: '100%', boxSizing: 'border-box', resize: 'vertical',
              fontFamily: "'SF Mono', ui-monospace, Menlo, monospace", fontSize: 12.5, lineHeight: 1.5,
              color: TF_TONE.text, background: TF_TONE.page,
              border: `1px solid ${TF_TONE.border}`, borderRadius: 10, padding: '11px 12px',
            }}
          />
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginTop: 10, flexWrap: 'wrap' }}>
            <button
              onClick={save}
              disabled={busy || !dirty}
              style={{
                background: (!dirty) ? TF_TONE.border : TF_TONE.text,
                color: (!dirty) ? TF_TONE.muted : '#fff',
                border: 'none', borderRadius: 999, padding: '9px 20px',
                fontSize: 12.5, fontWeight: 700, fontFamily: "'Plus Jakarta Sans', sans-serif",
                cursor: (busy || !dirty) ? 'default' : 'pointer', letterSpacing: '0.02em',
              }}
            >{busy ? 'Saving…' : 'Save knowledge base'}</button>
            {status === 'saved' && !dirty && <span style={{ fontSize: 12, color: TF_TONE.success, fontWeight: 600 }}>Saved.</span>}
            {status === 'error' && <span style={{ fontSize: 12, color: TF_TONE.danger, fontWeight: 600 }}>Error: {errMsg}</span>}
            {dirty && status !== 'error' && <span style={{ fontSize: 12, color: TF_TONE.muted }}>Unsaved changes.</span>}
          </div>
        </React.Fragment>
      )}
    </div>
  );
};

const AdminLeadsBotSettings = () => {
  const [loading, setLoading] = React.useState(true);
  const [err, setErr] = React.useState('');
  const [value, setValue] = React.useState('');       // current textarea value
  const [savedValue, setSavedValue] = React.useState(''); // last persisted value
  // Keep a ref to the latest textarea value so the sandbox always sends the
  // CURRENT text (including unsaved edits) without re-rendering on every keypress.
  const valueRef = React.useRef('');
  valueRef.current = value;

  React.useEffect(() => {
    let alive = true;
    (async () => {
      try {
        const text = await window.ADMIN_DATA.teacherLeads.getBotInstructions();
        if (!alive) return;
        setValue(text); setSavedValue(text);
      } catch (e) {
        if (alive) setErr(e.message || 'Failed to load instructions');
      } finally {
        if (alive) setLoading(false);
      }
    })();
    return () => { alive = false; };
  }, []);

  if (loading) {
    return (
      <div style={{ background: TF_TONE.card, border: `1px solid ${TF_TONE.border}`, borderRadius: 14, padding: '16px 18px', marginBottom: 16, fontSize: 13, color: TF_TONE.muted }}>
        Loading bot instructions…
      </div>
    );
  }
  if (err) {
    return (
      <div style={{ background: TF_TONE.dangerBg, color: TF_TONE.danger, padding: '12px 14px', borderRadius: 12, fontSize: 13, marginBottom: 16 }}>
        Couldn't load bot instructions: {err}
      </div>
    );
  }

  return (
    <div style={{ fontFamily: "'Plus Jakarta Sans', sans-serif" }}>
      <BotInstructionsPanel
        value={value}
        onChange={setValue}
        savedValue={savedValue}
        onSaved={(saved) => { setSavedValue(saved); setValue(saved); }}
      />
      <LearnedGuidancePanel />
      <TestSandboxPanel getInstructions={() => valueRef.current} />
    </div>
  );
};

window.AdminLeadsBotSettings = AdminLeadsBotSettings;
