import type { MouseEvent as ReactMouseEvent, ReactNode } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { Dropdown, Tooltip } from "@arco-design/web-react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useShallow } from "zustand/react/shallow";
import "./Layout.scss";

import { ROUTES } from "@/constants";
import { hasPendingExploreTemplateTabIntent } from "@/store/useExploreFeedTabStore";
import { isLayoutContentScrollLocked, isScrollEventFromLayoutModalMount, isScrollEventInLayoutContentScroll, setLayoutContentModalMount, setLayoutContentScrollRoot } from "@/layouts/layoutContentModalMount";
import { changeLanguage, type AppLanguage, SUPPORTED_LANGUAGES } from "@/i18n";
import { closeAssetDetailsModal, closeCharacterDetailModal, openGatewayApikeyDialog, openLoginDialog, openPointsDetailDialog, openPointsRechargeDialog } from "@/dialog";
import { canAccessRoute, guardRouteAccess } from "@/router/routeGuard";
import { useComposerStore } from "@/store/useComposerStore";
import { useUserStore } from "@/store/useUserStore";
import { getToken, removeToken } from "@/utils/auth";
import { buildCanvasUrl, CANVAS_BASE_URL } from "@/utils/canvasUrl";
import { openLatestPrivacyPolicyLink, PRIVACY_POLICY_KEY_AI_USE_NOTICE, PRIVACY_POLICY_KEY_PRIVACY_POLICY, PRIVACY_POLICY_KEY_USAGE_TUTORIAL, PRIVACY_POLICY_KEY_USER_TERMS } from "@/utils/openLatestPrivacyPolicyLink";
import { getParamConfig, logout } from "@/api/api";
import { useCopyText } from "@/hooks/useCopyText";
import { useLayoutViewportScale } from "@/hooks/useViewportScale";
import { memberLevelMap } from "@/constants";
import { GlobalPromptPanel } from "@/layouts/globalPromptPanel/globalPromptPanel";

interface LayoutProps {
  children?: ReactNode;
}

/** 左侧栏菜单项（文案 key 见 locales）；`iconSrc` 为 `@/` 资源路径，在 JSX 内用 `new URL` 解析。 */
interface LayoutSideNavItem {
  id: string;
  labelKey: string;
  iconSrc: string;
  activeIconSrc?: string;
  to: string;
  badgeKey?: string;
}

interface LayoutSideNavIconProps {
  item: LayoutSideNavItem;
}

const LAYOUT_NAV_ASSETS = {
  logo: new URL("@/assets/images/layout/nav/popi-logo.png", import.meta.url).href,
  explore: new URL("@/assets/images/layout/nav/explore.png", import.meta.url).href,
  exploreActive: new URL("@/assets/images/layout/nav/explore_active.png", import.meta.url).href,
  characters: new URL("@/assets/images/layout/nav/characters.png", import.meta.url).href,
  charactersActive: new URL("@/assets/images/layout/nav/characters_active.png", import.meta.url).href,
  create: new URL("@/assets/images/layout/nav/create.png", import.meta.url).href,
  createActive: new URL("@/assets/images/layout/nav/create_active.png", import.meta.url).href,
  canvas: new URL("@/assets/images/layout/nav/popi-tv.png", import.meta.url).href,
  canvasActive: new URL("@/assets/images/layout/nav/popi-tv_active.png", import.meta.url).href,
  voice: new URL("@/assets/images/layout/nav/voice.png", import.meta.url).href,
  voiceActive: new URL("@/assets/images/layout/nav/voice_active.png", import.meta.url).href,
  subscribe: new URL("@/assets/images/layout/nav/subscribe.png", import.meta.url).href,
  subscribeActive: new URL("@/assets/images/layout/nav/subscribe_active.png", import.meta.url).href,
  profile: new URL("@/assets/images/layout/nav/profile.png", import.meta.url).href,
  profileActive: new URL("@/assets/images/layout/nav/profile_active.png", import.meta.url).href,
  more: new URL("@/assets/images/layout/nav/more.png", import.meta.url).href,
  topHelp: new URL("@/assets/images/layout/nav/top-language.png", import.meta.url).href,
  topLanguage: new URL("@/assets/images/layout/nav/top-help.png", import.meta.url).href,
  topService: new URL("@/assets/images/layout/nav/top-weixin.png", import.meta.url).href,
  topSkill: new URL("@/assets/images/layout/nav/top-skill.png", import.meta.url).href,
} as const;

