import {
  type ReactNode,
  useCallback,
  useEffect,
  useRef,
  useState,
} from 'react';

import type { PlatformTheme } from '../../../packages/shared/src/contracts/runtime';
import type {
  AuthAuditLogEntry,
  AuthCaptchaChallenge,
  AuthRiskBlockSummary,
  AuthSessionSummary,
  AuthUser,
} from '../../services/authService';
import type { PlatformSettingsSection } from './AuthUiContext';
import { CaptchaChallengeField } from './CaptchaChallengeField';

type AccountModalProps = {
  user: AuthUser;
  isOpen: boolean;
  initialSection?: PlatformSettingsSection | null;
  platformTheme: PlatformTheme;
  riskBlocks: AuthRiskBlockSummary[];
  sessions: AuthSessionSummary[];
  auditLogs: AuthAuditLogEntry[];
  loadingRiskBlocks: boolean;
  loadingSessions: boolean;
  loadingAuditLogs: boolean;
  isHydratingSettings: boolean;
  isPersistingSettings: boolean;
  settingsError: string | null;
  onClose: () => void;
  onPlatformThemeChange: (theme: PlatformTheme) => void;
  onLogout: () => Promise<void>;
  onRefreshRiskBlocks: () => Promise<void>;
  onLiftRiskBlock: (scopeType: 'phone' | 'ip') => Promise<void>;
  onRefreshSessions: () => Promise<void>;
  onLogoutAll: () => Promise<void>;
  onRefreshAuditLogs: () => Promise<void>;
  onRevokeSession: (sessionId: string) => Promise<void>;
  changePhoneCaptchaChallenge: AuthCaptchaChallenge | null;
  onSendChangePhoneCode: (
    phone: string,
    captcha?: {
      challengeId?: string;
      answer?: string;
    },
  ) => Promise<{
    cooldownSeconds: number;
    expiresInSeconds: number;
  }>;
  onChangePhone: (phone: string, code: string) => Promise<void>;
};

const SETTINGS_SECTIONS: Array<{
  id: 'appearance' | 'account';
  label: string;
  detail: string;
}> = [
  { id: 'appearance', label: '主题外观', detail: '亮暗主题' },
  { id: 'account', label: '账号信息', detail: '身份与安全' },
];

const ACCOUNT_MODAL_MAX_HEIGHT =
  'calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) - 2rem)';

type PrimarySettingsSection = (typeof SETTINGS_SECTIONS)[number]['id'];

function normalizeSettingsSection(
  section: PlatformSettingsSection | null | undefined,
): PrimarySettingsSection | null {
  if (section === 'appearance') {
    return 'appearance';
  }

  if (
    section === 'account' ||
    section === 'security' ||
    section === 'devices' ||
    section === 'logs'
  ) {
    return 'account';
  }

  return null;
}

function resolveLoginMethodLabel(loginMethod: AuthUser['loginMethod']) {
  switch (loginMethod) {
    case 'wechat':
      return '微信登录';
    case 'phone':
      return '手机号登录';
    default:
      return '账号登录';
  }
}

function formatSessionTime(value: string) {
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return value;
  }

  return date.toLocaleString('zh-CN', {
    hour12: false,
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  });
}

function SettingsEntryCard({
  label,
  detail,
  summary,
  onClick,
}: {
  label: string;
  detail: string;
  summary: string;
  onClick: (trigger: HTMLButtonElement) => void;
}) {
  return (
    <button
      type="button"
      onClick={(event) => onClick(event.currentTarget)}
      className="platform-subpanel w-full rounded-[1.5rem] px-4 py-4 text-left transition hover:border-[var(--platform-surface-hover-border)]"
    >
      <div className="flex items-start justify-between gap-3">
        <div>
          <div className="text-sm font-semibold text-[var(--platform-text-strong)]">
            {label}
          </div>
          <div className="mt-1 text-[11px] text-[var(--platform-text-soft)]">
            {detail}
          </div>
        </div>
        <span className="text-lg leading-none text-[var(--platform-text-soft)]">
          ›
        </span>
      </div>
      <div className="mt-3 text-sm text-[var(--platform-text-base)]">
        {summary}
      </div>
    </button>
  );
}

