// Splat chat widget — wired to /api/chat (Anthropic via Vercel serverless)

const { useState, useRef, useEffect } = React;

// Compute consecutive-day dose-log streak (mirrors dashboard.computeStreak)
function _computeStreak(logs) {
  if (!logs || logs.length === 0) return 0;
  const dates = new Set(logs.map(l => l.date));
  let streak = 0;
  const d = new Date();
  for (let i = 0; i < 365; i++) {
    const ds = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
    if (dates.has(ds)) {
      streak++;
      d.setDate(d.getDate() - 1);
    } else {
      if (i === 0) { d.setDate(d.getDate() - 1); continue; }
      break;
    }
  }
  return streak;
}

// Find a peptide name by id from the static PEPTIDES catalog
function _peptideName(id) {
  if (typeof PEPTIDES === "undefined") return id;
  const p = PEPTIDES.find(x => x.id === id);
  return p ? p.name : id;
}

// Read local fallback (used when Supabase isn't configured / signed in)
function _readLocal() {
  const lsGet = (k, fb) => {
    try { const v = JSON.parse(localStorage.getItem(k)); return v == null ? fb : v; }
    catch (e) { return fb; }
  };
  return {
    name: (typeof localStorage !== "undefined" && localStorage.getItem("pp_name")) || "",
    stack: lsGet("pp_dash_stack", []),
    journal: lsGet("pp_dash_journal", []),
    symptoms: lsGet("pp_dash_symptoms", []),
    supply: lsGet("pp_dash_supply", []),
    labs: lsGet("pp_dash_labs", []),
    logs: lsGet("pp_dash_logs", []),
    timeline: lsGet("pp_dash_timeline", []),
  };
}

// Read live dashboard state from Supabase (one row per dashboard feature).
// Returns the same shape as _readLocal so the formatter doesn't care.
async function _readSupabase(sb, userId) {
  const tables = [
    "peptide_stack", "dose_logs", "journal_entries",
    "symptoms", "labs", "supply", "timeline",
  ];
  const results = await Promise.all(tables.map(t =>
    sb.from(t).select("*").eq("user_id", userId).order("created_at", { ascending: true })
  ));
  // Pull the user's profile for name
  const { data: profile } = await sb.from("profiles").select("name").eq("id", userId).single();
  const [stack, logs, journal, symptoms, labs, supply, timeline] = results.map(r => r.data || []);
  // Normalise the column "when_" back to "when" so the formatter matches local data
  const norm = (rows) => rows.map(r => {
    const out = { ...r };
    if (out.when_ != null) { out.when = out.when_; delete out.when_; }
    return out;
  });
  return {
    name: (profile && profile.name) || (typeof localStorage !== "undefined" && localStorage.getItem("pp_name")) || "",
    stack: norm(stack).map(r => ({ id: r.peptide_id, dose: r.dose, when: r.when, done: r.done, note: r.note, locked: r.locked })),
    logs: norm(logs).map(r => ({ id: r.peptide_id, dose: r.dose, when: r.when, date: r.date })),
    journal: journal.map(r => ({ date: r.date, text: r.text })),
    symptoms: symptoms.map(r => ({ date: r.date, text: r.text })),
    labs: labs.map(r => ({ name: r.name, date: r.date, status: r.status })),
    supply: supply.map(r => ({ id: r.peptide_id, level: r.level, daysLeft: r.days_left, low: r.low })),
    timeline: timeline.map(r => ({ id: r.peptide_id, start: r.start_week, end: r.end_week, label: r.label, active: r.active })),
  };
}

