import { useState } from 'preact/hooks';
import { askAgent, explainPanelQuestion } from '../api.js';
import { renderWithNums } from '../numText.jsx';
import { useExplain } from './ExplainButton.jsx';

// The live LangGraph router emits route "REFUSE" (agent/graph.py's REFUSE
// constant); the offline keyword fallback router emits "refusal" (see
// app/api/main.py). Both mean the same thing to the UI — match either,
// case-insensitively, rather than assume one contract spelling.
export const isRefusalRoute = (route) => /^refus(e|al)$/i.test(route || '');

// The REASONED route (agent/graph.py's REASONED constant): a cited,
// number-disciplined LLM interpretation of a conceptually relevant
// question no tool computes — grounded, never a fresh engine number.
// Prefer the response's own `mode === 'reasoned'` field (docs/
// api_contract.md §POST /api/agent/ask) when it is present; this route
// check is the fallback for callers that only have `route`.
export const isReasonedRoute = (route) => /^reasoned$/i.test(route || '');

// agent/graph.py's REASONED_PREFIX — every reasoned answer's wire text
// starts with this machine-readable marker (belt-and-suspenders alongside
// `mode === 'reasoned'`). The badge below already labels the bubble, so
// strip the marker from the DISPLAYED text rather than show it twice.
const REASONED_PREFIX_RE = /^\[REASONED\s*—\s*interpretation, not engine output\]\s*/;

/** Shared agent-message bubble — used by ChatPanel's own log AND by
 * MiniChatDock's expanded log, so the two chat surfaces render identically. */
export function AgentMessage({ msg }) {
  const isRefusal = msg.mode ? msg.mode === 'refusal' : isRefusalRoute(msg.route);
  const isReasoned = msg.mode ? msg.mode === 'reasoned' : isReasonedRoute(msg.route);
  if (isRefusal) {
    return (
      <div class="msg msg-refusal">
        <span class="refusal-tag">Outside validated scope</span>
        <p>{msg.answer}</p>
      </div>
    );
  }
  if (isReasoned) {
    const text = (msg.answer || '').replace(REASONED_PREFIX_RE, '');
    return (
      <div class="msg msg-agent msg-reasoned">
        <span class="reasoned-tag">Reasoned — interpretation, not engine output</span>
        <p>{renderWithNums(text)}</p>
      </div>
    );
  }
  return (
    <div class="msg msg-agent">
      <p>{renderWithNums(msg.answer)}</p>
      {msg.route && (
        <div class="citations">
          <span class="citations-label">via</span>
          <code class="citation">⚙ {msg.route}</code>
        </div>
      )}
    </div>
  );
}

/**
 * Ask-the-copilot chat. Two skins from the same component:
 *  - mode="dock": compact, used inside the collapsed mini-chat dock.
 *  - mode="full": Copilot tab, with suggestion chips.
 * `contextLabel` (e.g. the current tab name) is prefixed into the question
 * sent to the agent so the router has situational context; it is NOT
 * invented data, just a routing hint, and is shown to the user before send.
 */
export default function ChatPanel({ mode = 'full', contextLabel, suggestions, onResult }) {
  const [messages, setMessages] = useState([]);
  const [question, setQuestion] = useState('');
  const [busy, setBusy] = useState(false);

  // Every panel heading carries the AI-explain affordance — including this
  // one (operator request #3); here it recaps the session's own state
  // rather than an exhibit payload.
  const { button: explainBtn, strip: explainStrip } = useExplain({
    label: 'Ask the copilot',
    buildQuestion:
      mode === 'full'
        ? () =>
            explainPanelQuestion({
              panelId: 'copilot_chat',
              title: 'Ask the copilot',
              recap: messages.length
                ? `${messages.filter((m) => m.role === 'user').length} question(s) asked this session; last route: ${
                    messages.filter((m) => m.role === 'agent').at(-1)?.route ?? 'none yet'
                  }.`
                : 'no questions asked yet this session — the validated routes are six numeric/documentation tools plus a reasoned-interpretation pass for conceptually relevant questions with no fixed tool.',
            })
        : undefined,
  });

  const send = async (q) => {
    const trimmed = q.trim();
    if (!trimmed || busy) return;
    setQuestion('');
    setMessages((m) => [...m, { role: 'user', text: trimmed }]);
    setBusy(true);
    try {
      const wire = contextLabel ? `[${contextLabel}] ${trimmed}` : trimmed;
      const res = await askAgent(wire);
      setMessages((m) => [...m, { role: 'agent', ...res }]);
      onResult?.({ question: trimmed, ...res, at: Date.now() });
    } catch (err) {
      setMessages((m) => [
        ...m,
        { role: 'error', text: `Request failed: ${err.message}` },
      ]);
    } finally {
      setBusy(false);
    }
  };

  const submit = (e) => {
    e.preventDefault();
    send(question);
  };

  const defaultChips =
    suggestions || [
      'What is the reported allowance under the downside scenario?',
      'Write pandas to find the 10 largest-balance Stage 3 loans',
      'What does the double-trigger LTV x UER coefficient mean?',
    ];

  return (
    <section class={`panel chat-panel chat-panel-${mode}`}>
      {mode === 'full' && (
        <>
          <div class="panel-head">
            <h2>Ask the copilot</h2>
            {explainBtn}
          </div>
          <p class="panel-sub">
            Narrated, data-grounded answers — every number traces to an engine
            tool call; out-of-scope questions are refused by design.
          </p>
          {explainStrip}
        </>
      )}
      <div class="chat-log">
        {messages.length === 0 && (
          <div class="empty-note">
            {mode === 'full' ? (
              <div class="chip-row">
                {defaultChips.map((c) => (
                  <button type="button" class="chip" key={c} onClick={() => send(c)}>
                    {c}
                  </button>
                ))}
              </div>
            ) : (
              <>Ask about {contextLabel ? contextLabel.toLowerCase() : 'this book'}…</>
            )}
          </div>
        )}
        {messages.map((msg, i) =>
          msg.role === 'user' ? (
            <div class="msg msg-user" key={i}>
              <p>{msg.text}</p>
            </div>
          ) : msg.role === 'error' ? (
            <div class="msg msg-error" key={i}>
              <p>{msg.text}</p>
            </div>
          ) : (
            <AgentMessage msg={msg} key={i} />
          ),
        )}
        {busy && (
          <div class="explain-status">
            <span class="status-dot status-dot-warn pulse" /> THINKING
          </div>
        )}
      </div>
      <form class="chat-form" onSubmit={submit}>
        <input
          type="text"
          value={question}
          placeholder={
            contextLabel ? `Ask about ${contextLabel}…` : 'Ask about ECL, staging, scenarios…'
          }
          onInput={(e) => setQuestion(e.currentTarget.value)}
          disabled={busy}
        />
        <button class="btn" type="submit" disabled={busy || !question.trim()}>
          Ask
        </button>
      </form>
    </section>
  );
}