function OverlayPanel({
  eyebrow,
  title,
  description,
  action,
  onBack,
  onClose,
  children,
}: {
  eyebrow: string;
  title: string;
  description?: string;
  action?: ReactNode;
  onBack?: () => void;
  onClose: () => void;
  children: ReactNode;
}) {
  return (
    <div
      className="absolute inset-0 z-10 flex items-end bg-black/20 backdrop-blur-[2px] sm:items-center sm:justify-center sm:p-4"
      onClick={onBack ?? onClose}
    >
      <div
        className="platform-auth-card flex max-h-full w-full min-h-0 flex-col overflow-hidden rounded-[28px] p-5 sm:max-w-3xl sm:p-6"
        role="dialog"
        aria-modal="true"
        aria-label={title}
        style={{ maxHeight: ACCOUNT_MODAL_MAX_HEIGHT }}
        onClick={(event) => event.stopPropagation()}
      >
        <div className="flex items-start justify-between gap-4">
          <div className="min-w-0">
            <div className="text-xs uppercase tracking-[0.28em] text-[var(--platform-cool-text)]">
              {eyebrow}
            </div>
            <div className="mt-2 text-2xl font-semibold text-[var(--platform-text-strong)]">
              {title}
            </div>
            {description ? (
              <div className="mt-2 text-sm text-[var(--platform-text-base)]">
                {description}
              </div>
            ) : null}
          </div>
          <div className="flex items-center gap-2">
            {action}
            {onBack ? (
              <button
                type="button"
                autoFocus
                className="platform-button platform-button--ghost min-h-0 rounded-full px-3 py-1.5 text-xs"
                onClick={onBack}
              >
                返回
              </button>
            ) : (
              <button
                type="button"
                className="platform-button platform-button--ghost min-h-0 rounded-full px-3 py-1.5 text-xs"
                onClick={onClose}
              >
                关闭
              </button>
            )}
          </div>
        </div>

        <div className="mt-5 min-h-0 flex-1 overflow-y-auto overscroll-y-contain pr-1">
          {children}
        </div>
      </div>
    </div>
  );
}

function ThemeOptionCard({
  active,
  title,
  detail,
  previewClassName,
  onClick,
}: {
  active: boolean;
  title: string;
  detail: string;
  previewClassName: string;
  onClick: () => void;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      className={`platform-subpanel w-full rounded-[1.5rem] p-4 text-left transition ${
        active
          ? 'border-[var(--platform-surface-hover-border)] shadow-[0_18px_44px_rgba(255,91,132,0.14)]'
          : 'hover:border-[var(--platform-surface-hover-border)]'
      }`}
    >
      <div className={`h-28 rounded-[1.15rem] ${previewClassName}`} />
      <div className="mt-4 text-base font-semibold text-[var(--platform-text-strong)]">
        {title}
      </div>
      <div className="mt-1 text-sm text-[var(--platform-text-base)]">
        {detail}
      </div>
    </button>
  );
}