// Format the dashboard data into a context string Splat sees in his system prompt.
function _formatContext(d) {
  const lines = [];
  if (d.name) lines.push(`User's name: ${d.name}`);

  if (d.stack && d.stack.length) {
    lines.push("Active peptide stack:");
    d.stack.forEach(s => {
      const parts = [`- ${_peptideName(s.id)}`];
      if (s.dose) parts.push(`dose ${s.dose}`);
      if (s.when) parts.push(`schedule ${s.when}`);
      if (s.note) parts.push(`note: ${s.note}`);
      lines.push(parts.join(" · "));
    });
  } else {
    lines.push("Active peptide stack: (none yet — user hasn't added any peptides)");
  }

  if (d.logs && d.logs.length) {
    const streak = _computeStreak(d.logs);
    const last = d.logs[d.logs.length - 1] || {};
    lines.push(`Dose log: ${d.logs.length} total entries, last on ${last.date || "n/a"} (${_peptideName(last.id)} ${last.dose || ""}). Current streak: ${streak} day(s).`);
    // Recent 5 logs
    const recent = d.logs.slice(-5).map(l => `[${l.date}] ${_peptideName(l.id)} ${l.dose || ""} @ ${l.when || "—"}`);
    lines.push("Recent doses: " + recent.join(" | "));
  }

  if (d.journal && d.journal.length) {
    const recent = d.journal.slice(-5).map(j => `[${j.date}] ${j.text}`).join(" | ");
    lines.push(`Journal entries (${d.journal.length} total). Recent: ${recent}`);
  }

  if (d.symptoms && d.symptoms.length) {
    const recent = d.symptoms.slice(-5).map(s => `[${s.date}] ${s.text}`).join(" | ");
    lines.push(`Symptom log (${d.symptoms.length} total). Recent: ${recent}`);
  }

  if (d.supply && d.supply.length) {
    const all = d.supply.map(s => {
      const low = s.low || (s.daysLeft != null && s.daysLeft <= 7);
      return `${_peptideName(s.id)}: ${s.level != null ? s.level + "%" : "?"}` +
             `${s.daysLeft != null ? ", " + s.daysLeft + "d left" : ""}` +
             `${low ? " ⚠ LOW" : ""}`;
    });
    lines.push("Supply: " + all.join(" | "));
  }

  if (d.labs && d.labs.length) {
    lines.push(`Labs uploaded: ${d.labs.length}. Most recent: ${d.labs.slice(-3).map(l => `${l.name} (${l.status || "?"})`).join(" | ")}`);
  }

  if (d.timeline && d.timeline.length) {
    lines.push("Cycle timeline:");
    d.timeline.forEach(t => {
      lines.push(`- ${_peptideName(t.id)}: weeks ${t.start ?? "?"}–${t.end ?? "?"} ${t.active ? "(active)" : "(planned)"}`);
    });
  }

  return lines.join("\n");
}

// Build the full Splat context — async because we may hit Supabase.
async function buildContext() {
  try {
    const sb = (typeof window !== "undefined" && window.getSupabase) ? window.getSupabase() : null;
    if (sb) {
      try {
        const { data } = await sb.auth.getSession();
        if (data && data.session && data.session.user) {
          const live = await _readSupabase(sb, data.session.user.id);
          return _formatContext(live);
        }
      } catch (e) { /* fall through to local */ }
    }
    return _formatContext(_readLocal());
  } catch (e) {
    return "";
  }
}

const INITIAL_MSG = {
  from: "splat",
  emote: "waves",
  text: "Hey there! 🫧 I'm Splat — your peptide pal. Ask me anything: dosing, side effects, stacks, or how to read your labs.",
  prompts: ["What is BPC-157?", "How do I rotate sites?", "Is my stack ok?", "Tell me a joke"],
};

// Char-by-char streaming with blinking caret (Variation #5).
// Used for Splat's replies so they feel alive instead of plopping in.
const StreamingText = ({ text = "", speedMs = 14, onDone }) => {
  const [shown, setShown] = useState("");
  const doneRef = useRef(false);

  useEffect(() => {
    setShown("");
    doneRef.current = false;
    if (!text) return;
    let i = 0;
    const tick = () => {
      i += 1;
      setShown(text.slice(0, i));
      if (i < text.length) {
        timer = setTimeout(tick, speedMs);
      } else {
        doneRef.current = true;
        onDone && onDone();
      }
    };
    let timer = setTimeout(tick, speedMs);
    return () => clearTimeout(timer);
  }, [text, speedMs]);

  const isStreaming = shown.length < text.length;
  return (
    <span style={{ whiteSpace: "pre-wrap" }}>
      {shown}
      <span
        aria-hidden="true"
        style={{
          display: "inline-block",
          width: 1,
          height: "1em",
          background: "currentColor",
          verticalAlign: "-2px",
          marginLeft: 2,
          opacity: isStreaming ? 1 : 0,
          animation: "splatCaretBlink 1s steps(1) infinite",
        }}
      />
    </span>
  );
};

