// ============================================================
// DIRECTION 1 — AI COACH · CHATBOT
// Refonte : conversation streamée type ChatGPT/Claude.
// Edge Function ai-chat (claude-haiku-4-5, SSE), digest des
// trades 30j en contexte, quota journalier géré côté serveur,
// historique persisté en localStorage par utilisateur.
// ============================================================

const QT_CHAT_ENDPOINT = "https://ctrbtvqogntpkrkwajvn.supabase.co/functions/v1/ai-chat";
const QT_CHAT_MAX_SENT = 20;      // messages d'historique envoyés au serveur
const QT_CHAT_MAX_STORED = 80;    // messages gardés en localStorage

// Digest compact des trades — uniquement des agrégats anonymisés.
const qtBuildCoachDigest = (trades) => {
  const total = trades.length;
  const wins = trades.filter(t => (t.result || "").toUpperCase() === "WIN").length;
  const losses = trades.filter(t => (t.result || "").toUpperCase() === "LOSS").length;

  const groupBy = (keyFn) => {
    const m = {};
    for (const t of trades) {
      const k = keyFn(t) || "?";
      if (!m[k]) m[k] = { trades: 0, wins: 0, pnl: 0 };
      m[k].trades += 1;
      m[k].pnl = +(m[k].pnl + (Number(t.pnl) || 0)).toFixed(2);
      if ((t.result || "").toUpperCase() === "WIN") m[k].wins += 1;
    }
    return m;
  };

  const byHourBucket = groupBy(t => {
    if (!t.date) return "?";
    const h = new Date(t.date).getUTCHours();
    if (h < 7) return "00-07 UTC";
    if (h < 12) return "07-12 UTC";
    if (h < 17) return "12-17 UTC";
    return "17-24 UTC";
  });

  const byWeekday = groupBy(t => {
    if (!t.date) return "?";
    return ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"][new Date(t.date).getDay()];
  });

  const avgR = (filterRes) => {
    const sel = trades.filter(t => (t.result || "").toUpperCase() === filterRes);
    if (sel.length === 0) return 0;
    return +(sel.reduce((s, t) => s + (Number(t.r_multiple) || 0), 0) / sel.length).toFixed(2);
  };

  return {
    period_days: 30,
    currency: "EUR",
    total_trades: total,
    win_rate: total > 0 ? +(wins / total).toFixed(3) : 0,
    total_pnl: +trades.reduce((s, t) => s + (Number(t.pnl) || 0), 0).toFixed(2),
    avg_r_win: avgR("WIN"),
    avg_r_loss: avgR("LOSS"),
    wins,
    losses,
    by_asset: groupBy(t => t.asset),
    by_session: groupBy(t => t.session),
    by_strategy: groupBy(t => t.strategy),
    by_hour_bucket_utc: byHourBucket,
    by_weekday: byWeekday,
  };
};

// ── Rendu markdown-lite (gras + puces + paragraphes) ────────────
const qtChatInline = (text, keyBase) => {
  const parts = String(text).split(/\*\*(.+?)\*\*/g);
  return parts.map((p, i) =>
    i % 2 === 1
      ? <strong key={`${keyBase}-b${i}`} style={{ color: QT.text, fontWeight: 700 }}>{p}</strong>
      : <span key={`${keyBase}-t${i}`}>{p}</span>
  );
};

const QTChatBody = ({ text }) => {
  const lines = String(text || "").split("\n");
  const out = [];
  lines.forEach((line, i) => {
    const trimmed = line.trim();
    if (trimmed === "") {
      out.push(<div key={`sp${i}`} style={{ height: 8 }} />);
    } else if (/^[-•]\s+/.test(trimmed)) {
      out.push(
        <div key={`li${i}`} style={{ display: "flex", gap: 8, paddingLeft: 4, marginBottom: 3 }}>
          <span style={{ color: QT.indigo, flexShrink: 0 }}>·</span>
          <span>{qtChatInline(trimmed.replace(/^[-•]\s+/, ""), `li${i}`)}</span>
        </div>
      );
    } else {
      out.push(<div key={`p${i}`} style={{ marginBottom: 2 }}>{qtChatInline(trimmed, `p${i}`)}</div>);
    }
  });
  return <div>{out}</div>;
};