// 侧栏图标顺序与 2026 7月官网 Figma 设计保持一致。
const LAYOUT_SIDE_NAV_ITEMS: readonly LayoutSideNavItem[] = [
  {
    id: "explore",
    labelKey: "layout.nav.explore",
    iconSrc: LAYOUT_NAV_ASSETS.explore,
    activeIconSrc: LAYOUT_NAV_ASSETS.exploreActive,
    to: ROUTES.EXPLORE,
  },
  {
    id: "characters",
    labelKey: "layout.nav.characters",
    iconSrc: LAYOUT_NAV_ASSETS.characters,
    activeIconSrc: LAYOUT_NAV_ASSETS.charactersActive,
    to: ROUTES.CHARACTERS,
  },
  {
    id: "voice",
    labelKey: "layout.nav.voice",
    iconSrc: LAYOUT_NAV_ASSETS.voice,
    activeIconSrc: LAYOUT_NAV_ASSETS.voiceActive,
    to: ROUTES.VOICE,
  },
  {
    id: "create",
    labelKey: "layout.nav.create",
    iconSrc: LAYOUT_NAV_ASSETS.create,
    activeIconSrc: LAYOUT_NAV_ASSETS.createActive,
    to: ROUTES.CREATE,
  },
  {
    id: "canvas",
    labelKey: "layout.nav.canvas",
    iconSrc: LAYOUT_NAV_ASSETS.canvas,
    activeIconSrc: LAYOUT_NAV_ASSETS.canvasActive,
    to: CANVAS_BASE_URL ?? "",
    badgeKey: "layout.nav.canvasBadge",
  },
];

const LANGUAGE_LABEL_KEYS: Record<AppLanguage, string> = {
  "zh-CN": "layout.language.zhCN",
  "zh-TW": "layout.language.zhTW",
  "en-US": "layout.language.enUS",
};

const LAYOUT_PROMO_BAR_DISMISSED_KEY = "popiart.layout.promoBar.dismissed";

function isLayoutPromoBarDismissed(): boolean {
  try {
    return localStorage.getItem(LAYOUT_PROMO_BAR_DISMISSED_KEY) === "1";
  } catch {
    return false;
  }
}

function buildSkillHubUrl(): string | null {
  const baseUrl = import.meta.env.VITE_SKILLHUB_URL;
  if (!baseUrl) return null;
  const url = new URL(baseUrl);
  url.searchParams.set("token", getToken() ?? "");
  url.searchParams.set("t", String(Date.now()));
  return url.toString();
}

function readServiceQrCode(value: unknown): string {
  if (typeof value !== "object" || value == null || !("systemConfig" in value)) return "";
  const systemConfig = value.systemConfig;
  if (typeof systemConfig !== "object" || systemConfig == null || !("serviceQrCode" in systemConfig)) return "";
  return typeof systemConfig.serviceQrCode === "string" ? systemConfig.serviceQrCode : "";
}

const LAYOUT_FOOTER_NAV_ITEMS: readonly LayoutSideNavItem[] = [
  {
    id: "subscribe",
    labelKey: "layout.footer.subscribe",
    iconSrc: LAYOUT_NAV_ASSETS.subscribe,
    activeIconSrc: LAYOUT_NAV_ASSETS.subscribeActive,
    to: ROUTES.SUBSCRIBE,
  },
  {
    id: "profile",
    labelKey: "layout.footer.profile",
    iconSrc: LAYOUT_NAV_ASSETS.profile,
    activeIconSrc: LAYOUT_NAV_ASSETS.profileActive,
    to: ROUTES.PROFILE,
  },
];

function LayoutSideNavIcon({ item }: LayoutSideNavIconProps) {
  if (!item.activeIconSrc) {
    return <img className="layoutShell__sideIcon" src={item.iconSrc} alt="" />;
  }

  return (
    <span className="layoutShell__sideIcon" aria-hidden="true">
      <img className="layoutShell__sideIconImage layoutShell__sideIconImage_default" src={item.iconSrc} alt="" />
      <img className="layoutShell__sideIconImage layoutShell__sideIconImage_active" src={item.activeIconSrc} alt="" />
    </span>
  );
}

/**
 * 主布局（与路由结合）：顶部栏 + 左侧栏 + 右侧内容区
 *
 * 说明：content 区域由各路由页面自己渲染，这里只保证外层布局像素级对齐。
 */