export function AccountModal({
  user,
  isOpen,
  initialSection = null,
  platformTheme,
  riskBlocks,
  sessions,
  auditLogs,
  loadingRiskBlocks,
  loadingSessions,
  loadingAuditLogs,
  isHydratingSettings,
  isPersistingSettings,
  settingsError,
  onClose,
  onPlatformThemeChange,
  onLogout,
  onRefreshRiskBlocks,
  onLiftRiskBlock,
  onRefreshSessions,
  onLogoutAll,
  onRefreshAuditLogs,
  onRevokeSession,
  changePhoneCaptchaChallenge,
  onSendChangePhoneCode,
  onChangePhone,
}: AccountModalProps) {
  const [activeSection, setActiveSection] =
    useState<PrimarySettingsSection | null>(
      normalizeSettingsSection(initialSection),
    );
  const [isChangePhonePanelOpen, setIsChangePhonePanelOpen] = useState(false);
  const [phone, setPhone] = useState('');
  const [code, setCode] = useState('');
  const [captchaAnswer, setCaptchaAnswer] = useState('');
  const [changePhoneError, setChangePhoneError] = useState('');
  const [changePhoneHint, setChangePhoneHint] = useState('');
  const [accountNotice, setAccountNotice] = useState('');
  const [sendingCode, setSendingCode] = useState(false);
  const [changingPhone, setChangingPhone] = useState(false);
  const [cooldownSeconds, setCooldownSeconds] = useState(0);
  const settingsHomeRef = useRef<HTMLDivElement | null>(null);
  const sectionTriggerRef = useRef<HTMLButtonElement | null>(null);
  const changePhoneTriggerRef = useRef<HTMLButtonElement | null>(null);

  const focusAfterNextPaint = useCallback((element: HTMLElement | null) => {
    if (!element) {
      return;
    }

    window.requestAnimationFrame(() => {
      if (element.isConnected) {
        element.focus();
      }
    });
  }, []);

  const resetChangePhoneDraft = useCallback(() => {
    setPhone('');
    setCode('');
    setCaptchaAnswer('');
    setChangePhoneError('');
    setChangePhoneHint('');
    setCooldownSeconds(0);
  }, []);

  useEffect(() => {
    if (!isOpen) {
      return;
    }

    setActiveSection(normalizeSettingsSection(initialSection));
    setIsChangePhonePanelOpen(false);
    setAccountNotice('');
    sectionTriggerRef.current = null;
    changePhoneTriggerRef.current = null;
    resetChangePhoneDraft();
  }, [initialSection, isOpen, resetChangePhoneDraft]);

  useEffect(() => {
    const settingsHome = settingsHomeRef.current;
    if (!settingsHome) {
      return;
    }

    settingsHome.toggleAttribute('inert', activeSection !== null);

    return () => {
      settingsHome.removeAttribute('inert');
    };
  }, [activeSection]);

  useEffect(() => {
    if (cooldownSeconds <= 0) {
      return;
    }

    const timeoutId = window.setTimeout(() => {
      setCooldownSeconds((current) => Math.max(0, current - 1));
    }, 1000);

    return () => {
      window.clearTimeout(timeoutId);
    };
  }, [cooldownSeconds]);

  const closeSectionPanel = useCallback(() => {
    const sectionTrigger = sectionTriggerRef.current;
    setIsChangePhonePanelOpen(false);
    setActiveSection(null);
    resetChangePhoneDraft();
    focusAfterNextPaint(sectionTrigger);
  }, [focusAfterNextPaint, resetChangePhoneDraft]);

  const closeChangePhonePanel = useCallback(() => {
    const changePhoneTrigger = changePhoneTriggerRef.current;
    setIsChangePhonePanelOpen(false);
    resetChangePhoneDraft();
    focusAfterNextPaint(changePhoneTrigger);
  }, [focusAfterNextPaint, resetChangePhoneDraft]);

  if (!isOpen) {
    return null;
  }

  const themeStatusText = settingsError
    ? settingsError
    : isHydratingSettings
      ? '正在读取平台设置...'
      : isPersistingSettings
        ? '正在同步平台设置...'
        : '平台设置已同步';

  const accountSummaryCards = [
    ['登录方式', resolveLoginMethodLabel(user.loginMethod)],
    ['手机号', user.phoneNumberMasked || '未绑定'],
    ['微信绑定', user.wechatBound ? '已绑定' : '未绑定'],
  ] as const;

  const sectionSummaries: Record<PrimarySettingsSection, string> = {
    appearance:
      platformTheme === 'dark' ? '当前使用暗色主题。' : '当前使用亮色主题。',
    account:
      user.phoneNumberMasked || user.wechatBound
        ? '查看身份、安全状态、登录设备与操作记录。'
        : '查看账号绑定状态与安全记录。',
  };

  return (
    <div
      className={`platform-theme platform-theme--${platformTheme} platform-overlay fixed inset-0 z-[70] flex items-end justify-center overflow-hidden px-4 sm:items-center`}
      style={{
        paddingTop: 'calc(env(safe-area-inset-top, 0px) + 1rem)',
        paddingBottom: 'calc(env(safe-area-inset-bottom, 0px) + 1rem)',
      }}
      onClick={onClose}
    >
      <div
        className="platform-auth-card relative flex h-[min(100%,calc(100vh-2rem))] w-full max-w-5xl min-h-0 flex-col overflow-hidden rounded-[28px] p-5 sm:p-6"
        role="dialog"
        aria-modal="true"
        aria-label="设置与账号安全"
        style={{ maxHeight: ACCOUNT_MODAL_MAX_HEIGHT }}
        onClick={(event) => event.stopPropagation()}
      >
        <div className="flex items-start justify-between gap-4">
          <div>
            <div className="text-2xl font-semibold text-[var(--platform-text-strong)]">
              设置与账号安全
            </div>
          </div>
          <button
            type="button"
            className="platform-button platform-button--ghost min-h-0 rounded-full px-3 py-1.5 text-xs"
            onClick={onClose}
          >
            关闭
          </button>
        </div>

        <div className="mt-5 min-h-0 flex-1 overflow-y-auto overscroll-y-contain pr-1">
          <div ref={settingsHomeRef} className="flex min-h-0 flex-col gap-4">
            <div className="grid gap-3 sm:grid-cols-2">
              {SETTINGS_SECTIONS.map((section) => (
                <SettingsEntryCard
                  key={section.id}
                  label={section.label}
                  detail={section.detail}
                  summary={sectionSummaries[section.id]}
                  onClick={(trigger) => {
                    sectionTriggerRef.current = trigger;
                    setAccountNotice('');
                    setActiveSection(section.id);
                  }}
                />
              ))}
            </div>
          </div>
        </div>

        {activeSection === 'appearance' ? (
          <OverlayPanel
            eyebrow="平台偏好"
            title="主题外观"
            description="切换平台亮色或暗色主题。"
            onBack={closeSectionPanel}
            onClose={onClose}
          >
            <div className="flex min-h-0 flex-col gap-4">
              <div className="grid gap-3 md:grid-cols-2">
                <ThemeOptionCard
                  active={platformTheme === 'light'}
                  title="亮色主题"
                  detail="暖白底面板，粉橘强调。"
                  previewClassName="bg-[radial-gradient(circle_at_top,rgba(255,255,255,0.28),transparent_30%),linear-gradient(135deg,#fff8fb_0%,#ffe9ee_52%,#ffd8cb_100%)] border border-white/70"
                  onClick={() => onPlatformThemeChange('light')}
                />
                <ThemeOptionCard
                  active={platformTheme === 'dark'}
                  title="暗色主题"
                  detail="保留原有紫蓝深色方案。"
                  previewClassName="bg-[radial-gradient(circle_at_top_left,rgba(129,140,248,0.28),transparent_34%),linear-gradient(180deg,#17192b_0%,#0b0d15_100%)] border border-white/10"
                  onClick={() => onPlatformThemeChange('dark')}
                />
              </div>

              <div className="platform-subpanel rounded-2xl px-4 py-4">
                <div className="flex flex-wrap items-center justify-between gap-3">
                  <div>
                    <div className="text-sm font-semibold text-[var(--platform-text-strong)]">
                      当前主题
                    </div>
                    <div className="mt-1 text-sm text-[var(--platform-text-base)]">
                      {platformTheme === 'dark' ? '暗色主题' : '亮色主题'}
                    </div>
                  </div>
                  <span className="platform-pill platform-pill--neutral px-3 py-1 text-[11px]">
                    {themeStatusText}
                  </span>
                </div>
              </div>
            </div>
          </OverlayPanel>
        ) : null}

        {activeSection === 'account' ? (
          <OverlayPanel
            eyebrow="身份信息"
            title="账号信息"
            description="统一查看身份、安全状态、登录设备与最近操作。"
            onBack={closeSectionPanel}
            onClose={onClose}
          >
            <div className="flex min-h-0 flex-col gap-4">
              {accountNotice ? (
                <div className="platform-banner platform-banner--success text-sm">
                  {accountNotice}
                </div>
              ) : null}

              <div className="grid gap-3 sm:grid-cols-2">
                {accountSummaryCards.map(([label, value]) => (
                  <div
                    key={label}
                    className="platform-subpanel rounded-2xl px-4 py-3"
                  >
                    <div className="text-xs tracking-[0.18em] text-[var(--platform-text-soft)]">
                      {label}
                    </div>
                    <div className="mt-2 text-sm font-semibold text-[var(--platform-text-strong)]">
                      {value}
                    </div>
                  </div>
                ))}
              </div>

              <div className="grid gap-3 sm:grid-cols-2">
                <button
                  type="button"
                  className="platform-button platform-button--ghost h-11 w-full text-sm"
                  onClick={() => {
                    void onLogout();
                  }}
                >
                  退出登录
                </button>
                <button
                  type="button"
                  className="platform-button platform-button--danger h-11 w-full text-sm"
                  onClick={() => {
                    void onLogoutAll();
                  }}
                >
                  退出全部设备
                </button>
              </div>

              <div className="platform-subpanel rounded-2xl px-4 py-4">
                <div className="flex items-center justify-between gap-3">
                  <div>
                    <div className="text-base font-semibold text-[var(--platform-text-strong)]">
                      更换手机号
                    </div>
                    <div className="mt-1 text-sm text-[var(--platform-text-base)]">
                      在独立面板中输入新的手机号与验证码。
                    </div>
                  </div>
                  <button
                    type="button"
                    className="platform-button platform-button--ghost min-h-0 rounded-full px-3 py-1.5 text-[11px]"
                    onClick={(event) => {
                      changePhoneTriggerRef.current = event.currentTarget;
                      setAccountNotice('');
                      resetChangePhoneDraft();
                      setIsChangePhonePanelOpen(true);
                    }}
                  >
                    更换手机号
                  </button>
                </div>
              </div>

              <div className="platform-subpanel rounded-2xl px-4 py-4">
                <div className="flex flex-wrap items-start justify-between gap-3">
                  <div>
                    <div className="text-base font-semibold text-[var(--platform-text-strong)]">
                      安全状态
                    </div>
                    <div className="mt-1 text-sm text-[var(--platform-text-base)]">
                      查看当前生效中的账号保护与限制。
                    </div>
                  </div>
                  <button
                    type="button"
                    className="platform-button platform-button--ghost min-h-0 rounded-full px-3 py-1.5 text-[11px]"
                    onClick={() => {
                      void onRefreshRiskBlocks();
                    }}
                  >
                    刷新
                  </button>
                </div>

                <div className="mt-4 grid gap-3">
                  {loadingRiskBlocks ? (
                    <div className="rounded-2xl border border-[var(--platform-subpanel-border)] px-4 py-3 text-sm text-[var(--platform-text-soft)]">
                      正在读取安全状态...
                    </div>
                  ) : riskBlocks.length > 0 ? (
                    riskBlocks.map((block) => (
                      <div
                        key={`${block.scopeType}:${block.expiresAt}`}
                        className="platform-banner platform-banner--warning text-sm"
                      >
                        <div className="flex items-center justify-between gap-3">
                          <span>{block.title}</span>
                          <span className="text-xs">
                            剩余约{' '}
                            {Math.max(1, Math.ceil(block.remainingSeconds / 60))}{' '}
                            分钟
                          </span>
                        </div>
                        <div className="mt-2 text-xs leading-5">
                          {block.detail}
                        </div>
                        <button
                          type="button"
                          className="platform-button platform-button--secondary mt-3 min-h-0 h-9 px-3 text-xs"
                          onClick={() => {
                            void onLiftRiskBlock(block.scopeType);
                          }}
                        >
                          解除保护
                        </button>
                      </div>
                    ))
                  ) : (
                    <div className="rounded-2xl border border-[var(--platform-subpanel-border)] px-4 py-3 text-sm text-[var(--platform-text-soft)]">
                      当前没有生效中的安全限制。
                    </div>
                  )}
                </div>
              </div>

              <div className="platform-subpanel rounded-2xl px-4 py-4">
                <div className="flex flex-wrap items-start justify-between gap-3">
                  <div>
                    <div className="text-base font-semibold text-[var(--platform-text-strong)]">
                      登录设备
                    </div>
                    <div className="mt-1 text-sm text-[var(--platform-text-base)]">
                      查看当前账号的设备会话与登录状态。
                    </div>
                  </div>
                  <button
                    type="button"
                    className="platform-button platform-button--ghost min-h-0 rounded-full px-3 py-1.5 text-[11px]"
                    onClick={() => {
                      void onRefreshSessions();
                    }}
                  >
                    刷新
                  </button>
                </div>

                <div className="mt-4 grid gap-3">
                  {loadingSessions ? (
                    <div className="rounded-2xl border border-[var(--platform-subpanel-border)] px-4 py-3 text-sm text-[var(--platform-text-soft)]">
                      正在读取当前登录设备...
                    </div>
                  ) : sessions.length > 0 ? (
                    sessions.map((session) => (
                      <div
                        key={session.sessionId}
                        className="rounded-2xl border border-[var(--platform-subpanel-border)] px-4 py-3 text-sm text-[var(--platform-text-base)]"
                      >
                        <div className="flex items-center justify-between gap-3">
                          <span>{session.clientLabel}</span>
                          <span className="platform-pill platform-pill--success px-2.5 py-1 text-[10px]">
                            {session.isCurrent ? '当前设备' : '已登录'}
                          </span>
                        </div>
                        <div className="mt-2 text-xs leading-5 text-[var(--platform-text-soft)]">
                          最近活跃：{formatSessionTime(session.lastSeenAt)}
                        </div>
                        <div className="text-xs leading-5 text-[var(--platform-text-soft)]">
                          到期时间：{formatSessionTime(session.expiresAt)}
                        </div>
                        {session.ipMasked ? (
                          <div className="text-xs leading-5 text-[var(--platform-text-soft)]">
                            IP：{session.ipMasked}
                          </div>
                        ) : null}
                        {!session.isCurrent ? (
                          <button
                            type="button"
                            className="platform-button platform-button--danger mt-3 min-h-0 h-9 px-3 text-xs"
                            onClick={() => {
                              void onRevokeSession(session.sessionId);
                            }}
                          >
                            踢下线
                          </button>
                        ) : null}
                      </div>
                    ))
                  ) : (
                    <div className="rounded-2xl border border-[var(--platform-subpanel-border)] px-4 py-3 text-sm text-[var(--platform-text-soft)]">
                      暂无可展示的登录设备。
                    </div>
                  )}
                </div>
              </div>

              <div className="platform-subpanel rounded-2xl px-4 py-4">
                <div className="flex flex-wrap items-start justify-between gap-3">
                  <div>
                    <div className="text-base font-semibold text-[var(--platform-text-strong)]">
                      操作记录
                    </div>
                    <div className="mt-1 text-sm text-[var(--platform-text-base)]">
                      查看最近的账号登录与安全动作。
                    </div>
                  </div>
                  <button
                    type="button"
                    className="platform-button platform-button--ghost min-h-0 rounded-full px-3 py-1.5 text-[11px]"
                    onClick={() => {
                      void onRefreshAuditLogs();
                    }}
                  >
                    刷新
                  </button>
                </div>

                <div className="mt-4 grid gap-3">
                  {loadingAuditLogs ? (
                    <div className="rounded-2xl border border-[var(--platform-subpanel-border)] px-4 py-3 text-sm text-[var(--platform-text-soft)]">
                      正在读取账号操作记录...
                    </div>
                  ) : auditLogs.length > 0 ? (
                    auditLogs.map((log) => (
                      <div
                        key={log.id}
                        className="rounded-2xl border border-[var(--platform-subpanel-border)] px-4 py-3 text-sm text-[var(--platform-text-base)]"
                      >
                        <div className="flex items-center justify-between gap-3">
                          <span>{log.title}</span>
                          <span className="text-xs text-[var(--platform-text-soft)]">
                            {formatSessionTime(log.createdAt)}
                          </span>
                        </div>
                        <div className="mt-2 text-xs leading-5 text-[var(--platform-text-soft)]">
                          {log.detail}
                        </div>
                        {log.ipMasked ? (
                          <div className="text-xs leading-5 text-[var(--platform-text-soft)]">
                            IP：{log.ipMasked}
                          </div>
                        ) : null}
                      </div>
                    ))
                  ) : (
                    <div className="rounded-2xl border border-[var(--platform-subpanel-border)] px-4 py-3 text-sm text-[var(--platform-text-soft)]">
                      暂无账号操作记录。
                    </div>
                  )}
                </div>
              </div>
            </div>

            {isChangePhonePanelOpen ? (
              <OverlayPanel
                eyebrow="手机号换绑"
                title="绑定新手机号"
                description="输入新手机号并完成验证码验证。"
                onBack={closeChangePhonePanel}
                onClose={onClose}
              >
                <div className="grid gap-3">
                  <label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
                    <span>新手机号</span>
                    <input
                      className="platform-input h-11"
                      value={phone}
                      inputMode="numeric"
                      placeholder="13800000000"
                      onChange={(event) => setPhone(event.target.value)}
                    />
                  </label>

                  <label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
                    <span>验证码</span>
                    <div className="flex gap-3">
                      <input
                        className="platform-input h-11 min-w-0 flex-1"
                        value={code}
                        inputMode="numeric"
                        placeholder="输入验证码"
                        onChange={(event) => setCode(event.target.value)}
                      />
                      <button
                        type="button"
                        disabled={
                          sendingCode || cooldownSeconds > 0 || !phone.trim()
                        }
                        className="platform-button platform-button--secondary h-11 shrink-0 px-4 text-sm disabled:cursor-not-allowed disabled:opacity-55"
                        onClick={() => {
                          void (async () => {
                            setSendingCode(true);
                            setChangePhoneError('');
                            try {
                              const result = await onSendChangePhoneCode(
                                phone,
                                {
                                  challengeId:
                                    changePhoneCaptchaChallenge?.challengeId,
                                  answer: captchaAnswer,
                                },
                              );
                              setCooldownSeconds(result.cooldownSeconds);
                              setChangePhoneHint(
                                `验证码已发送，有效期约 ${Math.max(1, Math.round(result.expiresInSeconds / 60))} 分钟。`,
                              );
                              setCaptchaAnswer('');
                            } catch (error) {
                              setChangePhoneError(
                                error instanceof Error
                                  ? error.message
                                  : '发送验证码失败，请稍后再试。',
                              );
                              setChangePhoneHint('');
                            } finally {
                              setSendingCode(false);
                            }
                          })();
                        }}
                      >
                        {sendingCode
                          ? '发送中...'
                          : cooldownSeconds > 0
                            ? `${cooldownSeconds}s`
                            : '获取验证码'}
                      </button>
                    </div>
                  </label>

                  {changePhoneHint ? (
                    <div className="platform-banner platform-banner--success text-sm">
                      {changePhoneHint}
                    </div>
                  ) : null}

                  <CaptchaChallengeField
                    challenge={changePhoneCaptchaChallenge}
                    answer={captchaAnswer}
                    onAnswerChange={setCaptchaAnswer}
                  />

                  {changePhoneError ? (
                    <div className="platform-banner platform-banner--danger text-sm">
                      {changePhoneError}
                    </div>
                  ) : null}

                  <button
                    type="button"
                    disabled={changingPhone || !phone.trim() || !code.trim()}
                    className="platform-button platform-button--primary h-11 px-4 text-sm disabled:cursor-not-allowed disabled:opacity-60"
                    onClick={() => {
                      void (async () => {
                        setChangingPhone(true);
                        setChangePhoneError('');
                        try {
                          await onChangePhone(phone, code);
                          setAccountNotice('手机号已更新。');
                          closeChangePhonePanel();
                        } catch (error) {
                          setChangePhoneError(
                            error instanceof Error
                              ? error.message
                              : '更换手机号失败，请稍后再试。',
                          );
                        } finally {
                          setChangingPhone(false);
                        }
                      })();
                    }}
                  >
                    {changingPhone ? '提交中...' : '确认更换手机号'}
                  </button>
                </div>
              </OverlayPanel>
            ) : null}
          </OverlayPanel>
        ) : null}
      </div>
    </div>
  );
}