// ── Composant principal ─────────────────────────────────────────
const QTCoach = ({ go } = {}) => {
  const [boot, setBoot] = React.useState("loading");   // loading | ready | error
  const [bootError, setBootError] = React.useState("");
  const [messages, setMessages] = React.useState([]);  // {role, content}
  const [input, setInput] = React.useState("");
  const [streaming, setStreaming] = React.useState(false);
  const [quota, setQuota] = React.useState(null);      // {used, limit, plan}
  const [quotaBlocked, setQuotaBlocked] = React.useState(false);
  const [stats, setStats] = React.useState(null);      // bandeau contexte

  const digestRef = React.useRef(null);
  const userIdRef = React.useRef(null);
  const tokenRef = React.useRef(null);
  const abortRef = React.useRef(null);
  const scrollRef = React.useRef(null);
  const inputRef = React.useRef(null);
  const messagesRef = React.useRef([]);
  messagesRef.current = messages;

  const storageKey = () => `nomerus_coach_chat_${userIdRef.current || "anon"}`;

  const persist = (msgs) => {
    try {
      localStorage.setItem(storageKey(), JSON.stringify(msgs.slice(-QT_CHAT_MAX_STORED)));
    } catch (e) { /* stockage plein : tant pis */ }
  };

  // ── Bootstrap : session + trades 30j → digest ────────────────
  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const sb = window.nomerusSb();
        if (!sb) throw new Error("Connexion Supabase indisponible.");
        const { data: { session } } = await sb.auth.getSession();
        if (!session || !session.access_token) throw new Error("Session expirée. Reconnecte-toi.");
        userIdRef.current = session.user && session.user.id;
        tokenRef.current = session.access_token;

        const since = new Date(Date.now() - 30 * 86400e3).toISOString();
        const { data: trades, error: tErr } = await sb
          .from("trades")
          .select("date, asset, direction, pnl, r_multiple, result, session, strategy")
          .gte("date", since)
          .order("date", { ascending: true });
        if (tErr) throw tErr;
        if (cancelled) return;

        const list = trades || [];
        digestRef.current = qtBuildCoachDigest(list);

        let net = 0, wins = 0;
        for (const t of list) {
          net += Number(t.pnl) || 0;
          if ((t.result || "").toUpperCase() === "WIN") wins += 1;
        }
        setStats({
          count: list.length,
          winRate: list.length > 0 ? (wins / list.length) * 100 : 0,
          net,
        });

        // Historique local
        try {
          const saved = JSON.parse(localStorage.getItem(storageKey()) || "[]");
          if (Array.isArray(saved)) setMessages(saved.slice(-QT_CHAT_MAX_STORED));
        } catch (e) { /* historique corrompu : on repart à zéro */ }

        // Consommation du jour (lecture directe, RLS "select own")
        try {
          const today = new Date().toISOString().slice(0, 10);
          const { data: usage } = await sb
            .from("ai_chat_usage").select("messages")
            .eq("user_id", userIdRef.current).eq("day", today).maybeSingle();
          if (usage && typeof usage.messages === "number") {
            setQuota({ used: usage.messages, limit: null, plan: null });
          }
        } catch (e) { /* table pas encore migrée : non bloquant */ }

        setBoot("ready");
      } catch (e) {
        if (cancelled) return;
        console.error("[coach]", e);
        setBootError(e.message || "Erreur inconnue.");
        setBoot("error");
      }
    })();
    return () => {
      cancelled = true;
      if (abortRef.current) abortRef.current.abort();
    };
  }, []);

  // Auto-scroll en bas à chaque nouveau contenu
  React.useEffect(() => {
    const el = scrollRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [messages, streaming]);

  // ── Envoi d'un message ────────────────────────────────────────
  const sendMessage = async (rawText) => {
    const text = String(rawText || "").trim();
    if (!text || streaming || quotaBlocked) return;

    const base = [...messagesRef.current, { role: "user", content: text }];
    const withPlaceholder = [...base, { role: "assistant", content: "" }];
    setMessages(withPlaceholder);
    persist(base);
    setInput("");
    setStreaming(true);

    const setAssistant = (updater) => {
      setMessages(prev => {
        const next = prev.slice();
        const last = next[next.length - 1];
        if (last && last.role === "assistant") {
          next[next.length - 1] = { role: "assistant", content: updater(last.content) };
        }
        return next;
      });
    };

    const controller = new AbortController();
    abortRef.current = controller;

    try {
      // Rafraîchir le token si besoin (session longue durée)
      const sb = window.nomerusSb();
      if (sb) {
        const { data: { session } } = await sb.auth.getSession();
        if (session && session.access_token) tokenRef.current = session.access_token;
      }

      const resp = await fetch(QT_CHAT_ENDPOINT, {
        method: "POST",
        signal: controller.signal,
        headers: {
          "authorization": `Bearer ${tokenRef.current}`,
          "content-type": "application/json",
        },
        body: JSON.stringify({
          messages: base.slice(-QT_CHAT_MAX_SENT),
          digest: digestRef.current || undefined,
        }),
      });

      if (resp.status === 429) {
        const body = await resp.json().catch(() => ({}));
        setQuota({ used: body.used, limit: body.limit, plan: body.plan });
        setQuotaBlocked(true);
        setAssistant(() =>
          "Tu as atteint ta limite de messages du Coach pour aujourd'hui. " +
          "Le compteur se remet à zéro à minuit (UTC) — reviens demain, ton journal sera toujours là.");
        return;
      }
      if (!resp.ok || !resp.body) {
        const body = await resp.json().catch(() => ({}));
        throw new Error(
          body.error === "ai_temporarily_disabled"
            ? "Le Coach IA est temporairement indisponible. Réessaie dans quelques minutes."
            : `Le Coach n'a pas pu répondre (${resp.status}). Réessaie dans un instant.`
        );
      }

      // Lecture du flux SSE
      const reader = resp.body.getReader();
      const decoder = new TextDecoder();
      let buffer = "";
      let gotDelta = false;

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        let idx;
        while ((idx = buffer.indexOf("\n")) !== -1) {
          const line = buffer.slice(0, idx).trimEnd();
          buffer = buffer.slice(idx + 1);
          if (!line.startsWith("data:")) continue;
          let ev;
          try { ev = JSON.parse(line.slice(5).trim()); } catch (e) { continue; }
          if (ev.type === "delta" && typeof ev.text === "string") {
            gotDelta = true;
            setAssistant(prev => prev + ev.text);
          } else if (ev.type === "done") {
            if (ev.quota) setQuota(ev.quota);
          } else if (ev.type === "error") {
            throw new Error("Le flux de réponse a été interrompu. Réessaie.");
          }
        }
      }
      if (!gotDelta) throw new Error("Réponse vide du Coach. Réessaie.");

      // Persister l'échange complet
      setMessages(prev => { persist(prev); return prev; });
    } catch (e) {
      if (e && e.name === "AbortError") {
        // Arrêt volontaire : on garde ce qui a été streamé
        setMessages(prev => { persist(prev); return prev; });
      } else {
        console.error("[coach chat]", e);
        setAssistant(prev => prev || `⚠ ${e.message || "Erreur inconnue."}`);
      }
    } finally {
      abortRef.current = null;
      setStreaming(false);
      if (inputRef.current) inputRef.current.focus();
    }
  };

  const stopStreaming = () => {
    if (abortRef.current) abortRef.current.abort();
  };

  const newConversation = () => {
    if (streaming) stopStreaming();
    setMessages([]);
    try { localStorage.removeItem(storageKey()); } catch (e) { /* ignore */ }
  };

  const onKeyDown = (e) => {
    if (e.key === "Enter" && !e.shiftKey) {
      e.preventDefault();
      sendMessage(input);
    }
  };

  // ── États non-nominaux ────────────────────────────────────────
  if (boot === "loading") {
    return (
      <div style={{ padding: 18 }}>
        <div style={{
          background: QT.panel, border: `1px solid ${QT.border}`, borderRadius: 4,
          padding: "60px 22px", textAlign: "center",
        }}>
          <div style={{ fontFamily: QT.mono, fontSize: 10, color: QT.indigo, letterSpacing: 2, marginBottom: 16 }}>
            ◆ COACH IA · CHARGEMENT
          </div>
          <div style={{ fontFamily: QT.sans, fontSize: 14, color: QT.textDim }}>
            Le Coach charge le contexte de ton journal…
          </div>
        </div>
      </div>
    );
  }

  if (boot === "error") {
    return (
      <div style={{ padding: 18 }}>
        <div style={{
          background: QT.panel, border: `1px solid rgba(255, 107, 122, 0.3)`, borderRadius: 4,
          padding: "50px 22px", textAlign: "center",
        }}>
          <div style={{ fontFamily: QT.mono, fontSize: 10, color: QT.red, letterSpacing: 2, marginBottom: 16 }}>
            // COACH INDISPONIBLE
          </div>
          <div style={{ fontFamily: QT.sans, fontSize: 14, color: QT.textDim, marginBottom: 22 }}>{bootError}</div>
          <button onClick={() => window.location.reload()} style={qtBtnPrimary}>RÉESSAYER</button>
        </div>
      </div>
    );
  }

  // ── Chat ──────────────────────────────────────────────────────
  const suggestions = (stats && stats.count >= 5) ? [
    "Fais-moi le bilan de mes 30 derniers jours",
    "Quel est mon plus gros point faible en ce moment ?",
    "Sur quelles sessions / actifs suis-je le plus rentable ?",
    "Est-ce que je respecte mon risk management ?",
  ] : [
    "Comment bien démarrer mon journal de trading ?",
    "Qu'est-ce que tu pourras m'apprendre une fois mes trades journalisés ?",
  ];

  const empty = messages.length === 0;

  return (
    <div style={{ height: "100%", display: "flex", flexDirection: "column", overflow: "hidden" }}>

      {/* Bandeau contexte + actions */}
      <div style={{
        display: "flex", alignItems: "center", justifyContent: "space-between",
        padding: "10px 18px", borderBottom: `1px solid ${QT.border}`, background: QT.panel,
        fontFamily: QT.mono, fontSize: 10, color: QT.textDim, letterSpacing: 1.5,
        flexWrap: "wrap", gap: 8, flexShrink: 0,
      }}>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
          <span style={{ color: QT.indigo, fontWeight: 700 }}>◆ CONTEXTE LIVE</span>
          <span style={{ color: QT.textMute }}>·</span>
          {stats && stats.count > 0 ? (
            <React.Fragment>
              <span>{stats.count} TRADES · 30J</span>
              <span style={{ color: QT.textMute }}>·</span>
              <span>WR {qtFmt(stats.winRate, 1)}%</span>
              <span style={{ color: QT.textMute }}>·</span>
              <span style={{ color: stats.net >= 0 ? QT.green : QT.red }}>
                {qtSign(stats.net)}{qtFmt(Math.abs(stats.net))} CHF
              </span>
            </React.Fragment>
          ) : (
            <span style={{ color: QT.amber }}>AUCUN TRADE SUR 30J — LE COACH T'AIDE À DÉMARRER</span>
          )}
        </span>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 12 }}>
          {quota && quota.limit != null && (
            <span style={{ color: quotaBlocked ? QT.amber : QT.textMute }}>
              {Math.min(quota.used, quota.limit)}/{quota.limit} MSG AUJOURD'HUI
            </span>
          )}
          {!empty && (
            <button onClick={newConversation} style={{
              background: "transparent", border: `1px solid ${QT.border}`, borderRadius: 4,
              color: QT.textDim, cursor: "pointer", fontFamily: QT.mono, fontSize: 9,
              letterSpacing: 1.5, padding: "5px 10px",
            }}>+ NOUVELLE CONVERSATION</button>
          )}
        </span>
      </div>

      {/* Fil de messages */}
      <div ref={scrollRef} style={{ flex: 1, overflowY: "auto", padding: "18px 18px 8px" }}>
        <div style={{ maxWidth: 860, margin: "0 auto", display: "flex", flexDirection: "column", gap: 14 }}>

          {empty && (
            <div style={{ textAlign: "center", padding: "48px 16px 24px" }}>
              <div style={{ fontFamily: QT.mono, fontSize: 10, color: QT.indigo, letterSpacing: 2, marginBottom: 14 }}>
                ◆ COACH IA
              </div>
              <div style={{ fontFamily: QT.sans, fontSize: 20, fontWeight: 600, color: QT.text, marginBottom: 8, letterSpacing: -0.3 }}>
                Pose-moi une question sur ton trading.
              </div>
              <div style={{ fontFamily: QT.sans, fontSize: 13, color: QT.textDim, maxWidth: 520, margin: "0 auto 26px", lineHeight: 1.6 }}>
                Je connais tes trades des 30 derniers jours : performance, sessions, actifs,
                risk management, psychologie. Disponible 24h/24.
              </div>
              <div style={{ display: "flex", flexWrap: "wrap", gap: 8, justifyContent: "center" }}>
                {suggestions.map((s) => (
                  <button key={s} onClick={() => sendMessage(s)} style={{
                    background: QT.panel, border: `1px solid ${QT.border}`, borderRadius: 4,
                    color: QT.textDim, cursor: "pointer", fontFamily: QT.sans, fontSize: 12.5,
                    padding: "9px 14px", lineHeight: 1.4,
                  }}>{s}</button>
                ))}
              </div>
            </div>
          )}

          {messages.map((m, i) => {
            const isUser = m.role === "user";
            const isStreamingMsg = streaming && !isUser && i === messages.length - 1;
            return (
              <div key={i} style={{ display: "flex", justifyContent: isUser ? "flex-end" : "flex-start" }}>
                <div style={{
                  maxWidth: "78%",
                  background: isUser ? QT.indigoSoft : QT.panel,
                  border: `1px solid ${isUser ? QT.borderStrong : QT.border}`,
                  borderRadius: 6,
                  padding: "11px 14px",
                }}>
                  <div style={{
                    fontFamily: QT.mono, fontSize: 8.5, letterSpacing: 1.5, marginBottom: 6,
                    color: isUser ? QT.indigo : QT.textMute,
                  }}>
                    {isUser ? "TOI" : "◆ COACH"}
                  </div>
                  <div style={{ fontFamily: QT.sans, fontSize: 13.5, color: QT.text, lineHeight: 1.65 }}>
                    {m.content
                      ? <QTChatBody text={m.content} />
                      : (isStreamingMsg
                          ? <span style={{ color: QT.textDim }}>Le Coach réfléchit…</span>
                          : null)}
                    {isStreamingMsg && m.content ? (
                      <span style={{ color: QT.indigo }}>▌</span>
                    ) : null}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>

      {/* Saisie */}
      <div style={{ flexShrink: 0, borderTop: `1px solid ${QT.border}`, background: QT.panel, padding: "12px 18px 8px" }}>
        <div style={{ maxWidth: 860, margin: "0 auto" }}>
          <div style={{
            display: "flex", gap: 8, alignItems: "flex-end",
            background: QT.bg, border: `1px solid ${QT.borderStrong}`, borderRadius: 6, padding: 8,
          }}>
            <textarea
              ref={inputRef}
              value={input}
              onChange={(e) => setInput(e.target.value)}
              onKeyDown={onKeyDown}
              rows={Math.min(5, Math.max(1, input.split("\n").length))}
              placeholder={quotaBlocked
                ? "Limite quotidienne atteinte — reviens demain."
                : "Écris au Coach… (Entrée pour envoyer, Maj+Entrée pour une nouvelle ligne)"}
              disabled={quotaBlocked}
              style={{
                flex: 1, resize: "none", background: "transparent", border: "none", outline: "none",
                color: QT.text, fontFamily: QT.sans, fontSize: 13.5, lineHeight: 1.5, padding: "4px 6px",
              }}
            />
            {streaming ? (
              <button onClick={stopStreaming} title="Arrêter la réponse" style={{
                background: "transparent", border: `1px solid ${QT.border}`, borderRadius: 4,
                color: QT.red, cursor: "pointer", fontFamily: QT.mono, fontSize: 10,
                letterSpacing: 1.5, padding: "9px 14px", flexShrink: 0,
              }}>■ STOP</button>
            ) : (
              <button
                onClick={() => sendMessage(input)}
                disabled={!input.trim() || quotaBlocked}
                style={{
                  background: input.trim() && !quotaBlocked ? QT.indigo : QT.border,
                  border: "none", borderRadius: 4,
                  color: input.trim() && !quotaBlocked ? QT.bg : QT.textMute,
                  cursor: input.trim() && !quotaBlocked ? "pointer" : "default",
                  fontFamily: QT.mono, fontSize: 10, fontWeight: 700,
                  letterSpacing: 1.5, padding: "9px 16px", flexShrink: 0,
                }}>ENVOYER →</button>
            )}
          </div>
          <div style={{
            fontFamily: QT.mono, fontSize: 9, color: QT.textMute, letterSpacing: 1,
            padding: "7px 4px 4px", lineHeight: 1.5, textAlign: "center",
          }}>
            Le Coach IA analyse tes trades journalisés (agrégats anonymisés). Il ne prédit pas les
            marchés, ne donne aucun conseil financier et n'exécute aucun ordre.
          </div>
        </div>
      </div>
    </div>
  );
};

window.QTCoach = QTCoach;
