import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
import {
  API_BASE,
  authClientFetchHeaders,
  buildAuthCallbackUrl,
  clearAuthClientContext,
  fetchClientVisitMeta,
  formatAuthBanMessage,
  getOAuthReturnTo,
  normalizeSelfServiceAccountInput,
  persistAuthClientFromFlow,
  randomSelfServiceAccount
} from "../config";
import UserPortalAuthSection from "./userPortal/UserPortalAuthSection";
import UserPortalProfileSection from "./userPortal/UserPortalProfileSection";
import OAuthConsentScreen from "./OAuthConsentScreen";

export default function UserPortal({
  onReady,
  authFlow,
  onPreviewImage,
  oauthStandalone = false,
  onGuestAuthLayoutChange
}) {
  const [account, setAccount] = useState("");
  const [password, setPassword] = useState("");
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  const [mode, setMode] = useState("login");

  const [registerForm, setRegisterForm] = useState({
    account: "", password: "", username: "", email: "", code: "", inviteCode: ""
  });
  const [registrationRequireInvite, setRegistrationRequireInvite] = useState(false);
  const [inviteRegisterReward, setInviteRegisterReward] = useState(10);
  const [registrationAccountMin, setRegistrationAccountMin] = useState(3);
  const [registrationAccountMax, setRegistrationAccountMax] = useState(32);
  const [registerSent, setRegisterSent] = useState(false);
  const [registerExpiresAt, setRegisterExpiresAt] = useState("");
  const [registerLoading, setRegisterLoading] = useState(false);
  const [registerError, setRegisterError] = useState("");
  const [registerMessage, setRegisterMessage] = useState("");

  const [resetForm, setResetForm] = useState({
    account: "", email: "", code: "", newPassword: ""
  });
  const [resetSent, setResetSent] = useState(false);
  const [resetExpiresAt, setResetExpiresAt] = useState("");
  const [resetLoading, setResetLoading] = useState(false);
  const [resetError, setResetError] = useState("");
  const [resetMessage, setResetMessage] = useState("");

  const [secondaryForm, setSecondaryForm] = useState({ email: "", code: "" });
  const [secondarySent, setSecondarySent] = useState(false);
  const [secondaryExpiresAt, setSecondaryExpiresAt] = useState("");
  const [secondaryLoading, setSecondaryLoading] = useState(false);
  const [secondaryError, setSecondaryError] = useState("");
  const [secondaryMessage, setSecondaryMessage] = useState("");

  const [profileForm, setProfileForm] = useState({
    username: "", phone: "", avatarUrl: "", websiteUrl: "", bio: "", password: ""
  });
  const [profileLoading, setProfileLoading] = useState(false);
  const [profileError, setProfileError] = useState("");
  const [profileMessage, setProfileMessage] = useState("");
  const [profileEditorOpen, setProfileEditorOpen] = useState(false);
  const [profileEditorFocus, setProfileEditorFocus] = useState("");
  const [secondaryEditorOpen, setSecondaryEditorOpen] = useState(false);

  const [checkInReward, setCheckInReward] = useState(1);
  const [checkInToday, setCheckInToday] = useState(false);
  const [checkInLoading, setCheckInLoading] = useState(false);
  const [checkInError, setCheckInError] = useState("");
  const [checkInMessage, setCheckInMessage] = useState("");

  const [githubOAuthEnabled, setGithubOAuthEnabled] = useState(false);
  const [giteaOAuthEnabled, setGiteaOAuthEnabled] = useState(false);
  const [googleOAuthEnabled, setGoogleOAuthEnabled] = useState(false);
  const [microsoftOAuthEnabled, setMicrosoftOAuthEnabled] = useState(false);
  const [linuxdoOAuthEnabled, setLinuxdoOAuthEnabled] = useState(false);
  const [oauthLogos, setOauthLogos] = useState({
    github: "", gitea: "", google: "", microsoft: "", linuxdo: ""
  });

  const [turnstileEnabled, setTurnstileEnabled] = useState(false);
  const [turnstileSiteKey, setTurnstileSiteKey] = useState("");
  const [loginTurnstileToken, setLoginTurnstileToken] = useState("");
  const [registerTurnstileToken, setRegisterTurnstileToken] = useState("");
  const loginTurnstileRef = useRef(null);
  const registerTurnstileRef = useRef(null);

  const isAuthFlow = Boolean(authFlow?.redirectUri);

  const redirectToAuthCallback = (tokenValue, userData, expiresAt = "") => {
    if (!isAuthFlow || !authFlow?.redirectUri || !tokenValue) return;
    const callbackUrl = buildAuthCallbackUrl(authFlow.redirectUri, {
      token: tokenValue,
      account: userData?.account || "",
      username: userData?.username || "",
      expiresAt,
      state: authFlow.state || ""
    });
    window.location.replace(callbackUrl);
  };

  const clearTokenAndUser = () => {
    localStorage.removeItem("sproutgate_token");
    localStorage.removeItem("sproutgate_token_expires_at");
    setUser(null);
    setProfileForm({
      username: "", phone: "", avatarUrl: "", websiteUrl: "", bio: "", password: ""
    });
  };

  const bearerHeaders = (tokenValue) => ({
    ...authClientFetchHeaders(),
    Authorization: `Bearer ${tokenValue}`
  });

  const syncCurrentUser = async (tokenValue) => {
    const baseHeaders = bearerHeaders(tokenValue);
    const res = await fetch(`${API_BASE}/api/auth/me`, { headers: baseHeaders });
    const data = await res.json();
    if (!res.ok) {
      if (res.status === 403) {
        clearTokenAndUser();
        const msg = formatAuthBanMessage(data);
        setError(msg);
        throw new Error(msg);
      }
      throw new Error(data.error || "加载用户失败");
    }
    if (data.user) setUser(data.user);
    const checkIn = data.checkIn || {};
    setCheckInReward(Number(checkIn.rewardCoins) || 1);
    setCheckInToday(Boolean(checkIn.checkedInToday));

    fetchClientVisitMeta()
      .then((meta) => {
        if (!meta.ip && !meta.displayLocation) return null;
        const h = { ...baseHeaders };
        if (meta.ip) h["X-Visit-Ip"] = meta.ip;
        if (meta.displayLocation) h["X-Visit-Location"] = meta.displayLocation;
        return fetch(`${API_BASE}/api/auth/me`, { headers: h });
      })
      .then(async (r) => {
        if (!r) return null;
        const d = await r.json().catch(() => ({}));
        if (r.status === 403) {
          clearTokenAndUser();
          setError(formatAuthBanMessage(d));
          return null;
        }
        return r.ok ? d : null;
      })
      .then((d) => {
        if (!d) return;
        if (d.user) setUser(d.user);
        const c = d.checkIn;
        if (c) {
          setCheckInReward(Number(c.rewardCoins) || 1);
          setCheckInToday(Boolean(c.checkedInToday));
        }
      })
      .catch(() => {});

    return data;
  };

  useEffect(() => {
    let cancelled = false;
    const done = () => { if (!cancelled && onReady) onReady(); };
    const hash = (window.location.hash || "").replace(/^#/, "");
    if (hash) {
      const p = new URLSearchParams(hash);
      const ot = p.get("oauth_token");
      if (ot) {
        const ex = p.get("oauth_expires_at") || "";
        localStorage.setItem("sproutgate_token", ot);
        localStorage.setItem("sproutgate_token_expires_at", ex);
        window.history.replaceState(null, "", window.location.pathname + window.location.search);
        syncCurrentUser(ot).catch(() => {}).finally(() => done());
        return () => { cancelled = true; };
      }
      if (p.get("oauth_error")) {
        let desc = p.get("oauth_error_description") || "OAuth 登录失败";
        try {
          desc = decodeURIComponent(desc.replace(/\+/g, " "));
        } catch { /* use raw */ }
        setError(desc);
        window.history.replaceState(null, "", window.location.pathname + window.location.search);
        done();
        return () => { cancelled = true; };
      }
      if (p.get("oauth_bound")) {
        window.history.replaceState(null, "", window.location.pathname + window.location.search);
        const t = localStorage.getItem("sproutgate_token");
        if (t) {
          syncCurrentUser(t).catch(() => {}).finally(() => done());
        } else {
          done();
        }
        return () => { cancelled = true; };
      }
    }
    const token = localStorage.getItem("sproutgate_token");
    if (token) {
      syncCurrentUser(token).catch(() => {}).finally(() => done());
    } else {
      done();
    }
    return () => { cancelled = true; };
  }, [onReady]);

  useEffect(() => {
    persistAuthClientFromFlow(authFlow);
  }, [authFlow]);

  useLayoutEffect(() => {
    if (!onGuestAuthLayoutChange) return;
    const invalidOAuthStandalone = Boolean(
      oauthStandalone && !(authFlow?.redirectUri || "").trim()
    );
    onGuestAuthLayoutChange(Boolean(!user && !invalidOAuthStandalone));
  }, [user, oauthStandalone, authFlow, onGuestAuthLayoutChange]);

  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const res = await fetch(`${API_BASE}/api/public/registration-policy`);
        const data = await res.json().catch(() => ({}));
        if (!cancelled && res.ok) {
          setRegistrationRequireInvite(Boolean(data.requireInviteCode));
          const ir = Number(data.inviteRegisterRewardCoins);
          if (Number.isFinite(ir) && ir >= 0) setInviteRegisterReward(ir);
          const am = Number(data.selfServiceAccountMax);
          const ami = Number(data.selfServiceAccountMin);
          if (Number.isFinite(am) && am >= 3 && am <= 32) setRegistrationAccountMax(am);
          if (Number.isFinite(ami) && ami >= 3 && ami <= 32) setRegistrationAccountMin(ami);
          setGithubOAuthEnabled(Boolean(data.githubOAuthEnabled));
          setGiteaOAuthEnabled(Boolean(data.giteaOAuthEnabled));
          setGoogleOAuthEnabled(Boolean(data.googleOAuthEnabled));
          setMicrosoftOAuthEnabled(Boolean(data.microsoftOAuthEnabled));
          setLinuxdoOAuthEnabled(Boolean(data.linuxdoOAuthEnabled));
          setOauthLogos({
            github: String(data.githubOAuthLogoUrl || "").trim(),
            gitea: String(data.giteaOAuthLogoUrl || "").trim(),
            google: String(data.googleOAuthLogoUrl || "").trim(),
            microsoft: String(data.microsoftOAuthLogoUrl || "").trim(),
            linuxdo: String(data.linuxdoOAuthLogoUrl || "").trim()
          });
          setTurnstileEnabled(Boolean(data.turnstileEnabled));
          setTurnstileSiteKey(String(data.turnstileSiteKey || "").trim());
        }
      } catch {
        /* 忽略，默认不要求邀请码 */
      }
    })();
    return () => { cancelled = true; };
  }, []);

  // 仅在外部 user 更新且资料弹窗关闭时同步表单，避免 /api/auth/me 二次请求在编辑期间把表单项覆盖回服务器值（QQ 用户会把已填写的自定义头像链接清空）
  useEffect(() => {
    if (!user || profileEditorOpen) return;
    setProfileForm({
      username: user.username || "",
      phone: user.phone || "",
      avatarUrl: user.customAvatarUrl ?? "",
      websiteUrl: user.websiteUrl || "",
      bio: user.bio || "",
      password: ""
    });
  }, [user, profileEditorOpen]);

  const handleLogin = async (event) => {
    event.preventDefault();
    setLoading(true);
    setError("");
    try {
      const loginPayload = { account, password };
      if (turnstileEnabled) loginPayload.turnstileToken = loginTurnstileToken;
      const cid = (authFlow?.clientId || "").trim();
      const cname = (authFlow?.clientName || "").trim();
      if (cid) {
        loginPayload.clientId = cid;
        if (cname) loginPayload.clientName = cname;
      } else {
        const h = authClientFetchHeaders();
        if (h["X-Auth-Client"]) {
          loginPayload.clientId = h["X-Auth-Client"];
          if (h["X-Auth-Client-Name"]) loginPayload.clientName = h["X-Auth-Client-Name"];
        }
      }
      const res = await fetch(`${API_BASE}/api/auth/login`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(loginPayload)
      });
      const data = await res.json();
      if (!res.ok) {
        throw new Error(
          res.status === 403 ? formatAuthBanMessage(data) : (data.error || "登录失败")
        );
      }
      localStorage.setItem("sproutgate_token", data.token);
      localStorage.setItem("sproutgate_token_expires_at", data.expiresAt || "");
      setUser(data.user);
      syncCurrentUser(data.token).catch(() => {});
      setAccount("");
      setPassword("");
      if (isAuthFlow && !oauthStandalone) {
        redirectToAuthCallback(data.token, data.user, data.expiresAt || "");
        return;
      }
    } catch (err) {
      setError(err.message);
      setLoginTurnstileToken("");
      loginTurnstileRef.current?.reset();
    } finally {
      setLoading(false);
    }
  };

  const handleLogout = () => {
    clearAuthClientContext();
    clearTokenAndUser();
    setProfileEditorOpen(false);
    setProfileEditorFocus("");
    setSecondaryEditorOpen(false);
    setCheckInReward(1);
    setCheckInToday(false);
    setCheckInError("");
    setCheckInMessage("");
  };

  const handleContinueAuth = () => {
    const tokenValue = localStorage.getItem("sproutgate_token") || "";
    if (!tokenValue) {
      setError("当前没有可用的登录会话，请先重新登录");
      return;
    }
    redirectToAuthCallback(
      tokenValue, user,
      localStorage.getItem("sproutgate_token_expires_at") || ""
    );
  };

  const handleSwitchAuthAccount = () => {
    handleLogout();
    setMode("login");
    setAccount("");
    setPassword("");
    setError("");
  };

  const handleRegisterChange = (field, value) => {
    if (field === "account") {
      value = normalizeSelfServiceAccountInput(value, registrationAccountMax);
    }
    setRegisterForm((prev) => ({ ...prev, [field]: value }));
  };

  const resetRegisterFlow = () => {
    setRegisterSent(false);
    setRegisterExpiresAt("");
    setRegisterForm({
      account: randomSelfServiceAccount(10),
      password: "",
      username: "",
      email: "",
      code: "",
      inviteCode: ""
    });
  };

  const handleSendCode = async (event) => {
    event.preventDefault();
    setRegisterLoading(true);
    setRegisterError("");
    setRegisterMessage("");
    if (!registerForm.account || !registerForm.password || !registerForm.email) {
      setRegisterError("请填写账户、密码和邮箱");
      setRegisterLoading(false);
      return;
    }
    if (registerForm.account.length < registrationAccountMin) {
      setRegisterError(`账户至少 ${registrationAccountMin} 位，仅小写字母与数字`);
      setRegisterLoading(false);
      return;
    }
    if (registrationRequireInvite && !String(registerForm.inviteCode || "").trim()) {
      setRegisterError("请输入邀请码");
      setRegisterLoading(false);
      return;
    }
    try {
      const payload = {
        account: registerForm.account,
        password: registerForm.password,
        username: registerForm.username,
        email: registerForm.email
      };
      if (turnstileEnabled) payload.turnstileToken = registerTurnstileToken;
      const inv = String(registerForm.inviteCode || "").trim();
      if (inv) payload.inviteCode = inv;
      const res = await fetch(`${API_BASE}/api/auth/register`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload)
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || "发送验证码失败");
      setRegisterSent(true);
      setRegisterExpiresAt(data.expiresAt || "");
      setRegisterMessage("验证码已发送，请检查邮箱");
    } catch (err) {
      setRegisterError(err.message);
      setRegisterTurnstileToken("");
      registerTurnstileRef.current?.reset();
    } finally {
      setRegisterLoading(false);
    }
  };

  const handleVerifyRegister = async (event) => {
    event.preventDefault();
    setRegisterLoading(true);
    setRegisterError("");
    setRegisterMessage("");
    if (!registerForm.account || !registerForm.code) {
      setRegisterError("请输入账户与验证码");
      setRegisterLoading(false);
      return;
    }
    try {
      const res = await fetch(`${API_BASE}/api/auth/verify-email`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ account: registerForm.account, code: registerForm.code })
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || "验证失败");
      const coins = Number(data.user?.sproutCoins);
      setRegisterMessage(
        Number.isFinite(coins) && coins > 0
          ? `注册成功，已获得 ${coins} 萌芽币，请使用账号登录`
          : "注册成功，请使用账号登录"
      );
      setRegisterSent(false);
      setRegisterForm({ account: "", password: "", username: "", email: "", code: "", inviteCode: "" });
      setMode("login");
    } catch (err) {
      setRegisterError(err.message);
    } finally {
      setRegisterLoading(false);
    }
  };

  const handleResetChange = (field, value) => {
    setResetForm((prev) => ({ ...prev, [field]: value }));
  };

  const handleSendReset = async (event) => {
    event.preventDefault();
    setResetLoading(true);
    setResetError("");
    setResetMessage("");
    if (!resetForm.account || !resetForm.email) {
      setResetError("请填写账户与邮箱");
      setResetLoading(false);
      return;
    }
    try {
      const res = await fetch(`${API_BASE}/api/auth/forgot-password`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ account: resetForm.account, email: resetForm.email })
      });
      const data = await res.json();
      if (!res.ok) {
        throw new Error(
          res.status === 403 ? formatAuthBanMessage(data) : (data.error || "发送重置邮件失败")
        );
      }
      setResetSent(true);
      setResetExpiresAt(data.expiresAt || "");
      setResetMessage("重置验证码已发送，请检查邮箱");
    } catch (err) {
      setResetError(err.message);
    } finally {
      setResetLoading(false);
    }
  };

  const handleResetPassword = async (event) => {
    event.preventDefault();
    setResetLoading(true);
    setResetError("");
    setResetMessage("");
    if (!resetForm.account || !resetForm.code || !resetForm.newPassword) {
      setResetError("请填写账户、验证码与新密码");
      setResetLoading(false);
      return;
    }
    try {
      const res = await fetch(`${API_BASE}/api/auth/reset-password`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          account: resetForm.account,
          code: resetForm.code,
          newPassword: resetForm.newPassword
        })
      });
      const data = await res.json();
      if (!res.ok) {
        throw new Error(
          res.status === 403 ? formatAuthBanMessage(data) : (data.error || "重置失败")
        );
      }
      setResetMessage("密码已重置，请使用新密码登录");
      setResetSent(false);
      setResetForm({ account: "", email: "", code: "", newPassword: "" });
      setMode("login");
    } catch (err) {
      setResetError(err.message);
    } finally {
      setResetLoading(false);
    }
  };

  const handleSecondaryChange = (field, value) => {
    setSecondaryForm((prev) => ({ ...prev, [field]: value }));
  };

  const handleSendSecondary = async (event) => {
    event.preventDefault();
    setSecondaryLoading(true);
    setSecondaryError("");
    setSecondaryMessage("");
    const token = localStorage.getItem("sproutgate_token");
    if (!token) { setSecondaryError("请先登录"); setSecondaryLoading(false); return; }
    if (!secondaryForm.email) { setSecondaryError("请输入辅助邮箱"); setSecondaryLoading(false); return; }
    try {
      const res = await fetch(`${API_BASE}/api/auth/secondary-email/request`, {
        method: "POST",
        headers: { "Content-Type": "application/json", ...bearerHeaders(token) },
        body: JSON.stringify({ email: secondaryForm.email })
      });
      const data = await res.json();
      if (!res.ok) {
        if (res.status === 403) {
          clearTokenAndUser();
          setSecondaryError(formatAuthBanMessage(data));
        } else {
          throw new Error(data.error || "发送验证码失败");
        }
        return;
      }
      setSecondarySent(true);
      setSecondaryExpiresAt(data.expiresAt || "");
      setSecondaryMessage("验证码已发送，请检查邮箱");
    } catch (err) {
      setSecondaryError(err.message);
    } finally {
      setSecondaryLoading(false);
    }
  };

  const handleVerifySecondary = async (event) => {
    event.preventDefault();
    setSecondaryLoading(true);
    setSecondaryError("");
    setSecondaryMessage("");
    const token = localStorage.getItem("sproutgate_token");
    if (!token) { setSecondaryError("请先登录"); setSecondaryLoading(false); return; }
    if (!secondaryForm.email || !secondaryForm.code) {
      setSecondaryError("请输入邮箱与验证码");
      setSecondaryLoading(false);
      return;
    }
    try {
      const res = await fetch(`${API_BASE}/api/auth/secondary-email/verify`, {
        method: "POST",
        headers: { "Content-Type": "application/json", ...bearerHeaders(token) },
        body: JSON.stringify({ email: secondaryForm.email, code: secondaryForm.code })
      });
      const data = await res.json();
      if (!res.ok) {
        if (res.status === 403) {
          clearTokenAndUser();
          setSecondaryError(formatAuthBanMessage(data));
        } else {
          throw new Error(data.error || "验证失败");
        }
        return;
      }
      if (data.user) setUser(data.user);
      setSecondaryMessage("辅助邮箱验证成功");
      setSecondarySent(false);
      setSecondaryForm({ email: "", code: "" });
      setSecondaryEditorOpen(false);
    } catch (err) {
      setSecondaryError(err.message);
    } finally {
      setSecondaryLoading(false);
    }
  };

  const handleProfileChange = (field, value) => {
    setProfileForm((prev) => ({ ...prev, [field]: value }));
  };

  const handleProfileSave = async (event) => {
    event.preventDefault();
    setProfileLoading(true);
    setProfileError("");
    setProfileMessage("");
    const token = localStorage.getItem("sproutgate_token");
    if (!token) { setProfileError("请先登录"); setProfileLoading(false); return; }
    const payload = {
      username: profileForm.username,
      phone: profileForm.phone,
      avatarUrl: profileForm.avatarUrl,
      websiteUrl: profileForm.websiteUrl,
      bio: profileForm.bio
    };
    if (profileForm.password) payload.password = profileForm.password;
    try {
      const res = await fetch(`${API_BASE}/api/auth/profile`, {
        method: "PUT",
        headers: { "Content-Type": "application/json", ...bearerHeaders(token) },
        body: JSON.stringify(payload)
      });
      const data = await res.json();
      if (!res.ok) {
        if (res.status === 403) {
          clearTokenAndUser();
          setProfileError(formatAuthBanMessage(data));
        } else {
          throw new Error(data.error || "保存失败");
        }
        return;
      }
      setUser(data.user);
      setProfileMessage("保存成功");
      setProfileForm((prev) => ({ ...prev, password: "" }));
      setProfileEditorOpen(false);
      setProfileEditorFocus("");
    } catch (err) {
      setProfileError(err.message);
    } finally {
      setProfileLoading(false);
    }
  };

  const openProfileEditor = (field = "") => {
    setProfileError("");
    setProfileMessage("");
    if (user) {
      setProfileForm({
        username: user.username || "",
        phone: user.phone || "",
        avatarUrl: user.customAvatarUrl ?? "",
        websiteUrl: user.websiteUrl || "",
        bio: user.bio || "",
        password: ""
      });
    }
    setProfileEditorFocus(field);
    setProfileEditorOpen(true);
  };

  const openSecondaryEditor = () => {
    setSecondaryError("");
    setSecondaryMessage("");
    setSecondarySent(false);
    setSecondaryForm({ email: "", code: "" });
    setSecondaryEditorOpen(true);
  };

  const closeProfileEditor = () => {
    setProfileEditorOpen(false);
    setProfileEditorFocus("");
  };

  const closeSecondaryEditor = () => {
    setSecondaryEditorOpen(false);
    setSecondarySent(false);
    setSecondaryForm({ email: "", code: "" });
    setSecondaryError("");
    setSecondaryMessage("");
  };

  const profileFocusLabels = {
    phone: "手机号",
    avatarUrl: "头像",
    websiteUrl: "个人主页",
    bio: "简介",
    username: "用户名"
  };
  const profileModalTitle = profileEditorFocus && profileFocusLabels[profileEditorFocus]
    ? `修改${profileFocusLabels[profileEditorFocus]}`
    : "修改资料";

  const handleCheckIn = async () => {
    const token = localStorage.getItem("sproutgate_token");
    if (!token) { setCheckInError("请先登录"); return; }
    setCheckInLoading(true);
    setCheckInError("");
    setCheckInMessage("");
    try {
      const res = await fetch(`${API_BASE}/api/auth/check-in`, {
        method: "POST",
        headers: bearerHeaders(token)
      });
      const data = await res.json();
      if (!res.ok) {
        if (res.status === 403) {
          clearTokenAndUser();
          setCheckInError(formatAuthBanMessage(data));
        } else {
          throw new Error(data.error || "签到失败");
        }
        return;
      }
      if (data.user) setUser(data.user);
      const checkIn = data.checkIn || {};
      setCheckInReward(Number(checkIn.rewardCoins) || 1);
      setCheckInToday(Boolean(checkIn.checkedInToday));
      setCheckInMessage(
        data.message || (data.alreadyCheckedIn ? "今日已签到" : `签到成功，获得 ${data.awardedCoins ?? data.rewardCoins ?? 0} 萌芽币`)
      );
    } catch (err) {
      setCheckInError(err.message);
    } finally {
      setCheckInLoading(false);
    }
  };

  const openOAuthLogin = (provider) => {
    const ret = getOAuthReturnTo();
    if (!ret) return;
    if (turnstileEnabled) {
      const tok = mode === "register" ? registerTurnstileToken : loginTurnstileToken;
      if (!tok) {
        if (mode === "register") {
          setRegisterError("请先完成人机验证后再使用第三方登录");
        } else {
          setError("请先完成人机验证后再使用第三方登录");
        }
        return;
      }
      const u = new URL(`${API_BASE}/api/auth/oauth/${encodeURIComponent(provider)}/start`);
      u.searchParams.set("return_to", ret);
      u.searchParams.set("turnstile_token", tok);
      window.location.href = u.toString();
      return;
    }
    window.location.href = `${API_BASE}/api/auth/oauth/${provider}/start?return_to=${encodeURIComponent(ret)}`;
  };

  const bindOAuth = async (provider) => {
    const t = localStorage.getItem("sproutgate_token");
    if (!t) return;
    setProfileError("");
    setProfileMessage("");
    try {
      const res = await fetch(`${API_BASE}/api/auth/oauth/${provider}/bind`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${t}`,
          ...authClientFetchHeaders()
        },
        body: JSON.stringify({ returnTo: getOAuthReturnTo() })
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || "无法开始绑定");
      if (data.url) window.location.href = data.url;
    } catch (e) {
      setProfileError(e.message || "绑定失败");
    }
  };

  const unlinkOAuth = async (provider) => {
    if (!window.confirm("确定要解绑该第三方账号吗？")) return;
    const t = localStorage.getItem("sproutgate_token");
    if (!t) return;
    setProfileError("");
    setProfileMessage("");
    try {
      const res = await fetch(`${API_BASE}/api/auth/oauth/${provider}`, {
        method: "DELETE",
        headers: { Authorization: `Bearer ${t}`, ...authClientFetchHeaders() }
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || "解绑失败");
      if (data.user) setUser(data.user);
      setProfileMessage("已更新第三方账号绑定");
    } catch (e) {
      setProfileError(e.message || "解绑失败");
    }
  };

  if (oauthStandalone && !authFlow?.redirectUri?.trim()) {
    return (
      <section className="panel">
        <div className="card form oauth-authorize-invalid">
          <h2>无效授权链接</h2>
          <p className="hint">缺少回跳地址（redirect_uri 或 return_url）。请从第三方应用重新发起登录。</p>
        </div>
      </section>
    );
  }

  const showOAuthConsent = oauthStandalone && isAuthFlow && Boolean(user);
  const showProfile = Boolean(user) && !showOAuthConsent;

  return (
    <section className="panel">
      <UserPortalAuthSection
        isAuthFlow={isAuthFlow}
        oauthStandalone={oauthStandalone}
        user={user}
        mode={mode}
        setMode={setMode}
        account={account}
        setAccount={setAccount}
        password={password}
        setPassword={setPassword}
        loading={loading}
        error={error}
        setError={setError}
        handleLogin={handleLogin}
        registerForm={registerForm}
        handleRegisterChange={handleRegisterChange}
        registerSent={registerSent}
        registerExpiresAt={registerExpiresAt}
        registerLoading={registerLoading}
        registerError={registerError}
        registerMessage={registerMessage}
        handleSendCode={handleSendCode}
        handleVerifyRegister={handleVerifyRegister}
        setRegisterError={setRegisterError}
        setRegisterMessage={setRegisterMessage}
        registrationRequireInvite={registrationRequireInvite}
        inviteRegisterReward={inviteRegisterReward}
        registrationAccountMin={registrationAccountMin}
        registrationAccountMax={registrationAccountMax}
        resetRegisterFlow={resetRegisterFlow}
        resetForm={resetForm}
        handleResetChange={handleResetChange}
        resetSent={resetSent}
        resetExpiresAt={resetExpiresAt}
        resetLoading={resetLoading}
        resetError={resetError}
        resetMessage={resetMessage}
        handleSendReset={handleSendReset}
        handleResetPassword={handleResetPassword}
        setResetError={setResetError}
        setResetMessage={setResetMessage}
        githubOAuthEnabled={githubOAuthEnabled}
        giteaOAuthEnabled={giteaOAuthEnabled}
        googleOAuthEnabled={googleOAuthEnabled}
        microsoftOAuthEnabled={microsoftOAuthEnabled}
        linuxdoOAuthEnabled={linuxdoOAuthEnabled}
        oauthLogos={oauthLogos}
        onOAuthLogin={openOAuthLogin}
        turnstileEnabled={turnstileEnabled}
        turnstileSiteKey={turnstileSiteKey}
        loginTurnstileToken={loginTurnstileToken}
        onLoginTurnstileToken={setLoginTurnstileToken}
        loginTurnstileRef={loginTurnstileRef}
        registerTurnstileToken={registerTurnstileToken}
        onRegisterTurnstileToken={setRegisterTurnstileToken}
        registerTurnstileRef={registerTurnstileRef}
      />
      {showOAuthConsent && (
        <OAuthConsentScreen
          authFlow={authFlow}
          user={user}
          onAllow={handleContinueAuth}
          onSwitchAccount={handleSwitchAuthAccount}
          onPreviewImage={onPreviewImage}
        />
      )}
      {showProfile && (
        <UserPortalProfileSection
          user={user}
          onPreviewImage={onPreviewImage}
          handleLogout={handleLogout}
          openProfileEditor={openProfileEditor}
          openSecondaryEditor={openSecondaryEditor}
          profileEditorOpen={profileEditorOpen}
          closeProfileEditor={closeProfileEditor}
          handleProfileSave={handleProfileSave}
          profileForm={profileForm}
          handleProfileChange={handleProfileChange}
          profileLoading={profileLoading}
          profileError={profileError}
          profileMessage={profileMessage}
          profileModalTitle={profileModalTitle}
          secondaryEditorOpen={secondaryEditorOpen}
          closeSecondaryEditor={closeSecondaryEditor}
          handleSendSecondary={handleSendSecondary}
          handleVerifySecondary={handleVerifySecondary}
          secondaryForm={secondaryForm}
          handleSecondaryChange={handleSecondaryChange}
          secondarySent={secondarySent}
          secondaryLoading={secondaryLoading}
          secondaryError={secondaryError}
          secondaryMessage={secondaryMessage}
          secondaryExpiresAt={secondaryExpiresAt}
          checkInReward={checkInReward}
          checkInToday={checkInToday}
          checkInLoading={checkInLoading}
          checkInError={checkInError}
          checkInMessage={checkInMessage}
          handleCheckIn={handleCheckIn}
          githubOAuthEnabled={githubOAuthEnabled}
          giteaOAuthEnabled={giteaOAuthEnabled}
          googleOAuthEnabled={googleOAuthEnabled}
          microsoftOAuthEnabled={microsoftOAuthEnabled}
          linuxdoOAuthEnabled={linuxdoOAuthEnabled}
          oauthLogos={oauthLogos}
          onBindOAuth={bindOAuth}
          onUnlinkOAuth={unlinkOAuth}
        />
      )}
    </section>
  );
}
