// AI panel - analysis, suggestions, chat
const { useState: useStateAI, useRef: useRefAI, useEffect: useEffectAI } = React;

const GOOGLE_CLIENT_ID_STORAGE = "google-oauth-client-id";
const GEMINI_MODEL_STORAGE = "gemini-model-id";
const GEMINI_MEMORY_STORAGE = "gemini-ai-memory";
const GEMINI_SCOPE = "https://www.googleapis.com/auth/cloud-platform";

const GEMINI_MODELS = [
  { id: "gemini-2.0-flash-001",  label: "Flash 2.0", desc: "快速、省 token" },
  { id: "gemini-2.5-flash",  label: "Flash 2.5", desc: "均衡、推理更強" },
];

function vertexEndpoint(modelId) {
  const { googleProjectId, vertexRegion } = window.APP_CONFIG || {};
  const proj = googleProjectId || "";
  const region = vertexRegion || "us-central1";
  return `https://${region}-aiplatform.googleapis.com/v1/projects/${proj}/locations/${region}/publishers/google/models/${modelId}:generateContent`;
}

async function callGemini(accessToken, modelId, systemText, history, userText) {
  const contents = [];
  for (let i = 0; i < history.length; i++) {
    contents.push({
      role: history[i].role === "ai" ? "model" : "user",
      parts: [{ text: history[i].text }],
    });
  }
  contents.push({ role: "user", parts: [{ text: userText }] });

  const res = await fetch(vertexEndpoint(modelId), {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${accessToken}`,
    },
    body: JSON.stringify({
      systemInstruction: { parts: [{ text: systemText }] },
      contents,
      generationConfig: { maxOutputTokens: 8192, temperature: 0.7 },
    }),
  });

  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    const msg = err.error?.message || `HTTP ${res.status}`;
    if (res.status === 401) throw new Error("TOKEN_EXPIRED");
    throw new Error(msg);
  }

  const json = await res.json();
  const parts = json.candidates?.[0]?.content?.parts || [];
  const text = parts.filter(p => !p.thought).map(p => p.text || "").join("").trim();
  return text || "（模型無回應，請重試）";
}

function applyInline(text) {
  return text
    .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
    .replace(/\*(.+?)\*/g, '<em>$1</em>')
    .replace(/`(.+?)`/g, '<code style="background:rgba(0,0,0,0.07);padding:1px 4px;border-radius:3px;font-size:0.88em;font-family:monospace">$1</code>');
}

function renderMarkdown(raw) {
  const esc = raw.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  const lines = esc.split('\n');
  let html = '';
  let inList = false;
  for (let i = 0; i < lines.length; i++) {
    const m = lines[i].match(/^[-•*]\s+(.+)/);
    if (m) {
      if (!inList) { html += '<ul style="margin:5px 0 5px 14px;padding:0;list-style:disc">'; inList = true; }
      html += `<li style="margin-bottom:2px">${applyInline(m[1])}</li>`;
    } else {
      if (inList) { html += '</ul>'; inList = false; }
      const t = lines[i].trim();
      if (t === '') {
        if (i < lines.length - 1) html += '<div style="height:5px"></div>';
      } else {
        html += `<div>${applyInline(t)}</div>`;
      }
    }
  }
  if (inList) html += '</ul>';
  return html;
}

const MAX_HIST = 20;
const WEEKLY_ANALYSIS_PREFIX = "diet-diary-weekly-ai-";

function isValidAnalysisJSON(str) {
  if (!str) return false;
  try {
    const m = str.match(/\{[\s\S]*\}/);
    if (!m) return false;
    const obj = JSON.parse(m[0]);
    return !!(obj.diet && obj.exercise && obj.body && obj.motivation);
  } catch (e) { return false; }
}

function AIPanel({ data, profile, projectedDate, expectedKgPerWeek }) {
  const final = data[data.length - 1];
  const first = data[0];
  const totalLost = first.weight - final.weight;
  const fatLost = first.bodyFat - final.bodyFat;
  const muscleChange = final.muscle - first.muscle;
  const visceralChange = first.visceralFat - final.visceralFat;

  const projDateStr = projectedDate
    ? `${projectedDate.getFullYear()}/${projectedDate.getMonth()+1}/${projectedDate.getDate()}`
    : null;

  // 是否逾期：預估完成日晚於目標日，才顯示「需提速」
  const isBehindSchedule = projectedDate && projectedDate > new Date(profile.goalDate);

  const score = (() => {
    // A. 目標進度 (25分)
    const totalGoal = profile.startWeight - profile.goalWeight;
    const prog = totalGoal > 0 ? (first.weight - final.weight) / totalGoal : 0;
    const sA = prog >= 0.4 ? 25 : prog >= 0.2 ? 22 : prog >= 0.1 ? 18 : prog >= 0.05 ? 13 : prog >= 0.02 ? 8 : 4;

    // B. 體組成質量 (35分)
    const fatPctDrop = first.bodyFatPct - final.bodyFatPct;
    const sB_fat = fatPctDrop >= 2 ? 15 : fatPctDrop >= 1 ? 10 : fatPctDrop >= 0.5 ? 5 : 0;
    const mc = final.muscle - first.muscle;
    const sB_muscle = mc > 1 ? 20 : mc >= -0.5 ? 16 : mc >= -1 ? 10 : mc >= -2 ? 5 : 0;

    // C. 趨勢穩定性 (25分) — 每週體重或體脂率有改善的比例
    let goodWeeks = 0;
    for (let i = 1; i < data.length; i++) {
      if (data[i].weight < data[i - 1].weight || data[i].bodyFatPct < data[i - 1].bodyFatPct) goodWeeks++;
    }
    const sC = data.length > 1 ? Math.round((goodWeeks / (data.length - 1)) * 25) : 12;

    // D. 健康指標 (15分)
    const vDrop = first.visceralFat - final.visceralFat;
    const sD_v = Math.min(10, Math.max(0, vDrop) * 4);
    const sD_w = (final.waterPct - first.waterPct) > 0 ? 5 : (final.waterPct - first.waterPct) >= -0.5 ? 2 : 0;

    return Math.min(99, sA + sB_fat + sB_muscle + sC + sD_v + sD_w);
  })();

  // ── OAuth 狀態 ──────────────────────────────────────────────
  const [clientId, setClientId] = useStateAI(
    () => window.APP_CONFIG?.googleClientId || localStorage.getItem(GOOGLE_CLIENT_ID_STORAGE) || ""
  );
  const [clientIdInput, setClientIdInput] = useStateAI("");
  const [showClientIdForm, setShowClientIdForm] = useStateAI(
    () => !(window.APP_CONFIG?.googleClientId || localStorage.getItem(GOOGLE_CLIENT_ID_STORAGE))
  );
  // access token 放 sessionStorage：重整不流失，關分頁才清掉
  const [accessToken, setAccessToken] = useStateAI(
    () => sessionStorage.getItem("gemini-access-token") || ""
  );
  const [tokenExpiry, setTokenExpiry] = useStateAI(
    () => parseInt(sessionStorage.getItem("gemini-token-expiry") || "0", 10)
  );
  const [gisReady, setGisReady] = useStateAI(
    () => !!(window.google?.accounts?.oauth2)
  );
  const tokenClientRef = useRefAI(null);

  const isAuthenticated = !!(accessToken && Date.now() < tokenExpiry - 30000);

  // GIS 腳本非同步載入，輪詢直到就緒
  useEffectAI(() => {
    if (gisReady) return;
    const t = setInterval(() => {
      if (window.google?.accounts?.oauth2) { setGisReady(true); clearInterval(t); }
    }, 300);
    return () => clearInterval(t);
  }, [gisReady]);

  // clientId 或 GIS 就緒後初始化 tokenClient
  useEffectAI(() => {
    if (!gisReady || !clientId) return;
    tokenClientRef.current = window.google.accounts.oauth2.initTokenClient({
      client_id: clientId,
      scope: GEMINI_SCOPE,
      callback: (resp) => {
        if (resp.error) return;
        const expiry = Date.now() + resp.expires_in * 1000;
        setAccessToken(resp.access_token);
        setTokenExpiry(expiry);
        sessionStorage.setItem("gemini-access-token", resp.access_token);
        sessionStorage.setItem("gemini-token-expiry", String(expiry));
      },
    });
  }, [gisReady, clientId]);

  const requestToken = () => tokenClientRef.current?.requestAccessToken({ prompt: "" });
  const forceLogin  = () => tokenClientRef.current?.requestAccessToken({ prompt: "select_account" });

  const signOut = () => {
    if (accessToken) window.google?.accounts?.oauth2?.revoke(accessToken, () => {});
    setAccessToken(""); setTokenExpiry(0);
    sessionStorage.removeItem("gemini-access-token");
    sessionStorage.removeItem("gemini-token-expiry");
  };

  const saveClientId = () => {
    const id = clientIdInput.trim();
    if (!id) return;
    localStorage.setItem(GOOGLE_CLIENT_ID_STORAGE, id);
    setClientId(id); setShowClientIdForm(false); setClientIdInput("");
  };

  const [modelId, setModelId] = useStateAI(
    () => localStorage.getItem(GEMINI_MODEL_STORAGE) || "gemini-2.5-flash"
  );
  const changeModel = (id) => { setModelId(id); localStorage.setItem(GEMINI_MODEL_STORAGE, id); };

  const [memory, setMemory] = useStateAI(
    () => localStorage.getItem(GEMINI_MEMORY_STORAGE) || ""
  );
  const [showMemory, setShowMemory] = useStateAI(false);
  const [memoryDraft, setMemoryDraft] = useStateAI("");
  const openMemory  = () => { setMemoryDraft(memory); setShowMemory(true); };
  const saveMemory  = () => { setMemory(memoryDraft); localStorage.setItem(GEMINI_MEMORY_STORAGE, memoryDraft); setShowMemory(false); };
  const closeMemory = () => setShowMemory(false);

  // ── 週分析 ──────────────────────────────────────────────────
  const [weeklyAnalysis, setWeeklyAnalysis] = useStateAI(() => {
    const cached = localStorage.getItem(WEEKLY_ANALYSIS_PREFIX + final.date);
    return isValidAnalysisJSON(cached) ? cached : "";
  });
  const [analysisLoading, setAnalysisLoading] = useStateAI(false);

  const runWeeklyAnalysis = async (token, mId) => {
    if (!token || analysisLoading) return;
    setAnalysisLoading(true);
    const prev = data.length >= 2 ? data[data.length - 2] : null;
    const curr = final;
    const fmt = (v, u = "") => `${v}${u}`;
    const diff = prev
      ? [
          `體重：${fmt(prev.weight, "kg")} → ${fmt(curr.weight, "kg")}（${curr.weight > prev.weight ? "+" : ""}${(curr.weight - prev.weight).toFixed(1)} kg）`,
          `體脂率：${fmt(prev.bodyFatPct, "%")} → ${fmt(curr.bodyFatPct, "%")}（${curr.bodyFatPct > prev.bodyFatPct ? "+" : ""}${(curr.bodyFatPct - prev.bodyFatPct).toFixed(1)}%）`,
          `肌肉量：${fmt(prev.muscle, "kg")} → ${fmt(curr.muscle, "kg")}（${curr.muscle > prev.muscle ? "+" : ""}${(curr.muscle - prev.muscle).toFixed(1)} kg）`,
          `內臟脂肪：${prev.visceralFat} → ${curr.visceralFat}`,
          `體水分率：${fmt(prev.waterPct, "%")} → ${fmt(curr.waterPct, "%")}`,
          `BMR：${prev.bmr} → ${curr.bmr} kcal`,
        ].join("\n")
      : `起始量測：體重 ${curr.weight} kg、體脂率 ${curr.bodyFatPct}%、肌肉量 ${curr.muscle} kg（這是第一次紀錄）`;

    const sysPrompt = `你是溫暖、專業的健康教練，使用繁體中文。根據使用者本週數據分析，回傳純 JSON（不要加 markdown code block），格式：
{"diet":"下週飲食建議，60-80字，具體熱量與蛋白質目標","exercise":"活動建議，60-80字，針對當前運動習慣","body":"身體訊號觀察，60-80字，解釋本週數據變化的意義","motivation":"心理與動機，60-80字，肯定進展並鼓勵"}
只回傳 JSON，不含任何其他文字。${memory.trim() ? `\n\n使用者補充資訊（務必遵守）：\n${memory.trim()}` : ""}`;

    const userMsg = `使用者：${profile.age} 歲${profile.gender}，身高 ${profile.height} cm，目標 ${profile.goalWeight} kg（${profile.goalDate}）\n\n第 ${curr.week} 週（${curr.date}）數據：\n${diff}`;

    try {
      const result = await callGemini(token, mId, sysPrompt, [{ role: "ai", text: "" }], userMsg);
      localStorage.setItem(WEEKLY_ANALYSIS_PREFIX + curr.date, result);
      setWeeklyAnalysis(result);
    } catch (e) {
      console.warn("Weekly analysis error:", e.message);
    } finally {
      setAnalysisLoading(false);
    }
  };

  // 當資料更新（新增量測）時重新檢查快取
  useEffectAI(() => {
    const cached = localStorage.getItem(WEEKLY_ANALYSIS_PREFIX + final.date);
    if (isValidAnalysisJSON(cached)) { setWeeklyAnalysis(cached); return; }
    if (cached) localStorage.removeItem(WEEKLY_ANALYSIS_PREFIX + final.date); // 清除舊格式
    setWeeklyAnalysis("");
    if (isAuthenticated) runWeeklyAnalysis(accessToken, modelId);
  }, [final.date]);

  // 登入後如果還沒有有效分析則自動觸發
  useEffectAI(() => {
    if (!isAuthenticated || analysisLoading) return;
    const cached = localStorage.getItem(WEEKLY_ANALYSIS_PREFIX + final.date);
    if (!isValidAnalysisJSON(cached)) {
      if (cached) localStorage.removeItem(WEEKLY_ANALYSIS_PREFIX + final.date);
      runWeeklyAnalysis(accessToken, modelId);
    }
  }, [isAuthenticated]);

  const refreshAnalysis = () => {
    localStorage.removeItem(WEEKLY_ANALYSIS_PREFIX + final.date);
    setWeeklyAnalysis("");
    runWeeklyAnalysis(accessToken, modelId);
  };

  let aiCards = null;
  if (weeklyAnalysis) {
    try {
      const m = weeklyAnalysis.match(/\{[\s\S]*\}/);
      if (m) aiCards = JSON.parse(m[0]);
    } catch (e) {}
  }

  const suggestions = [
    {
      icon: "🍱",
      title: "下週飲食方向",
      body: aiCards?.diet || `BMR 約 ${final.bmr} kcal，建議每日攝取 ${Math.round(final.bmr * 0.83)}–${Math.round(final.bmr * 0.9)} kcal，蛋白質目標 ${Math.round(final.weight * 1.3)}–${Math.round(final.weight * 1.5)} g（每公斤體重 1.3–1.5 g）以保留肌肉。多吃蒸煮、燉、烤的瘦肉與魚，避開精製澱粉與含糖飲料。`,
    },
    {
      icon: "🚶",
      title: `活動建議（${profile.exerciseHabit}）`,
      body: aiCards?.exercise || `從每天 30 分鐘快走起步，週末加入 1–2 次徒手肌力（深蹲、橋式、伏地挺身）。內臟脂肪指數 ${final.visceralFat} 偏高，有氧 + 大肌群訓練是最有效的組合。`,
    },
    {
      icon: "🌙",
      title: "身體訊號觀察",
      body: aiCards?.body || `體重短暫回升但體脂與肌肉同步改善，是水分與肌肉修復的正常波動，不要被體重秤左右心情。持續 ${data.length} 週的紀錄本身就是很好的習慣。`,
    },
    {
      icon: "💛",
      title: "心理與動機",
      body: aiCards?.motivation || `${data.length} 週減 ${totalLost.toFixed(1)} kg 對 ${first.weight} kg 的起點來說是健康速度，相當於每週 ${((totalLost / first.weight) * 100 / data.length).toFixed(2)}%。請把焦點放在「體脂率從 ${first.bodyFatPct}% → ${final.bodyFatPct}%」這個更有意義的指標，這代表掉的是脂肪而非肌肉。`,
    },
  ];

  // ── 聊天狀態 ────────────────────────────────────────────────
  const [messages, setMessages] = useStateAI([]);
  const [input, setInput] = useStateAI("");
  const [busy, setBusy] = useStateAI(false);
  const chatRef = useRefAI(null);

  useEffectAI(() => {
    if (chatRef.current) chatRef.current.scrollTop = chatRef.current.scrollHeight;
  }, [messages, busy]);

  const send = async (text) => {
    const trimmed = text.trim();
    if (!trimmed || busy || !isAuthenticated) return;

    const historySnapshot = messages;
    setMessages(m => [...m, { role: "user", text: trimmed }]);
    setInput(""); setBusy(true);

    const dataSummary = data.map(d =>
      `第${d.week}週(${d.date}) 體重${d.weight}kg 體脂${d.bodyFatPct}% 肌肉${d.muscle}kg BMI${d.bmi} BMR${d.bmr} 內臟脂肪${d.visceralFat}`
    ).join("\n");

    const systemText = `你是一位溫暖、專業的健康教練，使用繁體中文回覆。語氣親切，回答具體可執行，約 200–350 字，分析原因後給出 2–3 個實際建議。

使用者資料：
- ${profile.age} 歲${profile.gender}，身高 ${profile.height} cm
- 起始體重 ${first.weight} kg（${first.date}），目標 ${profile.goalWeight} kg（${profile.goalDate}）
- 目前 ${final.weight} kg、體脂率 ${final.bodyFatPct}%、肌肉量 ${final.muscle} kg、BMR ${final.bmr} kcal、內臟脂肪指數 ${final.visceralFat}
- 運動習慣：${profile.exerciseHabit}

${data.length} 週數據：
${dataSummary}${memory.trim() ? `\n\n使用者補充記憶（請務必遵守）：\n${memory.trim()}` : ""}`;

    const contextHistory = historySnapshot.length > MAX_HIST
      ? historySnapshot.slice(-MAX_HIST)
      : historySnapshot;

    try {
      const reply = await callGemini(accessToken, modelId, systemText, contextHistory, trimmed);
      setMessages(m => [...m, { role: "ai", text: reply }]);
    } catch (e) {
      if (e.message === "TOKEN_EXPIRED") {
        setMessages(m => [...m, { role: "ai", text: "⚠️ 登入已逾期，請點「重新登入」。" }]);
        signOut();
      } else {
        setMessages(m => [...m, { role: "ai", text: `呼叫 Gemini 時出錯：${e.message}` }]);
      }
    } finally {
      setBusy(false);
    }
  };

  const suggested = [
    "幫我規劃下週菜單",
    "我該怎麼開始運動？",
    "預測我什麼時候能達標？",
    "為什麼這週體重沒掉？",
  ];

  return (
    <div className="grid">
      <div className="ai-card col-7">
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 6 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <span className="ai-tag"><span className="pulse"></span>12 — AI Coach · Gemini</span>
            {isAuthenticated && (
              <button
                onClick={refreshAnalysis}
                disabled={analysisLoading}
                title="重新生成 AI 建議"
                style={{
                  fontSize: 11, fontWeight: 600, letterSpacing: "0.04em",
                  color: analysisLoading ? "var(--ink-mute)" : "var(--accent-deep, #2d568a)",
                  background: "var(--accent-soft, #e8eef5)",
                  border: "1px solid var(--accent-deep, #2d568a)",
                  borderRadius: 4, padding: "3px 9px",
                  cursor: analysisLoading ? "default" : "pointer",
                  opacity: analysisLoading ? 0.6 : 1,
                }}>
                {analysisLoading ? "分析中…" : "↺ 刷新"}
              </button>
            )}
          </div>
          <div style={{ textAlign: "right" }}>
            <div style={{ fontSize: 11, color: "var(--ink-mute)", letterSpacing: "0.18em", textTransform: "uppercase", fontWeight: 600 }}>整體進度評分</div>
            <div style={{ fontFamily: "var(--font-num)", fontSize: 56, fontWeight: 300, color: "var(--accent-deep)", lineHeight: 1, marginTop: 8, letterSpacing: "-0.03em" }}>
              {score}<span style={{ fontSize: 14, color: "var(--ink-mute)", marginLeft: 6, fontFamily: "var(--font)", fontWeight: 500, letterSpacing: "0.12em" }}>/100</span>
            </div>
          </div>
        </div>
        <p className="ai-summary">
          過去 {data.length} 週你減去 <span className="hl">{totalLost.toFixed(1)} kg</span>，其中 <span className="hl">{fatLost.toFixed(1)} kg 是脂肪</span>、肌肉量幾乎守住（{muscleChange >= 0 ? "+" : ""}{muscleChange.toFixed(1)} kg），內臟脂肪指數從 {first.visceralFat} → {final.visceralFat}。
          {projDateStr
            ? <>以目前速度推估，將於 <span className="hl">{projDateStr}</span> 達到 {profile.goalWeight} kg 目標
                {isBehindSchedule ? `（逾期，建議提速至每週 ${Math.abs(expectedKgPerWeek).toFixed(2)} kg）` : "（已在目標節奏內）"}。</>
            : <>目前速度尚不足以推估達標日，建議加快減重步調。</>
          }
        </p>

        <ul className="ai-list">
          {suggestions.map((s, i) => (
            <li key={i}>
              <div className="ai-bullet">{s.icon}</div>
              <div>
                <div style={{ fontWeight: 600, marginBottom: 2 }}>{s.title}</div>
                {analysisLoading
                  ? <span className="typing-dots" style={{ display: "inline-flex", gap: 3 }}><span></span><span></span><span></span></span>
                  : <div>{s.body}</div>
                }
              </div>
            </li>
          ))}
        </ul>
      </div>

      <div className="card col-5" style={{ display: "flex", flexDirection: "column" }}>
        <div className="section-head" style={{ marginTop: 0 }}>
          <div>
            <div className="eyebrow" data-num="13 —">即時對話</div>
            <h2 className="h2" style={{ whiteSpace: "nowrap" }}>問問你的 AI 教練</h2>
          </div>
        </div>

        {showMemory && (
          <div style={{ background: "var(--surface-2, #f7f5f0)", borderRadius: 8, padding: "14px 16px", marginBottom: 12, border: "1px solid var(--line)" }}>
            <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: "0.12em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 8 }}>AI 記憶</div>
            <div style={{ fontSize: 12, color: "var(--ink-3)", marginBottom: 8, lineHeight: 1.6 }}>
              寫下每次都要讓 AI 記住的事，例如飲食禁忌、身體限制、偏好。
            </div>
            <textarea
              value={memoryDraft}
              onChange={e => setMemoryDraft(e.target.value)}
              placeholder={"例如：\n• 膝蓋舊傷，不能跑步\n• 不吃海鮮\n• 每天鈉攝取 < 1500 mg"}
              style={{
                width: "100%", minHeight: 100, resize: "vertical", boxSizing: "border-box",
                border: "1px solid var(--line-strong)", borderRadius: 6, padding: "8px 10px",
                fontSize: 13, fontFamily: "var(--font)", lineHeight: 1.7,
                background: "white", color: "var(--ink)", outline: "none",
              }}
            />
            <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 10 }}>
              <button onClick={closeMemory} style={{ fontSize: 12, padding: "5px 14px", border: "1px solid var(--line-strong)", borderRadius: 5, background: "transparent", cursor: "pointer", color: "var(--ink-mute)" }}>取消</button>
              <button onClick={saveMemory} style={{ fontSize: 12, padding: "5px 14px", border: "none", borderRadius: 5, background: "var(--accent-deep, #2d568a)", color: "white", cursor: "pointer", fontWeight: 600 }}>儲存</button>
            </div>
          </div>
        )}

        {/* 步驟一：輸入 OAuth Client ID */}
        {showClientIdForm && (
          <div style={{ padding: "16px 0", display: "flex", flexDirection: "column", gap: 12 }}>
            <div style={{ fontSize: 12, color: "var(--ink-3)", lineHeight: 1.7 }}>
              需要先在 Google Cloud Console 建立 OAuth 用戶端 ID。<br />
              <strong>設定步驟：</strong><br />
              1. <a href="https://console.cloud.google.com/apis/credentials" target="_blank" rel="noreferrer" style={{ color: "var(--accent)" }}>Cloud Console → 憑證</a> → 建立憑證 → OAuth 用戶端 ID<br />
              2. 應用程式類型選「<strong>網頁應用程式</strong>」<br />
              3. 已授權的 JavaScript 來源加入 <code style={{ background: "var(--accent-soft)", padding: "1px 4px", borderRadius: 3 }}>http://localhost:7788</code><br />
              4. 把用戶端 ID（格式 <code style={{ background: "var(--accent-soft)", padding: "1px 4px", borderRadius: 3 }}>…apps.googleusercontent.com</code>）貼到下方
            </div>
            <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
              <input
                placeholder="123456789-xxx.apps.googleusercontent.com"
                value={clientIdInput}
                onChange={e => setClientIdInput(e.target.value)}
                onKeyDown={e => { if (e.key === "Enter") saveClientId(); }}
                style={{ flex: 1, border: "none", borderBottom: "1px solid var(--line-strong)", padding: "8px 0", background: "transparent", fontSize: 13, fontFamily: "var(--font-mono)", color: "var(--ink)", outline: "none" }}
              />
              <button className="btn btn-primary" onClick={saveClientId} disabled={!clientIdInput.trim()}>儲存</button>
            </div>
            <div style={{ fontSize: 11, color: "var(--ink-mute)" }}>
              Client ID 只存在本機 localStorage，不會上傳。
            </div>
          </div>
        )}

        {/* 步驟二：已有 Client ID，但尚未登入 */}
        {!showClientIdForm && !isAuthenticated && (
          <div style={{ display: "flex", flexDirection: "column", gap: 14, padding: "20px 0", flex: 1, justifyContent: "center", alignItems: "center" }}>
            {!gisReady ? (
              <div style={{ fontSize: 13, color: "var(--ink-mute)" }}>載入 Google 登入中…</div>
            ) : (
              <>
                <button
                  onClick={forceLogin}
                  style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 20px", border: "1px solid var(--line-strong)", borderRadius: 6, background: "white", fontSize: 14, color: "var(--ink-2)", cursor: "pointer", fontFamily: "var(--font)", fontWeight: 500, boxShadow: "0 1px 3px rgba(0,0,0,0.08)" }}>
                  <svg width="18" height="18" viewBox="0 0 18 18">
                    <path fill="#4285F4" d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z"/>
                    <path fill="#34A853" d="M9 18c2.43 0 4.467-.806 5.956-2.184l-2.908-2.258c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 0 0 9 18z"/>
                    <path fill="#FBBC05" d="M3.964 10.707A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.707V4.961H.957A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.957 4.039l3.007-2.332z"/>
                    <path fill="#EA4335" d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.961L3.964 7.293C4.672 5.163 6.656 3.58 9 3.58z"/>
                  </svg>
                  以 Google 帳號登入
                </button>
                <button onClick={() => setShowClientIdForm(true)} style={{ fontSize: 11, color: "var(--ink-mute)", background: "none", border: "none", cursor: "pointer", textDecoration: "underline" }}>
                  更換 Client ID
                </button>
              </>
            )}
          </div>
        )}

        {/* 步驟三：已登入，可聊天 */}
        {!showClientIdForm && isAuthenticated && (
          <>
            <div className="chat" ref={chatRef}>
              {messages.map((m, i) => {
                const isCutoff = messages.length > MAX_HIST && i === messages.length - MAX_HIST;
                return (
                  <React.Fragment key={i}>
                    {isCutoff && (
                      <div style={{ textAlign: "center", fontSize: 10, color: "var(--ink-mute)", padding: "6px 0", letterSpacing: "0.08em" }}>
                        · · · 較早的對話已不在 AI 記憶範圍內 · · ·
                      </div>
                    )}
                    <div className={`chat-msg ${m.role}`}>
                      {m.role === "ai"
                        ? <span dangerouslySetInnerHTML={{ __html: renderMarkdown(m.text) }} />
                        : m.text
                      }
                    </div>
                  </React.Fragment>
                );
              })}
              {busy && (
                <div className="chat-msg ai">
                  <span className="typing-dots"><span></span><span></span><span></span></span>
                </div>
              )}
            </div>
            <div style={{ marginTop: 10 }}>
              {suggested.map(s => (
                <button key={s} className="chat-suggestion" onClick={() => send(s)} disabled={busy}>{s}</button>
              ))}
            </div>
            <div className="chat-input">
              <input
                placeholder="輸入你想問的事，例如：明天可以吃水餃嗎？"
                value={input}
                onChange={e => setInput(e.target.value)}
                onKeyDown={e => { if (e.key === "Enter") send(input); }}
                disabled={busy}
              />
              <button onClick={() => send(input)} disabled={busy || !input.trim()}>送出</button>
            </div>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginTop: 8 }}>
              <div style={{ display: "flex", gap: 6, alignItems: "center" }}>
                <button
                  onClick={openMemory}
                  title="編輯 AI 記憶"
                  style={{
                    fontSize: 11, fontWeight: 600, letterSpacing: "0.04em",
                    padding: "3px 9px", borderRadius: 4, border: "1px solid var(--line-strong)",
                    cursor: "pointer", background: memory.trim() ? "var(--accent-soft, #e8eef5)" : "transparent",
                    color: memory.trim() ? "var(--accent-deep, #2d568a)" : "var(--ink-mute)",
                  }}>
                  {memory.trim() ? "記憶 ✦" : "記憶"}
                </button>
                <div style={{ display: "flex", gap: 2, background: "var(--surface-2, #f2f0eb)", borderRadius: 6, padding: 2 }}>
                  {GEMINI_MODELS.map(m => (
                    <button
                      key={m.id}
                      title={m.desc}
                      onClick={() => changeModel(m.id)}
                      style={{
                        fontSize: 11, fontWeight: 600, letterSpacing: "0.04em",
                        padding: "3px 9px", borderRadius: 4, border: "none", cursor: "pointer",
                        background: modelId === m.id ? "var(--accent-deep, #2d568a)" : "transparent",
                        color: modelId === m.id ? "white" : "var(--ink-mute)",
                        transition: "background 0.15s, color 0.15s",
                      }}>
                      {m.label}
                    </button>
                  ))}
                </div>
              </div>
              <button onClick={signOut} style={{ fontSize: 11, color: "var(--ink-mute)", background: "none", border: "none", cursor: "pointer", padding: 0, letterSpacing: "0.08em", textDecoration: "underline" }}>
                登出
              </button>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

const FIELD_DEFS = [
  { key: "weight", label: "體重", unit: "kg", step: "0.1" },
  { key: "bodyFatPct", label: "體脂率", unit: "%", step: "0.1" },
  { key: "muscle", label: "肌肉量", unit: "kg", step: "0.1" },
  { key: "visceralFat", label: "內臟脂肪指數", unit: "", step: "1" },
  { key: "waterPct", label: "體水分率", unit: "%", step: "0.1" },
  { key: "bmi", label: "BMI", unit: "", step: "0.1" },
  { key: "bmr", label: "基礎代謝", unit: "kcal", step: "1" },
  { key: "bone", label: "骨量", unit: "kg", step: "0.1" },
];

const PART_KEYS = ["trunk", "leftArm", "rightArm", "leftLeg", "rightLeg"];
const PART_LABELS = { trunk: "軀幹", leftArm: "左手", rightArm: "右手", leftLeg: "左腳", rightLeg: "右腳" };
const PART_METRICS = [
  { metric: "muscle", label: "肌肉量", unit: "kg", step: "0.1" },
  { metric: "fat", label: "脂肪量", unit: "kg", step: "0.1" },
  { metric: "fatPct", label: "脂肪率", unit: "%", step: "0.1" },
];

const STORAGE_KEY_INPUT = "diet-diary-entries-v1";

function InputModal({ onClose, latest, onSaved }) {
  const todayStr = new Date().toISOString().slice(0, 10);
  const initial = {
    date: todayStr > latest.date ? todayStr : latest.date,
    note: "",
  };
  FIELD_DEFS.forEach(f => { initial[f.key] = latest[f.key] ?? ""; });
  PART_METRICS.forEach(m => PART_KEYS.forEach(pk => {
    initial[`p_${m.metric}_${pk}`] = latest.parts?.[m.metric]?.[pk] ?? "";
  }));
  const [form, setForm] = useStateAI(initial);
  const [error, setError] = useStateAI("");
  const upd = (k) => (e) => setForm({ ...form, [k]: e.target.value });

  const submit = (e) => {
    e.preventDefault();
    if (!form.date) { setError("請選擇日期"); return; }
    if (form.date <= latest.date) {
      setError(`日期需晚於上次量測（${latest.date}）`);
      return;
    }
    for (const f of FIELD_DEFS) {
      const v = form[f.key];
      if (v === "" || isNaN(parseFloat(v))) {
        setError(`請輸入${f.label}`);
        return;
      }
    }
    for (const m of PART_METRICS) {
      for (const pk of PART_KEYS) {
        const v = form[`p_${m.metric}_${pk}`];
        if (v === "" || isNaN(parseFloat(v))) {
          setError(`請輸入${PART_LABELS[pk]} ${m.label}`);
          return;
        }
      }
    }

    const num = (k) => parseFloat(form[k]);
    const weight = num("weight");
    const bodyFatPct = num("bodyFatPct");
    const waterPct = num("waterPct");
    const bodyFat = +(weight * bodyFatPct / 100).toFixed(1);
    const leanMass = +(weight - bodyFat).toFixed(1);
    const water = +(weight * waterPct / 100).toFixed(1);
    const wdNames = ["日", "一", "二", "三", "四", "五", "六"];
    const dt = new Date(form.date + "T00:00:00");
    const now = new Date();
    const hhmm = `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
    const weekday = `週${wdNames[dt.getDay()]} ${hhmm}`;

    const parts = { muscle: {}, fat: {}, fatPct: {} };
    PART_METRICS.forEach(m => PART_KEYS.forEach(pk => {
      parts[m.metric][pk] = +parseFloat(form[`p_${m.metric}_${pk}`]).toFixed(1);
    }));

    const entry = {
      week: 0, // 重排後填入
      date: form.date,
      weekday,
      weight,
      bodyFatPct,
      bodyFat,
      leanMass,
      muscle: num("muscle"),
      waterPct,
      water,
      bmi: num("bmi"),
      bone: num("bone"),
      visceralFat: parseInt(num("visceralFat"), 10),
      bmr: parseInt(num("bmr"), 10),
      note: form.note.trim() || `第 ${(latest.week || 0) + 1} 次量測`,
      parts,
      _saved: true,
    };

    try {
      const saved = JSON.parse(localStorage.getItem(STORAGE_KEY_INPUT) || "[]");
      saved.push(entry);
      localStorage.setItem(STORAGE_KEY_INPUT, JSON.stringify(saved));
    } catch (err) {
      setError("儲存失敗：" + err.message);
      return;
    }

    window.WEEKLY_DATA = [...window.WEEKLY_DATA, entry]
      .sort((a, b) => a.date.localeCompare(b.date));
    window.WEEKLY_DATA.forEach((d, i) => { d.week = i + 1; });
    if (window.rebuildMilestones) window.rebuildMilestones();
    const persisted = window.WEEKLY_DATA.find(d => d.date === entry.date) || entry;

    onSaved && onSaved(persisted);
    onClose();

    const toast = document.createElement("div");
    toast.textContent = "✦ 已儲存第 " + persisted.week + " 週量測";
    toast.style.cssText = "position:fixed;bottom:32px;left:50%;transform:translateX(-50%);background:#2d568a;color:white;padding:12px 22px;border-radius:999px;font-size:14px;z-index:60;box-shadow:0 8px 24px rgba(20,22,26,0.18);";
    document.body.appendChild(toast);
    setTimeout(() => toast.remove(), 2500);
  };

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <form className="modal" onClick={e => e.stopPropagation()} onSubmit={submit} style={{ maxWidth: 560 }}>
        <div className="eyebrow" data-num="14 —">新增量測</div>
        <h2 className="h2" style={{ marginBottom: 18 }}>記錄今天的數據</h2>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "14px 20px" }}>
          <div className="field" style={{ marginBottom: 0 }}>
            <label>量測日期</label>
            <input type="date" value={form.date} min={latest.date} onChange={upd("date")} />
          </div>
          <div className="field" style={{ marginBottom: 0 }}>
            <label>備註（選填）</label>
            <input type="text" value={form.note} onChange={upd("note")} placeholder="例如：飯後量測" />
          </div>
          {FIELD_DEFS.map((f, i) => (
            <div className="field" key={f.key} style={{ marginBottom: 0 }}>
              <label>{f.label}{f.unit ? ` (${f.unit})` : ""}</label>
              <input type="number" step={f.step} value={form[f.key]} onChange={upd(f.key)} autoFocus={i === 0} />
            </div>
          ))}
        </div>
        <div style={{ marginTop: 22, marginBottom: 12, fontSize: 13, fontWeight: 600, letterSpacing: "0.04em", color: "var(--ink)" }}>各部位數據</div>
        <div style={{ display: "grid", gridTemplateColumns: "auto 1fr 1fr 1fr", gap: "10px 14px", alignItems: "center" }}>
          <div></div>
          {PART_METRICS.map(m => (
            <div key={m.metric} style={{ fontSize: 11, color: "var(--ink-mute)", letterSpacing: "0.04em", textAlign: "center" }}>
              {m.label} ({m.unit})
            </div>
          ))}
          {PART_KEYS.map(pk => (
            <React.Fragment key={pk}>
              <div style={{ fontSize: 12, color: "var(--ink-mute)", whiteSpace: "nowrap" }}>{PART_LABELS[pk]}</div>
              {PART_METRICS.map(m => (
                <input
                  key={`${m.metric}_${pk}`}
                  type="number"
                  step={m.step}
                  value={form[`p_${m.metric}_${pk}`]}
                  onChange={upd(`p_${m.metric}_${pk}`)}
                  style={{ width: "100%" }}
                />
              ))}
            </React.Fragment>
          ))}
        </div>
        {error && (
          <div style={{ marginTop: 14, color: "var(--warn)", fontSize: 12, letterSpacing: "0.04em" }}>{error}</div>
        )}
        <div style={{ marginTop: 14, fontSize: 11, color: "var(--ink-mute)", letterSpacing: "0.04em" }}>
          資料保存在本機瀏覽器（localStorage），不會上傳。各部位欄位已預填上次數據，請依本週量測修改。
        </div>
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 22 }}>
          <button type="button" className="btn" onClick={onClose}>取消</button>
          <button type="submit" className="btn btn-primary">儲存</button>
        </div>
      </form>
    </div>
  );
}

window.AIPanel = AIPanel;
window.InputModal = InputModal;