const ChatWindow = ({ onClose, inline = false }) => {
  const [messages, setMessages] = useState([INITIAL_MSG]);
  const [input, setInput] = useState("");
  const [typing, setTyping] = useState(false);
  const bodyRef = useRef(null);

  useEffect(() => {
    if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
  }, [messages, typing]);

  const send = async (text) => {
    if (!text.trim() || typing) return;
    const next = [...messages, { from: "me", text }];
    setMessages(next);
    setInput("");
    setTyping(true);

    // Build conversation history for the API (skip the canned intro on first turn)
    const history = next
      .filter(m => m.from === "me" || (m.from === "splat" && m !== INITIAL_MSG))
      .map(m => ({ role: m.from === "me" ? "user" : "assistant", content: m.text }));

    try {
      const ctx = await buildContext();
      const res = await fetch("/api/chat", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ messages: history, context: ctx }),
      });
      const data = await res.json();
      if (data.reply) {
        setMessages([...next, { from: "splat", text: data.reply, stream: true }]);
      } else {
        setMessages([...next, {
          from: "splat",
          emote: "hiccup",
          text: "Hmm, I had trouble reaching my brain just now. " + (data.error || "") + (data.details ? " — " + data.details : ""),
          stream: true,
        }]);
      }
    } catch (err) {
      setMessages([...next, {
        from: "splat",
        emote: "offline",
        text: "I'm having connection issues. Try again in a moment!",
        stream: true,
      }]);
    }
    setTyping(false);
  };

  const inlineStyle = inline ? {
    position: "static",
    width: "100%",
    height: "calc(100vh - 220px)",
    minHeight: 480,
    bottom: "auto",
    right: "auto",
    boxShadow: "var(--shadow-sticker)",
    animation: "none",
  } : null;

  return (
    <div className="chat-window" style={inlineStyle}>
      <style>{`@keyframes splatCaretBlink { 50% { opacity: 0; } }`}</style>
      <div className="chat-header">
        <div className="avatar"><SplatBadge size={32} expression="happy" /></div>
        <div>
          <div className="nm">Splat</div>
          <div className="status">your peptide pal</div>
        </div>
        {!inline && <div className="close-btn" onClick={onClose}>×</div>}
      </div>
      <div className="chat-body" ref={bodyRef}>
        {messages.map((m, i) => (
          m.from === "splat" ? (
            <div key={i}>
              <div className="msg splat">
                {m.emote && <div className="emote">※ {m.emote}</div>}
                {m.stream
                  ? <div><StreamingText text={m.text} /></div>
                  : <div style={{ whiteSpace: "pre-wrap" }}>{m.text}</div>}
              </div>
              {m.prompts && (
                <div className="quick-prompts">
                  {m.prompts.map((p, j) => (
                    <button key={j} className="quick-prompt" onClick={() => send(p)}>{p}</button>
                  ))}
                </div>
              )}
            </div>
          ) : (
            <div key={i} className="msg me">{m.text}</div>
          )
        ))}
        {typing && (
          <div className="msg splat" style={{ display: "inline-flex", alignItems: "center", gap: 12 }}>
            <span className="loader3 sm" aria-hidden="true"><b/><b/><b/></span>
            <span style={{ fontSize: 12, color: "var(--muted)", fontFamily: "var(--mono)", letterSpacing: "0.06em" }}>splat is thinking…</span>
          </div>
        )}
      </div>
      <form className="chat-input" onSubmit={(e) => { e.preventDefault(); send(input); }}>
        <input
          placeholder="Ask Splat anything…"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          disabled={typing}
        />
        <button className="send-btn" type="submit" disabled={typing || !input.trim()}>↑</button>
      </form>
    </div>
  );
};

const SplatFab = ({ onOpen, tip, onDismissTip }) => (
  <div className="splat-fab">
    {tip && (
      <div className="splat-fab-tip">
        <div className="tip-close" onClick={onDismissTip}>×</div>
        <div className="tip-tag">※ splat thinks</div>
        {tip}
      </div>
    )}
    <button className="splat-fab-btn" onClick={onOpen} aria-label="Chat with Splat">
      <Splat size={68} expression="wink" />
    </button>
  </div>
);

Object.assign(window, { ChatWindow, SplatFab });