export function Layout({ children }: LayoutProps) {
  const { pathname, search } = useLocation();
  const navigate = useNavigate();
  const { t, i18n } = useTranslation();
  const { user, setUser, clearUser } = useUserStore(
    useShallow((s) => ({
      user: s.user,
      setUser: s.setUser,
      clearUser: s.clearUser,
    })),
  );
  const isLogin = user != null;
  const [userMenuOpen, setUserMenuOpen] = useState(false);
  const [languageMenuOpen, setLanguageMenuOpen] = useState(false);
  const [footerPopoverOpen, setFooterPopoverOpen] = useState(false);
  const [topbarServiceQrOpen, setTopbarServiceQrOpen] = useState(false);
  const [promoBarVisible, setPromoBarVisible] = useState(() => !isLayoutPromoBarDismissed());
  const activePromptPanelHost = useComposerStore((s) => s.activePromptPanelHost);

  const footerPopoverRef = useRef<HTMLDivElement>(null);
  const userMenuRef = useRef<HTMLDivElement>(null);
  const languageToolRef = useRef<HTMLDivElement>(null);
  const serviceToolRef = useRef<HTMLDivElement>(null);
  const contentFrameShellRef = useRef<HTMLElement | null>(null);
  const contentScrollRef = useRef<HTMLDivElement | null>(null);
  const layoutViewportRef = useRef<HTMLDivElement>(null);
  const { scaleInnerRef, scaleSpacerRef } = useLayoutViewportScale(layoutViewportRef);
  const copyText = useCopyText();
  const requestComposerCollapse = useComposerStore((s) => s.requestComposerCollapse);
  const requestComposerExpand = useComposerStore((s) => s.requestComposerExpand);
  const prevPathnameRef = useRef(pathname);

  useEffect(() => {
    if (!getToken()) {
      clearUser();

      return;
    }

    void setUser();
  }, [clearUser, setUser]);

  const finishLogout = (): void => {
    removeToken();
    clearUser();
    setUserMenuOpen(false);
    setLanguageMenuOpen(false);
    setFooterPopoverOpen(false);
    window.location.href = ROUTES.EXPLORE;
  };

  const handleLogout = (): void => {
    void (async () => {
      try {
        await logout();
      } catch {
        // 接口失败也继续本地退出
      } finally {
        finishLogout();
      }
    })();
  };

  const handleLayoutNavClick = (evt: ReactMouseEvent<HTMLAnchorElement>, to: string): void => {
    if (CANVAS_BASE_URL && to === CANVAS_BASE_URL) {
      evt.preventDefault();
      const url = buildCanvasUrl();
      if (url) window.open(url, "_blank");
      return;
    }
    if (!canAccessRoute(to)) {
      evt.preventDefault();
      void guardRouteAccess(to);
      return;
    }
    if (to === ROUTES.CREATE || to.startsWith(`${ROUTES.CREATE}/`)) {
      requestComposerExpand();
    }
  };

  const isVoiceArea = pathname === ROUTES.VOICE || pathname.startsWith(`${ROUTES.VOICE_MUSIC_SHARE}/`);
  const shouldShowFloatingComposer = pathname !== ROUTES.SUBSCRIBE && pathname !== ROUTES.CHARACTERS_CREATE && !isVoiceArea;

  const handlePromoBarClose = useCallback((evt: ReactMouseEvent<HTMLButtonElement>) => {
    evt.stopPropagation();
    evt.preventDefault();
    setPromoBarVisible(false);
    try {
      localStorage.setItem(LAYOUT_PROMO_BAR_DISMISSED_KEY, "1");
    } catch {
      // ignore quota / private mode
    }
  }, []);

  const [serviceQrCode, setServiceQrCode] = useState("");

  useEffect(() => {
    void getParamConfig()
      .then((res) => {
        setServiceQrCode(readServiceQrCode(res));
      })
      .catch(() => {
        setServiceQrCode("");
      });
  }, []);

  useEffect(() => {
    if (pathname === ROUTES.EXPLORE && hasPendingExploreTemplateTabIntent()) {
      return;
    }
    const scroll = contentScrollRef.current;
    if (scroll != null) {
      scroll.scrollTop = 0;
    }
    window.scrollTo(0, 0);
  }, [pathname]);

  useEffect(() => {
    if (prevPathnameRef.current === pathname) return;
    prevPathnameRef.current = pathname;
    if (pathname === ROUTES.CREATE || pathname.startsWith(`${ROUTES.CREATE}/`)) {
      requestComposerExpand();
    }
  }, [pathname, requestComposerExpand]);

  useEffect(() => {
    const shell = contentFrameShellRef.current;
    if (shell == null) return;
    /** 从「基准位置」到「当前位置」的位移达到该值时收起（不是 scrollTop 离文档顶部 280） */
    const DISPLACEMENT_THRESHOLD = 140;
    /** 最近一次滚动后若这么久内仍未达到阈值，将基准移到当前位置，下次从 0 重新量 */
    const BASELINE_RESET_MS = 3000;

    const baselineByScroller = new Map<HTMLElement, number>();
    const resetTimerByScroller = new Map<HTMLElement, number>();

    const clearResetTimer = (scroller: HTMLElement) => {
      const id = resetTimerByScroller.get(scroller);
      if (id != null) {
        window.clearTimeout(id);
        resetTimerByScroller.delete(scroller);
      }
    };

    const scheduleBaselineResetIfIdle = (scroller: HTMLElement) => {
      clearResetTimer(scroller);
      const id = window.setTimeout(() => {
        baselineByScroller.set(scroller, scroller.scrollTop);
        resetTimerByScroller.delete(scroller);
      }, BASELINE_RESET_MS);
      resetTimerByScroller.set(scroller, id);
    };

    const onScroll = (event: Event) => {
      if (isLayoutContentScrollLocked()) return;
      if (isScrollEventFromLayoutModalMount(event.target)) return;
      if (!isScrollEventInLayoutContentScroll(event.target)) return;
      const scroller = event.target instanceof HTMLElement ? event.target : null;
      if (scroller == null) return;

      let baselineTop = baselineByScroller.get(scroller);
      if (baselineTop == null) {
        baselineTop = scroller.scrollTop;
        baselineByScroller.set(scroller, baselineTop);
      }

      const top = scroller.scrollTop;
      const displacementFromBaseline = top - baselineTop;
      if (displacementFromBaseline >= DISPLACEMENT_THRESHOLD) {
        requestComposerCollapse();
        baselineByScroller.set(scroller, top);
        clearResetTimer(scroller);
        return;
      }
      scheduleBaselineResetIfIdle(scroller);
    };

    shell.addEventListener("scroll", onScroll, { capture: true, passive: true });
    return () => {
      for (const scroller of resetTimerByScroller.keys()) {
        clearResetTimer(scroller);
      }
      shell.removeEventListener("scroll", onScroll, { capture: true });
    };
  }, [pathname, requestComposerCollapse]);

  useEffect(() => {
    if (!footerPopoverOpen) return;
    const onDocMouseDown = (evt: MouseEvent) => {
      const el = footerPopoverRef.current;
      const target = evt.target as Node | null;
      if (el && target && !el.contains(target)) {
        setFooterPopoverOpen(false);
        setTopbarServiceQrOpen(false);
      }
    };
    document.addEventListener("mousedown", onDocMouseDown);
    return () => document.removeEventListener("mousedown", onDocMouseDown);
  }, [footerPopoverOpen]);

  useEffect(() => {
    if (!userMenuOpen) return;
    const onDocMouseDown = (evt: MouseEvent) => {
      const el = userMenuRef.current;
      const target = evt.target as Node | null;
      if (el && target && !el.contains(target)) {
        setUserMenuOpen(false);
      }
    };
    document.addEventListener("mousedown", onDocMouseDown);
    return () => document.removeEventListener("mousedown", onDocMouseDown);
  }, [userMenuOpen]);

  useEffect(() => {
    if (!topbarServiceQrOpen) return;
    const onDocMouseDown = (evt: MouseEvent) => {
      const el = serviceToolRef.current;
      const target = evt.target as Node | null;
      if (el && target && !el.contains(target)) {
        setTopbarServiceQrOpen(false);
      }
    };
    document.addEventListener("mousedown", onDocMouseDown);
    return () => document.removeEventListener("mousedown", onDocMouseDown);
  }, [topbarServiceQrOpen]);

  const dismissMainDetailModals = useCallback(() => {
    closeCharacterDetailModal();
    closeAssetDetailsModal();
  }, []);

  return (
    <div className="layoutShellViewport" ref={layoutViewportRef}>
      <div className="layoutShellScaleSpacer" ref={scaleSpacerRef}>
        <div className="layoutShellScaleInner" ref={scaleInnerRef}>
          <div className={["layoutShell", !promoBarVisible ? "layoutShell_promoDismissed" : ""].filter(Boolean).join(" ")}>
            {promoBarVisible ? (
              <div className="layoutShell__promoBar" role="banner" aria-label={t("layout.promo.aria")} onClick={() => navigate(ROUTES.SUBSCRIBE)}>
                <div className="layoutShell__promoBarInner">
                  <div className="layoutShell__promoSegment">
                    <span className="layoutShell__promoBadge">{t("layout.promo.badgeNew")}</span>
                    <p className="layoutShell__promoText">
                      <span className="layoutShell__promoText_heavy">{t("layout.promo.membershipTitle")}</span>
                      <span>{t("layout.promo.membershipDesc")}</span>
                    </p>
                  </div>
                  <p className="layoutShell__promoText layoutShell__promoText_seedance">
                    <span className="layoutShell__promoText_heavy">{t("layout.promo.seedanceHighlight")}</span>
                    <span>{t("layout.promo.seedanceSuffix")}</span>
                  </p>
                  <div className="layoutShell__promoSegment">
                    <span className="layoutShell__promoBadge">{t("layout.promo.badgeHot")}</span>
                    <p className="layoutShell__promoText">
                      <span>{t("layout.promo.discountPrefix")}</span>
                      <span className="layoutShell__promoText_heavy">{t("layout.promo.discountValue")}</span>
                      <span>{t("layout.promo.discountSuffix")}</span>
                    </p>
                  </div>
                  <p className="layoutShell__promoText">
                    <span>{t("layout.promo.pointsPrefix")}</span>
                    <span className="layoutShell__promoText_heavy">{t("layout.promo.pointsValue")}</span>
                  </p>
                </div>
                <button type="button" className="layoutShell__promoClose" aria-label={t("layout.promo.close")} onClick={handlePromoBarClose}>
                  <img src={new URL("@/assets/images/ic_close.png", import.meta.url).href} alt="" />
                </button>
              </div>
            ) : null}
            <header className="layoutShell__topbar" aria-label={t("layout.aria.topbar")} onClick={dismissMainDetailModals}>
              <div className="layoutShell__topbarRight" aria-label={t("layout.aria.userArea")}>
                <div className="layoutShell__topbarTools">
                  <button
                    type="button"
                    className="goToSkillHub"
                    onClick={() => {
                      const url = buildSkillHubUrl();
                      if (url) window.open(url, "_blank");
                    }}
                  >
                    <img className="layoutShell__topbarSkillIcon" src={LAYOUT_NAV_ASSETS.topSkill} alt="" />
                    <span className="layoutShell__topbarSkillText">{t("layout.topbar.skill")}</span>
                  </button>
                  {/* 使用说明 */}
                  <div className="layoutShell__topbarTool">
                    <Tooltip content={t("layout.usageTutorial")}>
                      <button
                        type="button"
                        className="layoutShell__topbarIconBtn"
                        onClick={() => {
                          void openLatestPrivacyPolicyLink(PRIVACY_POLICY_KEY_USAGE_TUTORIAL);
                        }}
                        aria-label={t("layout.usageTutorial")}
                      >
                        <img src={LAYOUT_NAV_ASSETS.topHelp} alt="" />
                      </button>
                    </Tooltip>
                  </div>
                  {/* 语言菜单 */}
                  <div className="layoutShell__topbarTool" ref={languageToolRef}>
                    <Dropdown
                      trigger="click"
                      position="br"
                      popupVisible={languageMenuOpen}
                      onVisibleChange={(visible) => {
                        setLanguageMenuOpen(visible);
                        if (visible) {
                          setUserMenuOpen(false);
                          setFooterPopoverOpen(false);
                          setTopbarServiceQrOpen(false);
                        }
                      }}
                      /* 高于同区域 Tooltip，避免悬停提示盖住下拉菜单 */
                      triggerProps={{ style: { zIndex: 2000 } }}
                      getPopupContainer={() => languageToolRef.current ?? document.body}
                      droplist={
                        <div className="layoutShell__languageDropdown" role="menu" aria-label={t("layout.language.menuAria")}>
                          {SUPPORTED_LANGUAGES.map((lng) => {
                            const active = i18n.language === lng;
                            const itemClass = ["layoutShell__languageDropdownItem", active ? "layoutShell__languageDropdownItem_active" : ""].filter(Boolean).join(" ");
                            return (
                              <button
                                key={lng}
                                type="button"
                                className={itemClass}
                                role="menuitem"
                                onClick={() => {
                                  void changeLanguage(lng);
                                  setLanguageMenuOpen(false);
                                }}
                              >
                                {t(LANGUAGE_LABEL_KEYS[lng])}
                              </button>
                            );
                          })}
                        </div>
                      }
                    >
                      <Tooltip content={t("layout.language.tooltip")} disabled={languageMenuOpen} {...(languageMenuOpen ? { popupVisible: false } : {})} style={{ zIndex: 1000 }}>
                        <button type="button" className="layoutShell__topbarIconBtn" aria-label={t("layout.language.menuAria")} aria-expanded={languageMenuOpen} aria-haspopup="menu">
                          <img src={LAYOUT_NAV_ASSETS.topLanguage} alt="" />
                        </button>
                      </Tooltip>
                    </Dropdown>
                  </div>
                  {/* 联系客服：顶栏定位浮层展示二维码 */}
                  <div className="layoutShell__topbarTool" ref={serviceToolRef}>
                    <button
                      type="button"
                      className={["layoutShell__topbarIconBtn", topbarServiceQrOpen ? "layoutShell__topbarIconBtn_active" : ""].filter(Boolean).join(" ")}
                      aria-label={t("layout.topbar.contactServiceAria")}
                      aria-expanded={topbarServiceQrOpen}
                      onClick={(e) => {
                        e.stopPropagation();
                        if (!serviceQrCode) return;
                        setTopbarServiceQrOpen((v) => !v);
                        setLanguageMenuOpen(false);
                        setUserMenuOpen(false);
                        setFooterPopoverOpen(false);
                      }}
                    >
                      <img src={LAYOUT_NAV_ASSETS.topService} alt="" />
                    </button>
                    {topbarServiceQrOpen ? (
                      <div className="layoutShell__topbarServiceQrFloat" role="dialog" aria-label={t("layout.topbar.contactServiceAria")} onClick={(e) => e.stopPropagation()}>
                        <img className="layoutShell__topbarServiceQrImage" src={serviceQrCode} alt={t("contactService.qrAlt")} />
                        <p className="layoutShell__topbarServiceQrTip">{t("layout.topbar.scan_contact_service")}</p>
                      </div>
                    ) : null}
                  </div>
                </div>
                {/* 用户积分 */}
                <div className="layoutShell__userPoints" onClick={() => openPointsDetailDialog()}>
                  <div className="layoutShell__userPointsValue">{String(user?.allCoins ?? 0)}</div>
                  <div className="layoutShell__userPointsLabel">{t("layout.nav.points")}</div>
                </div>
                {/* 登录注册 */}
                {isLogin ? (
                  <div className="layoutShell__avatarMenu" ref={userMenuRef}>
                    <button
                      type="button"
                      className="layoutShell__avatar"
                      onClick={() => {
                        setUserMenuOpen((v) => !v);
                        setLanguageMenuOpen(false);
                        setFooterPopoverOpen(false);
                        setTopbarServiceQrOpen(false);
                      }}
                      aria-label={t("layout.userMenu.aria")}
                    >
                      <img className="layoutShell__avatarBg" src={String(user?.avatar)} />
                    </button>
                    {userMenuOpen ? (
                      <div className="layoutShell__avatarDropdown" role="menu" aria-label={t("layout.userMenu.actionsAria")}>
                        <section className="layoutShell__avatarUserCard">
                          <img className="layoutShell__avatarUserAvatar" src={String(user?.avatar ?? "")} />
                          <div className="layoutShell__avatarUserMain">
                            <div className="layoutShell__avatarUserName">{String(user?.name ?? "")}</div>
                            <div className="layoutShell__avatarUserMeta">
                              <span className="layoutShell__avatarUserId">{String(user?.id ?? "")}</span>
                              <button type="button" className="layoutShell__avatarCopyId" onClick={() => void copyText(user?.id)} aria-label={t("layout.userMenu.copyUserId")}>
                                <img src={new URL("@/assets/images/ic_copy.png", import.meta.url).href} />
                              </button>
                            </div>
                          </div>
                        </section>

                        <button type="button" className="layoutShell__avatarMemberRow" onClick={() => navigate(ROUTES.SUBSCRIBE)}>
                          <span className="layoutShell__avatarMemberLabel">{Number(user?.memberLevel) > 0 ? "VIP" : t(memberLevelMap[0])} </span>
                          <span className="layoutShell__avatarMemberAction">{t("layout.userMenu.openMembership")}</span>
                          <img className="layoutShell__avatarChevron" src={new URL("@/assets/images/ic_arrow_right_purple.png", import.meta.url).href} />
                        </button>

                        <section className="layoutShell__avatarPointsCard">
                          <div className="layoutShell__avatarPointsLeft">
                            <div className="layoutShell__avatarPointsTop">
                              <span className="layoutShell__avatarPointsValue">{String(user?.allCoins ?? 0)}</span>
                              <img className="layoutShell__avatarPointsIcon" src={new URL("@/assets/images/ic_stars_active.png", import.meta.url).href} />
                            </div>
                            <button type="button" className="layoutShell__avatarPointsDetail" onClick={() => openPointsDetailDialog()}>
                              {t("layout.userMenu.pointsDetail")}
                              <img className="layoutShell__avatarArrow" src={new URL("@/assets/images/ic_arrow_right_gray.png", import.meta.url).href} />
                            </button>
                          </div>
                          <button type="button" className="layoutShell__avatarRechargeBtn" onClick={() => void openPointsRechargeDialog()}>
                            {t("layout.userMenu.recharge")}
                          </button>
                        </section>

                        <button
                          type="button"
                          className="layoutShell__avatarDropdownItem"
                          role="menuitem"
                          onClick={() => {
                            void openGatewayApikeyDialog();
                            setUserMenuOpen(false);
                          }}
                        >
                          <img className="layoutShell__avatarDropdownIcon" src={new URL("@/assets/images/ic_key.png", import.meta.url).href} />
                          <span>{t("layout.userMenu.personalKey")}</span>
                        </button>
                        <button
                          type="button"
                          className="layoutShell__avatarDropdownItem"
                          role="menuitem"
                          onClick={() => {
                            openPointsDetailDialog({ initialTab: "invite" });
                            setUserMenuOpen(false);
                          }}
                        >
                          <img className="layoutShell__avatarDropdownIcon" src={new URL("@/assets/images/ic_invite.png", import.meta.url).href} />
                          <span>{t("layout.userMenu.inviteFriends")}</span>
                        </button>
                        <button
                          type="button"
                          className="layoutShell__avatarDropdownItem"
                          role="menuitem"
                          onClick={() => {
                            navigate(ROUTES.PROFILE);
                            setUserMenuOpen(false);
                          }}
                        >
                          <img className="layoutShell__avatarDropdownIcon" src={new URL("@/assets/images/ic_user.png", import.meta.url).href} />
                          <span>{t("layout.userMenu.profileCenter")}</span>
                        </button>
                        <button type="button" className="layoutShell__avatarDropdownItem" role="menuitem" onClick={handleLogout}>
                          <img className="layoutShell__avatarDropdownIcon" src={new URL("@/assets/images/ic_gout.png", import.meta.url).href} />
                          <span>{t("layout.userMenu.logout")}</span>
                        </button>
                      </div>
                    ) : null}
                  </div>
                ) : (
                  <button type="button" className="layoutShell__avatar layoutShell__avatar_guest" aria-label={t("auth.login_or_register")} onClick={() => void openLoginDialog()}>
                    <img className="layoutShell__avatarBg" src={new URL("@/assets/images/avatar.png", import.meta.url).href} alt="" />
                  </button>
                )}
              </div>
            </header>

            <div className="layoutShell__body">
              {/* 左侧栏菜单 */}
              <aside className={["layoutShell__sidebar", footerPopoverOpen ? "layoutShell__sidebar_footerPopoverOpen" : ""].filter(Boolean).join(" ")} aria-label={t("layout.aria.sidebar")} onClick={dismissMainDetailModals}>
                <button type="button" className="layoutShell__brand" aria-label={t("layout.brand.aria")} onClick={() => navigate(ROUTES.INDEX)}>
                  <img className="layoutShell__brandLogo" src={LAYOUT_NAV_ASSETS.logo} alt="" />
                </button>
                <nav className="layoutShell__sideNav" aria-label={t("layout.aria.sideNav")}>
                  {LAYOUT_SIDE_NAV_ITEMS.map((item) => {
                    const isActive = pathname === item.to || (item.to !== "/" && pathname.startsWith(`${item.to}/`));
                    const className = ["layoutShell__sideItem", isActive ? "layoutShell__sideItem_active" : ""].filter(Boolean).join(" ");
                    return (
                      <Link key={item.id} to={item.to} className={className} onClick={(evt) => handleLayoutNavClick(evt, item.to)}>
                        <LayoutSideNavIcon item={item} />
                        <span className="layoutShell__sideText">{t(item.labelKey)}</span>
                        {item.badgeKey ? <span className="layoutShell__sideBadge">{t(item.badgeKey)}</span> : null}
                      </Link>
                    );
                  })}
                </nav>
                <div className="layoutShell__footer">
                  <nav className="layoutShell__footerNav" aria-label={t("layout.footer.navAria")}>
                    {LAYOUT_FOOTER_NAV_ITEMS.map((item) => {
                      const isActive = pathname === item.to || (item.to !== "/" && pathname.startsWith(`${item.to}/`));
                      const className = ["layoutShell__sideItem", isActive ? "layoutShell__sideItem_active" : ""].filter(Boolean).join(" ");
                      return (
                        <Link key={item.id} to={item.to} className={className} onClick={(evt) => handleLayoutNavClick(evt, item.to)}>
                          <LayoutSideNavIcon item={item} />
                          <span className="layoutShell__sideText">{t(item.labelKey)}</span>
                        </Link>
                      );
                    })}
                  </nav>
                  <div className="layoutShell__footerPopoverTrigger" ref={footerPopoverRef}>
                    <button
                      type="button"
                      className="layoutShell__footerMenuBtn"
                      onClick={(e) => {
                        e.stopPropagation();
                        setFooterPopoverOpen((v) => !v);
                        setTopbarServiceQrOpen(false);
                        setLanguageMenuOpen(false);
                        setUserMenuOpen(false);
                      }}
                      aria-label={t("layout.footerPopover.menuAria")}
                      aria-expanded={footerPopoverOpen}
                      aria-haspopup="dialog"
                    >
                      <img src={LAYOUT_NAV_ASSETS.more} alt="" />
                    </button>
                    {footerPopoverOpen ? (
                      <div className="layoutShell__footerPopoverPanel" role="dialog" aria-label={t("layout.footerPopover.menuAria")} onClick={(e) => e.stopPropagation()}>
                        <button
                          type="button"
                          className="layoutShell__footerPopoverLink"
                          onClick={() => {
                            setFooterPopoverOpen(false);
                            setTopbarServiceQrOpen(false);
                            void openLatestPrivacyPolicyLink(PRIVACY_POLICY_KEY_USER_TERMS);
                          }}
                        >
                          {t("app.user_terms")}
                        </button>
                        <button
                          type="button"
                          className="layoutShell__footerPopoverLink"
                          onClick={() => {
                            setFooterPopoverOpen(false);
                            setTopbarServiceQrOpen(false);
                            void openLatestPrivacyPolicyLink(PRIVACY_POLICY_KEY_PRIVACY_POLICY);
                          }}
                        >
                          {t("app.privacy_policy")}
                        </button>
                        <button
                          type="button"
                          className="layoutShell__footerPopoverLink"
                          onClick={() => {
                            setFooterPopoverOpen(false);
                            setTopbarServiceQrOpen(false);
                            void openLatestPrivacyPolicyLink(PRIVACY_POLICY_KEY_AI_USE_NOTICE);
                          }}
                        >
                          {t("app.ai_use_notice")}
                        </button>
                        <div className="layoutShell__footerPopoverMeta">
                          <div>{t("layout.footerPopover.icp")}</div>
                          <div>{t("footer.address")}</div>
                          <div>{t("layout.footerPopover.copyrightLine")}</div>
                        </div>
                      </div>
                    ) : null}
                  </div>
                </div>
              </aside>

              {/* 内容区 */}
              <main className="layoutShell__contentFrame" aria-label={t("layout.aria.content")} ref={contentFrameShellRef}>
                <div
                  className="layoutShell__contentScroll"
                  ref={(el) => {
                    contentScrollRef.current = el;
                    setLayoutContentScrollRoot(el);
                  }}
                >
                  {children}
                </div>
                <div className="layoutShell__modalMount" ref={setLayoutContentModalMount} aria-hidden />
              </main>
              {shouldShowFloatingComposer && activePromptPanelHost === "layout" ? (
                <div className="layoutShell__floatingComposerMount">
                  <GlobalPromptPanel pathname={pathname} search={search} navigateTo={(to, options) => navigate(to, options)} />
                </div>
              ) : null}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
