import { useEffect, useMemo, useRef, useState } from 'react'
import type { FormEvent, MouseEvent, ReactNode } from 'react'
import {
  BarChart3,
  FileText,
  KeyRound,
  Lock,
  PartyPopper,
  Users,
} from 'lucide-react'
import boyaLogo from './assets/boya-logo.png'
import confetti from 'canvas-confetti'
import './App.css'
import OfficialHomePage from './OfficialHomePage'

const API_BASE = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080/api'
const AUTH_STORAGE_KEY = 'assessment-auth'
const DEFAULT_PROJECT_SLUG = 'davos'
const DEFAULT_PROJECT_ASSESSMENT_IDS = ['SCL90-2078', 'SDS-2082', 'SAS-2083']

type Screen = 'home' | 'landing' | 'login' | 'register' | 'profile' | 'catalog' | 'assessment' | 'celebrate' | 'admin' | 'adminLegacy' | 'super'
type ProjectAuth = { id: number; slug: string; role: 'PROJECT_ADMIN' | 'PARTICIPANT'; hasProfile: boolean }
type AuthUser = { id: number; email: string; role: 'USER' | 'PROJECT_ADMIN' | 'PLATFORM_SUPER_ADMIN'; globalRole?: 'USER' | 'PLATFORM_SUPER_ADMIN'; token?: string; hasProfile: boolean; project?: ProjectAuth; defaultRoute?: string }
type ProjectConfig = { id: number; slug: string; name: string; landingTitle: string; landingDescription: string; reportTitle: string; logoUrl: string; enabled: boolean }
type SuperProject = ProjectConfig & { url: string; adminUrl: string; createdAt: string; updatedAt: string }
type PlatformAdmin = { id: number; email: string; role: string; enabled: boolean; createdAt: string }
type HomepageAppointment = { id: number; name: string; organization: string; phone: string; wechat: string; city: string; sourcePath: string; createdAt: string }
type Question = { id: number; code: string; dimension: string; prompt: string; sortOrder: number }
type Assessment = { testPaperId: string; title: string; description: string; questions: Question[]; completed?: boolean; canRetake?: boolean }
type UserAssessmentStatus = { testPaperId: string; title: string; completed: boolean; canRetake: boolean; submissionCount: number; lastSubmittedAt: string }
type Dashboard = { users: number; submissions: number; reports: number; invitationCodes: number }
type SubmissionDetail = {
  submission: Record<string, string | number>
  user: Record<string, string | number | boolean>
  answers: Array<{ questionCode: string; prompt: string; value: number }>
  reportId?: number
  reportSummary: string
  suggestions: string
}
type UserAssessmentReportOverview = {
  user: Record<string, string | number | boolean>
  assessments: Array<{
    testPaperId: string
    title: string
    completed: boolean
    submissionCount: number
    latestSubmittedAt: string
    latestSubmissionId?: number
    reportId?: number
    summary: string
    suggestions: string
  }>
  comprehensiveEvaluation: string
  comprehensiveReady?: boolean
  completionProgress?: string
  comprehensiveScore?: number
  comprehensiveLevel?: string
  comprehensiveTriggers?: string[]
}

type AdminState = {
  dashboard?: Dashboard
  users: Array<Record<string, string | number | boolean>>
  submissions: Array<Record<string, string | number>>
  reports: Array<Record<string, string | number>>
  invites: Array<Record<string, string | number | boolean>>
  logs: Array<Record<string, string | number>>
}
type LegalDocument = { type: 'USER_AGREEMENT' | 'PRIVACY_POLICY'; title: string; version: string; contentMd: string; updatedAt: string }
type LegalBundle = { userAgreement: LegalDocument; privacyPolicy: LegalDocument }
type ConsentRow = { id: number; userId: number; email: string; consentType: string; version: string; granted: boolean; ipAddress: string; createdAt: string }
type ExportFormat = 'csv' | 'xlsx' | 'pdf' | 'docx'

function renderMarkdownToHtml(markdown: string): string {
  const escapeHtml = (input: string) =>
    input
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;')

  const inline = (text: string) =>
    text
      .replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, '<a href="$2" target="_blank" rel="noreferrer">$1</a>')
      .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
      .replace(/\*([^*]+)\*/g, '<em>$1</em>')

  const lines = escapeHtml(markdown || '').split('\n')
  const chunks: string[] = []
  let inList = false
  for (const raw of lines) {
    const line = raw.trim()
    if (!line) {
      if (inList) {
        chunks.push('</ul>')
        inList = false
      }
      continue
    }
    if (line.startsWith('- ')) {
      if (!inList) {
        chunks.push('<ul>')
        inList = true
      }
      chunks.push(`<li>${inline(line.slice(2))}</li>`)
      continue
    }
    if (inList) {
      chunks.push('</ul>')
      inList = false
    }
    if (line.startsWith('### ')) chunks.push(`<h3>${inline(line.slice(4))}</h3>`)
    else if (line.startsWith('## ')) chunks.push(`<h2>${inline(line.slice(3))}</h2>`)
    else if (line.startsWith('# ')) chunks.push(`<h1>${inline(line.slice(2))}</h1>`)
    else chunks.push(`<p>${inline(line)}</p>`)
  }
  if (inList) chunks.push('</ul>')
  return chunks.join('')
}

function normalizeLegalBundle(raw: unknown): LegalBundle | null {
  if (!raw || typeof raw !== 'object') return null
  const data = raw as Record<string, unknown>
  const ua = data.userAgreement as Record<string, unknown> | undefined
  const pp = data.privacyPolicy as Record<string, unknown> | undefined
  if (!ua || !pp) return null
  return {
    userAgreement: {
      type: 'USER_AGREEMENT',
      title: String(ua.title || '用户协议'),
      version: String(ua.version || 'V1.0'),
      contentMd: String(ua.contentMd || ''),
      updatedAt: String(ua.updatedAt || ''),
    },
    privacyPolicy: {
      type: 'PRIVACY_POLICY',
      title: String(pp.title || '隐私条款'),
      version: String(pp.version || 'V1.0'),
      contentMd: String(pp.contentMd || ''),
      updatedAt: String(pp.updatedAt || ''),
    },
  }
}

const emptyAdmin: AdminState = { users: [], submissions: [], reports: [], invites: [], logs: [] }
const fallbackProjectConfig: ProjectConfig = {
  id: 0,
  slug: DEFAULT_PROJECT_SLUG,
  name: '达沃斯会议心理测评系统',
  landingTitle: '达沃斯心理测评',
  landingDescription: '围绕国际会议高密度议程与高强度互动场景，建立标准化心理测评流程，用于识别压力水平、适应状态与支持需求，为后续沟通协同和健康支持提供依据。',
  reportTitle: '达沃斯会议心理测评报告',
  logoUrl: '',
  enabled: true,
}

function parseRoute() {
  const parts = window.location.pathname.split('/').filter(Boolean)
  if (parts.length === 0) return { slug: DEFAULT_PROJECT_SLUG, screen: 'home' as Screen }
  if (parts[0] === 'super') return { slug: DEFAULT_PROJECT_SLUG, screen: 'super' as Screen }
  if (parts[0] === 'admin') {
    window.history.replaceState({}, '', `/${DEFAULT_PROJECT_SLUG}/admin`)
    return { slug: DEFAULT_PROJECT_SLUG, screen: 'admin' as Screen }
  }
  const slug = parts[0] || DEFAULT_PROJECT_SLUG
  const section = parts[1] || ''
  if (section === 'admin') return { slug, screen: 'admin' as Screen }
  if (section === 'admin-legacy') return { slug, screen: 'adminLegacy' as Screen }
  if (section === 'login') return { slug, screen: 'login' as Screen }
  if (section === 'register') return { slug, screen: 'register' as Screen }
  if (section === 'profile') return { slug, screen: 'profile' as Screen }
  if (section === 'catalog') return { slug, screen: 'catalog' as Screen }
  if (section === 'assessment') return { slug, screen: 'assessment' as Screen }
  return { slug, screen: 'landing' as Screen }
}

function pathFor(slug: string, screen: Screen) {
  if (screen === 'home') return '/'
  if (screen === 'super') return '/super'
  if (screen === 'landing') return `/${slug}`
  if (screen === 'admin') return `/${slug}/admin`
  if (screen === 'adminLegacy') return `/${slug}/admin-legacy`
  return `/${slug}/${screen}`
}

function scopedApiPath(slug: string, path: string) {
  if (path.startsWith('/admin')) return `/projects/${slug}${path}`
  if (path.startsWith('/assessment')) return `/projects/${slug}${path}`
  if (path.startsWith('/me/profile')) return `/projects/${slug}${path}`
  if (path.startsWith('/reports')) return `/projects/${slug}${path}`
  if (path.startsWith('/legal')) return `/projects/${slug}${path}`
  return path
}

function App() {
  const initialRoute = useMemo(() => parseRoute(), [])
  const [screen, setScreen] = useState<Screen>(initialRoute.screen)
  const [projectSlug, setProjectSlug] = useState(initialRoute.slug)
  const [projectConfig, setProjectConfig] = useState<ProjectConfig>(fallbackProjectConfig)
  const [auth, setAuth] = useState<AuthUser | null>(() => {
    const saved = localStorage.getItem(AUTH_STORAGE_KEY)
    return saved ? JSON.parse(saved) : null
  })
  const [message, setMessage] = useState('')
  const [loading, setLoading] = useState(false)
  const [assessment, setAssessment] = useState<Assessment | null>(null)
  const [assessments, setAssessments] = useState<Assessment[]>([])
  const [answers, setAnswers] = useState<Record<string, number>>({})
  const [admin, setAdmin] = useState<AdminState>(emptyAdmin)
  const [adminTab, setAdminTab] = useState<'users' | 'ops' | 'monitor' | 'protocol'>('users')
  const [adminMenuOpen, setAdminMenuOpen] = useState(false)
  const isPlatformSuper = auth?.globalRole === 'PLATFORM_SUPER_ADMIN' || auth?.role === 'PLATFORM_SUPER_ADMIN'
  const isAdmin = isPlatformSuper || auth?.project?.role === 'PROJECT_ADMIN' || auth?.role === 'PROJECT_ADMIN'
  const logoReturnsHome = screen !== 'login' && screen !== 'register'
  const mainClassName = screen === 'landing'
    ? 'landing-main'
    : (screen === 'admin' || screen === 'adminLegacy')
      ? 'admin-main'
      : screen === 'super'
        ? 'super-main'
        : undefined

  useEffect(() => {
    if (auth?.token) localStorage.setItem(AUTH_STORAGE_KEY, JSON.stringify(auth))
  }, [auth])

  useEffect(() => {
    if (screen === 'home' || screen === 'super') return
    let active = true
    fetch(`${API_BASE}/projects/${projectSlug}/public-config`)
      .then(async (res) => {
        const raw = await res.text()
        const data = raw ? JSON.parse(raw) : null
        if (!res.ok) throw new Error(data?.message || '项目配置加载失败')
        if (active) setProjectConfig(data)
      })
      .catch((error) => {
        if (active) setMessage(error instanceof Error ? error.message : '项目配置加载失败')
      })
    return () => { active = false }
  }, [projectSlug, screen])

  const api = async <T,>(path: string, init: RequestInit = {}): Promise<T> => {
    const response = await fetch(`${API_BASE}${scopedApiPath(projectSlug, path)}`, {
      ...init,
      headers: {
        'Content-Type': 'application/json',
        ...(auth?.token ? { Authorization: `Bearer ${auth.token}` } : {}),
        ...init.headers,
      },
    })
    const raw = await response.text()
    const data = raw ? JSON.parse(raw) : null
    if (!response.ok) {
      throw new Error((data && typeof data === 'object' && 'message' in data ? String((data as { message?: string }).message) : '') || '请求失败')
    }
    return (data ?? ({} as T)) as T
  }

  const goto = (target: Screen) => {
    setMessage('')
    setScreen(target)
    window.history.pushState({}, '', pathFor(projectSlug, target))
  }

  const loadAssessments = async () => {
    setLoading(true)
    try {
      setAssessments(await api<Assessment[]>('/assessment/list'))
    } catch (error) {
      setAssessments([])
      setMessage(error instanceof Error ? error.message : '量表加载失败，请重试')
    } finally {
      setLoading(false)
    }
  }

  const loadAdmin = async () => {
    if (!isAdmin) return
    setLoading(true)
    try {
      const [dashboard, users, submissions, reports, invites, logs] = await Promise.all([
        api<Dashboard>('/admin/dashboard'),
        api<AdminState['users']>('/admin/users'),
        api<AdminState['submissions']>('/admin/submissions'),
        api<AdminState['reports']>('/admin/reports'),
        api<AdminState['invites']>('/admin/invitation-codes'),
        api<AdminState['logs']>('/admin/audit-logs'),
      ])
      setAdmin({ dashboard, users, submissions, reports, invites, logs })
    } catch (error) {
      setMessage(error instanceof Error ? error.message : '后台数据加载失败，请重试')
    } finally {
      setLoading(false)
    }
  }

  /* eslint-disable react-hooks/set-state-in-effect, react-hooks/exhaustive-deps */
  // The effect synchronizes route-like screen changes with remote API data.
  useEffect(() => {
    if (!auth?.token) return
    if (screen === 'catalog') void loadAssessments()
    if ((screen === 'admin' || screen === 'adminLegacy') && isAdmin) void loadAdmin()
  }, [screen, auth?.token, auth?.role])
  /* eslint-enable react-hooks/set-state-in-effect, react-hooks/exhaustive-deps */

  const logout = () => {
    localStorage.removeItem(AUTH_STORAGE_KEY)
    setAuth(null)
    goto(screen === 'super' ? 'super' : 'landing')
  }

  if (screen === 'home') {
    return <OfficialHomePage />
  }

  return (
    <main className={mainClassName}>
      <nav className="topbar">
        <button className="brand" type="button" onClick={() => { if (logoReturnsHome) goto('landing') }}>
          <img src={boyaLogo} alt="博雅" />
        </button>
        <div className="nav-actions">
          {screen === 'admin' && isAdmin && (
            <>
              <span className="topbar-metric"><strong>{admin.dashboard?.users || 0}</strong>用户总数</span>
              <span className="topbar-metric"><strong>{admin.dashboard?.submissions || 0}</strong>答卷总数</span>
              <span className="topbar-metric"><strong>{admin.dashboard?.reports || 0}</strong>报告总数</span>
              <span className="topbar-metric"><strong>{admin.dashboard?.invitationCodes || 0}</strong>邀请码数量</span>
            </>
          )}
          {auth && <button type="button" onClick={() => goto(isPlatformSuper && screen === 'super' ? 'super' : (isAdmin ? 'admin' : 'catalog'))}>工作台</button>}
          {screen === 'admin' && isAdmin && (
            <>
              <div className="menu-wrap admin-menu-wrap">
                <button type="button" onClick={() => setAdminMenuOpen((v) => !v)}>功能</button>
                {adminMenuOpen && (
                  <div className="menu-dropdown">
                    <button type="button" onClick={() => { setAdminTab('users'); setAdminMenuOpen(false) }}>用户管理</button>
                    <button type="button" onClick={() => { setAdminTab('ops'); setAdminMenuOpen(false) }}>系统运维</button>
                    <button type="button" onClick={() => { setAdminTab('monitor'); setAdminMenuOpen(false) }}>数据监控</button>
                    <button type="button" onClick={() => { setAdminTab('protocol'); setAdminMenuOpen(false) }}>协议管理</button>
                  </div>
                )}
              </div>
              <button type="button" onClick={loadAdmin}>刷新</button>
            </>
          )}
          {auth ? (
            <button className="primary small" type="button" onClick={logout}>退出</button>
          ) : (
            <>
              <button type="button" onClick={() => goto('login')}>登录</button>
              <button className="primary small" type="button" onClick={() => goto('register')}>注册</button>
            </>
          )}
        </div>
      </nav>

      {screen === 'landing' && <Landing project={projectConfig} onStart={() => goto('register')} />}
      {screen === 'super' && !isPlatformSuper && <AuthForm mode="login" target="SUPER" projectSlug={projectSlug} message={message} onMessage={setMessage} onAuthed={setAuth} onDone={() => goto('super')} onSwitch={() => goto('login')} />}
      {screen === 'super' && isPlatformSuper && <SuperAdminPage auth={auth} setProjectSlug={setProjectSlug} />}
      {screen === 'login' && <AuthForm mode="login" target="PROJECT" projectSlug={projectSlug} message={message} onMessage={setMessage} onAuthed={setAuth} onDone={(user) => {
        if (user.project?.slug && user.project.slug !== projectSlug) setProjectSlug(user.project.slug)
        goto(user.project?.role === 'PROJECT_ADMIN' || user.role === 'PROJECT_ADMIN' ? (window.location.pathname.includes('/admin-legacy') ? 'adminLegacy' : 'admin') : (user.hasProfile ? 'catalog' : 'profile'))
      }} onSwitch={() => goto('register')} />}
      {screen === 'register' && <AuthForm mode="register" target="PROJECT" projectSlug={projectSlug} message={message} onMessage={setMessage} onAuthed={setAuth} onDone={() => goto('profile')} onSwitch={() => goto('login')} />}
      {screen === 'profile' && <ProfileForm api={api} onDone={() => goto('catalog')} />}
      {screen === 'catalog' && <Catalog assessments={assessments} loading={loading} onSelect={(item) => { setAssessment(item); setAnswers({}); goto('assessment') }} />}
      {screen === 'assessment' && assessment && <AssessmentPage assessment={assessment} answers={answers} setAnswers={setAnswers} api={api} onDone={() => goto('celebrate')} />}
      {screen === 'celebrate' && <CelebratePage onDone={() => goto('catalog')} />}
      {screen === 'admin' && isAdmin && <AdminV2Page admin={admin} setAdmin={setAdmin} api={api} reload={loadAdmin} token={auth?.token || ''} loading={loading} adminTab={adminTab} projectSlug={projectSlug} />}
      {screen === 'adminLegacy' && isAdmin && <AdminLegacyPage admin={admin} setAdmin={setAdmin} api={api} reload={loadAdmin} token={auth?.token || ''} loading={loading} />}
      {(screen === 'admin' || screen === 'adminLegacy') && !isAdmin && <EmptyState title="需要管理员权限" text="请使用管理员账号登录后进入后台。" />}
    </main>
  )
}

function Landing({ project, onStart }: { project: ProjectConfig; onStart: () => void }) {
  return (
    <>
      <section className="hero-section">
        <div className="hero-copy">
          <h1>{project.landingTitle}</h1>
          <p className="lead">{project.landingDescription}</p>
          <div className="hero-actions">
            <button className="primary hero-cta" type="button" onClick={onStart}>开始测评</button>
          </div>
        </div>
      </section>
      <footer className="landing-footer">
        <div className="footer-grid">
          <section>
            <h3>联系我们</h3>
            <p>商务合作：boyaxzgh@163.com</p>
            <p>联系方式：0411-84791865</p>
          </section>
          <section>
            <h3>机构信息</h3>
            <p>博雅学业职业发展研究中心</p>
            <p>大连市大数据研究院</p>
          </section>
        </div>
        <div className="footer-bottom">
          <span>© {new Date().getFullYear()} 大连博雅人力资源咨询有限公司 · 大连市大数据研究院</span>
        </div>
      </footer>
    </>
  )
}

function AuthForm({
  mode,
  target,
  projectSlug,
  message,
  onMessage,
  onAuthed,
  onDone,
  onSwitch,
}: {
  mode: 'login' | 'register'
  target: 'PROJECT' | 'SUPER'
  projectSlug: string
  message: string
  onMessage: (value: string) => void
  onAuthed: (user: AuthUser) => void
  onDone: (user: AuthUser) => void
  onSwitch: () => void
}) {
  const [form, setForm] = useState({ email: '', password: '', confirmPassword: '', invitationCode: '' })
  const [legalBundle, setLegalBundle] = useState<LegalBundle | null>(null)
  const [agreeTerms, setAgreeTerms] = useState(false)
  const [showTerms, setShowTerms] = useState(false)
  const [activeLegalPart, setActiveLegalPart] = useState<'user' | 'privacy'>('user')
  const [legalLoadError, setLegalLoadError] = useState('')

  useEffect(() => {
    if (mode !== 'register') return
    fetch(`${API_BASE}/projects/${projectSlug}/legal/combined`)
      .then(async (res) => {
        const raw = await res.text()
        const data = raw ? JSON.parse(raw) : null
        if (!res.ok) throw new Error((data && data.message) || '协议加载失败')
        const normalized = normalizeLegalBundle(data)
        if (!normalized) throw new Error('协议格式不正确')
        setLegalBundle(normalized)
        setLegalLoadError('')
      })
      .catch(() => {
        setLegalBundle(null)
        setLegalLoadError('协议暂时不可用，请稍后刷新或联系管理员')
      })
  }, [mode, projectSlug])

  const submit = async (event: FormEvent) => {
    event.preventDefault()
    onMessage('')
    try {
      const path = mode === 'login' ? '/auth/login' : `/projects/${projectSlug}/auth/register`
      const body = mode === 'login'
        ? { email: form.email, password: form.password, projectSlug, target }
        : {
            email: form.email,
            password: form.password,
            confirmPassword: form.confirmPassword,
            invitationCode: form.invitationCode,
            agreeTerms,
            userAgreementVersion: legalBundle?.userAgreement.version || '',
            privacyPolicyVersion: legalBundle?.privacyPolicy.version || '',
          }
      const response = await fetch(`${API_BASE}${path}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      })
      const raw = await response.text()
      const data = raw ? JSON.parse(raw) : {}
      if (!response.ok) {
        if (mode === 'login' && (response.status === 401 || response.status === 403)) {
          throw new Error('邮箱或密码错误，请重试')
        }
        const serverMessage = data.message || '请求失败'
        throw new Error(serverMessage === 'EMAIL_EXISTS_LOGIN_TO_JOIN' ? '该邮箱已注册，请登录后使用邀请码加入该项目' : serverMessage)
      }
      onAuthed(data)
      onDone(data)
    } catch (error) {
      onMessage(error instanceof Error ? error.message : '请求失败')
    }
  }

  return (
    <section className="center-shell">
      <form className="form-card" onSubmit={submit}>
        <h2>{target === 'SUPER' ? '平台超级管理员登录' : (mode === 'login' ? '登录测评系统' : '注册参测账号')}</h2>
        <label>邮箱<input required type="email" value={form.email} onChange={(e) => setForm({ ...form, email: e.target.value })} /></label>
        <label>密码<input required minLength={8} type="password" value={form.password} onChange={(e) => setForm({ ...form, password: e.target.value })} /></label>
        {mode === 'register' && (
          <>
            <label>确认密码<input required minLength={8} type="password" value={form.confirmPassword} onChange={(e) => setForm({ ...form, confirmPassword: e.target.value })} /></label>
            <label>邀请码<input required value={form.invitationCode} onChange={(e) => setForm({ ...form, invitationCode: e.target.value })} /></label>
            <label className="checkbox-row">
              <input type="checkbox" checked={agreeTerms} onChange={(e) => setAgreeTerms(e.target.checked)} />
              <span>
                我已阅读并同意
                <button
                  className="text-link terms-inline-link"
                  type="button"
                  onClick={() => {
                    setActiveLegalPart('user')
                    setShowTerms(true)
                  }}
                >
                  《用户协议》
                </button>
                与
                <button
                  className="text-link terms-inline-link"
                  type="button"
                  onClick={() => {
                    setActiveLegalPart('privacy')
                    setShowTerms(true)
                  }}
                >
                  《隐私条款》
                </button>
              </span>
            </label>
            {legalLoadError && <p className="message">{legalLoadError}</p>}
          </>
        )}
        {message && <p className="message">{message}</p>}
        <button className="primary full" type="submit" disabled={mode === 'register' && (!agreeTerms || !legalBundle?.userAgreement.version || !legalBundle?.privacyPolicy.version)}>{mode === 'login' ? '登录' : '注册并继续'}</button>
        {target === 'PROJECT' && <div className="auth-switch">
          <span>{mode === 'login' ? '还没有账号？' : '已有账号？'}</span>
          <button className="text-link" type="button" onClick={onSwitch}>{mode === 'login' ? '去注册' : '去登录'}</button>
        </div>}
      </form>
      {showTerms && (
        <div className="modal-backdrop" role="dialog" aria-modal="true">
          <section className="modal">
            <div className="section-head">
              <div>
                <h2>{activeLegalPart === 'user' ? (legalBundle?.userAgreement.title || '用户协议') : (legalBundle?.privacyPolicy.title || '隐私条款')}</h2>
                <p className="hint">
                  当前查看：{activeLegalPart === 'user' ? '用户协议' : '隐私条款'} | 版本：
                  {activeLegalPart === 'user' ? (legalBundle?.userAgreement.version || '-') : (legalBundle?.privacyPolicy.version || '-')}
                </p>
              </div>
              <button type="button" onClick={() => setShowTerms(false)}>关闭</button>
            </div>
            <div
              className="markdown-preview"
              dangerouslySetInnerHTML={{
                __html: renderMarkdownToHtml(
                  activeLegalPart === 'user'
                    ? (legalBundle?.userAgreement.contentMd || '协议内容加载失败，请联系管理员。')
                    : (legalBundle?.privacyPolicy.contentMd || '协议内容加载失败，请联系管理员。'),
                ),
              }}
            />
          </section>
        </div>
      )}
    </section>
  )
}

function ProfileForm({ api, onDone }: { api: <T>(path: string, init?: RequestInit) => Promise<T>; onDone: () => void }) {
  const [form, setForm] = useState({
    name: '',
    gender: '男',
    idCard: '',
    phone: '',
    education: '本科',
    schoolMajor: '',
  })
  const [message, setMessage] = useState('')

  const submit = async (event: FormEvent) => {
    event.preventDefault()
    try {
      await api('/me/profile', { method: 'PUT', body: JSON.stringify(form) })
      onDone()
    } catch (error) {
      setMessage(error instanceof Error ? error.message : '保存失败')
    }
  }

  return (
    <section className="center-shell">
      <form className="form-card wide" onSubmit={submit}>
        <p className="eyebrow">Participant Profile</p>
        <h2>完善参会信息</h2>
        <div className="grid-form">
          <label>姓名<input required value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} /></label>
          <label>性别<select value={form.gender} onChange={(e) => setForm({ ...form, gender: e.target.value })}>
            <option>男</option><option>女</option><option>其他</option>
          </select></label>
          <label>身份证<input required value={form.idCard} onChange={(e) => setForm({ ...form, idCard: e.target.value })} /></label>
          <label>联系方式<input required value={form.phone} onChange={(e) => setForm({ ...form, phone: e.target.value })} /></label>
          <label>学历<select value={form.education} onChange={(e) => setForm({ ...form, education: e.target.value })}>
            <option>博士</option><option>硕士</option><option>本科</option><option>大专</option><option>高中及以下</option>
          </select></label>
          <label>院校 / 专业<input required value={form.schoolMajor} onChange={(e) => setForm({ ...form, schoolMajor: e.target.value })} /></label>
        </div>
        {message && <p className="message">{message}</p>}
        <button className="primary full" type="submit">保存并进入答题</button>
      </form>
    </section>
  )
}

function Catalog({ assessments, loading, onSelect }: { assessments: Assessment[]; loading: boolean; onSelect: (assessment: Assessment) => void }) {
  return (
    <section className="workspace assessment-shell">
      <div className="section-head">
        <div>
          <h2>选择正式心理测评量表</h2>
        </div>
      </div>
      <div className="catalog-grid">
        {assessments.map((item) => (
          <article className="catalog-card" key={item.testPaperId}>
            <div>
              <h3>{item.title}</h3>
              <p>{item.description}</p>
            </div>
            <div className="catalog-meta">
              <span>{item.questions.length} 题</span>
              <span>{optionLabels(item.testPaperId).length} 档选项</span>
            </div>
            <button
              className="primary"
              type="button"
              disabled={item.completed && !item.canRetake}
              onClick={() => onSelect(item)}
            >
              {item.completed ? (item.canRetake ? '重新答题' : '已完成') : '开始答题'}
            </button>
          </article>
        ))}
        {loading && <EmptyState title="正在加载量表" text="请稍候，系统正在读取正式量表目录。" />}
        {!loading && !assessments.length && <EmptyState title="暂无可用量表" text="请确认后端服务已启动，且题库初始化成功。" />}
      </div>
    </section>
  )
}

function AssessmentPage({ assessment, answers, setAnswers, api, onDone }: {
  assessment: Assessment
  answers: Record<string, number>
  setAnswers: (answers: Record<string, number>) => void
  api: <T>(path: string, init?: RequestInit) => Promise<T>
  onDone: () => void
}) {
  const [index, setIndex] = useState(0)
  const [authorized, setAuthorized] = useState(false)
  const [message, setMessage] = useState('')
  const question = assessment.questions[index]
  const progress = Math.round((Object.keys(answers).length / assessment.questions.length) * 100)
  const labels = optionLabels(assessment.testPaperId)

  useEffect(() => {
    let active = true
    ;(async () => {
      try {
        const progress = await api<{ answersJson: string }>('/assessment/progress/' + encodeURIComponent(assessment.testPaperId))
        if (!active || !progress.answersJson) return
        const parsed = JSON.parse(progress.answersJson || '{}') as Record<string, number>
        if (Object.keys(parsed).length > 0) setAnswers(parsed)
      } catch { /* ignore */ }
    })()
    return () => { active = false }
  }, [assessment.testPaperId])

  useEffect(() => {
    if (!authorized) return
    const timer = window.setTimeout(() => {
      void api('/assessment/progress', {
        method: 'PUT',
        body: JSON.stringify({ testPaperId: assessment.testPaperId, answers }),
      }).catch(() => {})
    }, 300)
    return () => window.clearTimeout(timer)
  }, [answers, authorized, assessment.testPaperId])

  const submit = async () => {
    try {
      const payload = { testPaperId: assessment.testPaperId, answers: Object.entries(answers).map(([questionCode, value]) => ({ questionCode, value })) }
      await api<Record<string, unknown>>('/assessment/submit', { method: 'POST', body: JSON.stringify(payload) })
      onDone()
    } catch (error) {
      setMessage(error instanceof Error ? error.message : '提交失败')
    }
  }

  const selectAndContinue = (questionCode: string, value: number) => {
    const nextAnswers = { ...answers, [questionCode]: value }
    setAnswers(nextAnswers)
    if (index < assessment.questions.length - 1) {
      setIndex(index + 1)
    }
  }

  return (
    <section className="workspace assessment-shell">
      <div className="section-head">
        <h2>{assessment.title}</h2>
        <span>{progress}% completed</span>
      </div>
      {!authorized && (
        <article className="question-card precheck-card">
          <h3>答题提示</h3>
          <p>为保障评估结果有效性，请在安静环境中独立作答，按真实感受选择，不要代答或随意作答。</p>
          <button className="primary" type="button" onClick={() => setAuthorized(true)}>我已了解，开始答题</button>
        </article>
      )}
      {authorized && (
      <article className="question-card">
        <div className="question-meta">第 {index + 1} / {assessment.questions.length} 题 · {question.dimension}</div>
        <h3>{question.prompt}</h3>
        <div className="option-list">
          {labels.map((label, labelIndex) => {
            const value = labelIndex + 1
            const selected = answers[question.code] === value
            return (
              <button
                key={value}
                className={`option-item ${selected ? 'selected' : ''}`}
                type="button"
                onClick={() => selectAndContinue(question.code, value)}
              >
                <i aria-hidden="true" />
                <span>{value}. {label}</span>
              </button>
            )
          })}
        </div>
      </article>
      )}
      {message && <p className="message">{message}</p>}
      {authorized && (
      <div className="pager">
        <button type="button" disabled={index === 0} onClick={() => setIndex(index - 1)}>上一题</button>
        {index === assessment.questions.length - 1 && (
          <button className="primary" type="button" onClick={submit}>提交并生成报告</button>
        )}
      </div>
      )}
    </section>
  )
}

function optionLabels(testPaperId: string) {
  if (testPaperId.includes('2058')) return ['是', '否']
  if (testPaperId.includes('2081')) return ['A', 'B', 'C']
  if (testPaperId.includes('2082') || testPaperId.includes('2083')) return ['没有或很少', '小部分时间', '相当多时间', '绝大部分时间']
  if (testPaperId.includes('2078')) return ['没有', '很轻', '中等', '偏重', '严重']
  if (testPaperId.includes('2084')) return ['完全赞同', '比较赞同', '中立', '比较不赞同', '完全不赞同']
  return ['很少', '偶尔', '一般', '经常', '非常']
}

function CelebratePage({ onDone }: { onDone: () => void }) {
  const celebrateRef = useRef<HTMLDivElement | null>(null)

  useEffect(() => {
    const colors = ['#78c800', '#1cb0f6', '#ffc800', '#ff9600']
    const rect = celebrateRef.current?.getBoundingClientRect() || null
    const cx = rect ? rect.left + rect.width / 2 : window.innerWidth / 2
    const cy = rect ? rect.top + rect.height / 2 : window.innerHeight * 0.6
    const origin = {
      x: Math.max(0.05, Math.min(0.95, cx / window.innerWidth)),
      y: Math.max(0.1, Math.min(0.95, cy / window.innerHeight)),
    }
    confetti({ particleCount: 150, spread: 70, origin, colors })
    const timer = window.setTimeout(() => {
      confetti({ particleCount: 80, spread: 90, origin, colors, scalar: 0.9 })
    }, 220)
    return () => window.clearTimeout(timer)
  }, [onDone])

  return (
    <section className="center-shell celebrate-shell">
      <div ref={celebrateRef} className="celebrate-card">
        <PartyPopper size={78} />
        <h2>测评完成</h2>
        <p>感谢认真作答，本量表已提交。</p>
        <button className="primary" type="button" onClick={onDone}>返回量表列表</button>
      </div>
    </section>
  )
}

function SuperAdminPage({ auth, setProjectSlug }: { auth: AuthUser | null; setProjectSlug: (slug: string) => void }) {
  const [projects, setProjects] = useState<SuperProject[]>([])
  const [platformAdmins, setPlatformAdmins] = useState<PlatformAdmin[]>([])
  const [appointments, setAppointments] = useState<HomepageAppointment[]>([])
  const [activeSection, setActiveSection] = useState<'projects' | 'admins' | 'appointments'>('projects')
  const [createOpen, setCreateOpen] = useState(false)
  const [superAdminCreateOpen, setSuperAdminCreateOpen] = useState(false)
  const [form, setForm] = useState({
    name: '',
    slug: '',
    landingTitle: '',
    landingDescription: '',
    reportTitle: '',
    adminEmail: '',
    adminPassword: '',
    initialInviteCode: '',
  })
  const [superAdminForm, setSuperAdminForm] = useState({ email: '', password: '' })
  const [message, setMessage] = useState('')
  const headers = { 'Content-Type': 'application/json', ...(auth?.token ? { Authorization: `Bearer ${auth.token}` } : {}) }

  const superApi = async <T,>(path: string, init: RequestInit = {}) => {
    const response = await fetch(`${API_BASE}/super${path}`, { ...init, headers: { ...headers, ...init.headers } })
    const raw = await response.text()
    const data = raw ? JSON.parse(raw) : null
    if (!response.ok) throw new Error(data?.message || '请求失败')
    return data as T
  }

  const loadProjects = async () => {
    setProjects(await superApi<SuperProject[]>('/projects'))
  }

  const loadPlatformAdmins = async () => {
    setPlatformAdmins(await superApi<PlatformAdmin[]>('/admins'))
  }

  const loadAppointments = async () => {
    setAppointments(await superApi<HomepageAppointment[]>('/appointments'))
  }

  const loadWorkspace = async () => {
    const [projectRows, adminRows, appointmentRows] = await Promise.all([
      superApi<SuperProject[]>('/projects'),
      superApi<PlatformAdmin[]>('/admins'),
      superApi<HomepageAppointment[]>('/appointments'),
    ])
    setProjects(projectRows)
    setPlatformAdmins(adminRows)
    setAppointments(appointmentRows)
  }

  useEffect(() => {
    void loadWorkspace().catch((error) => setMessage(error instanceof Error ? error.message : '平台工作台加载失败'))
  }, [])

  const createProject = async (event: FormEvent) => {
    event.preventDefault()
    try {
      const response = await superApi<{ project: { slug: string; url: string; adminUrl: string }; initialInviteCode: string }>('/projects', {
        method: 'POST',
        body: JSON.stringify({
          ...form,
          enabledAssessmentIds: DEFAULT_PROJECT_ASSESSMENT_IDS,
        }),
      })
      setMessage(`项目已创建，初始邀请码：${response.initialInviteCode}`)
      setCreateOpen(false)
      setForm({
        name: '',
        slug: '',
        landingTitle: '',
        landingDescription: '',
        reportTitle: '',
        adminEmail: '',
        adminPassword: '',
        initialInviteCode: '',
      })
      await loadProjects()
    } catch (error) {
      setMessage(error instanceof Error ? error.message : '创建项目失败')
    }
  }

  const createPlatformAdmin = async (event: FormEvent) => {
    event.preventDefault()
    try {
      await superApi('/admins', { method: 'POST', body: JSON.stringify(superAdminForm) })
      setSuperAdminForm({ email: '', password: '' })
      setSuperAdminCreateOpen(false)
      setMessage('平台超级管理员已创建')
      await loadPlatformAdmins()
    } catch (error) {
      setMessage(error instanceof Error ? error.message : '创建平台超级管理员失败')
    }
  }

  const openProjectAdmin = (slug: string) => {
    setProjectSlug(slug)
    window.location.href = `/${slug}/admin`
  }

  const downloadProjectAllData = async (slug: string) => {
    try {
      const response = await fetch(`${API_BASE}/super/projects/${slug}/exports/all-data`, {
        headers: auth?.token ? { Authorization: `Bearer ${auth.token}` } : {},
      })
      if (!response.ok) {
        const raw = await response.text()
        let errorMessage = '导出项目全部数据失败'
        try {
          const parsed = raw ? JSON.parse(raw) : null
          if (parsed && typeof parsed === 'object' && 'message' in parsed) {
            errorMessage = String((parsed as { message?: string }).message || errorMessage)
          }
        } catch { /* ignore */ }
        throw new Error(errorMessage)
      }
      const blob = await response.blob()
      const url = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      const serverName = getFilenameFromDisposition(response.headers.get('content-disposition'))
      link.download = serverName || `${slug}-all-data.zip`
      link.click()
      URL.revokeObjectURL(url)
      setMessage(`已导出 ${slug} 全部数据`)
    } catch (error) {
      setMessage(error instanceof Error ? error.message : '导出项目全部数据失败')
    }
  }

  return (
    <section className="workspace admin-shell super-workspace">
      <aside className="super-sidebar" aria-label="平台菜单">
        <p className="super-sidebar-label">菜单</p>
        <button type="button" className={activeSection === 'projects' ? 'active' : ''} onClick={() => setActiveSection('projects')}>切换项目</button>
        <button type="button" className={activeSection === 'admins' ? 'active' : ''} onClick={() => setActiveSection('admins')}>管理员</button>
        <button type="button" className={activeSection === 'appointments' ? 'active' : ''} onClick={() => setActiveSection('appointments')}>查看预约</button>
      </aside>
      <div className="super-content">
        {activeSection === 'projects' && (
          <>
            <div className="section-head">
              <div>
                <h2>项目列表</h2>
                <p className="hint">创建项目后会生成独立访问路径和初始项目管理员。</p>
              </div>
              <div className="table-actions">
                <button type="button" onClick={() => { void loadProjects() }}>刷新</button>
                <button className="primary" type="button" onClick={() => setCreateOpen(true)}>创建项目</button>
              </div>
            </div>
            {message && <p className="message">{message}</p>}
            <section className="report-card compact admin-v2-card">
              <div className="table-wrap">
                <table>
                  <thead><tr><th>项目</th><th>Slug</th><th>状态</th><th>入口</th><th>操作</th></tr></thead>
                  <tbody>
                    {projects.map((project) => (
                      <tr key={project.slug}>
                        <td>{project.name}</td>
                        <td>{project.slug}</td>
                        <td>{project.enabled ? '启用' : '停用'}</td>
                        <td>{project.url}</td>
                        <td className="table-actions">
                          <button type="button" onClick={() => openProjectAdmin(project.slug)}>进入后台</button>
                          <button type="button" onClick={() => { void downloadProjectAllData(project.slug) }}>导出数据</button>
                          <button type="button" onClick={async () => { await superApi(`/projects/${project.slug}/status`, { method: 'PATCH' }); await loadProjects() }}>{project.enabled ? '停用' : '启用'}</button>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </section>
          </>
        )}
        {activeSection === 'admins' && (
          <>
            <div className="section-head">
              <div>
                <h2>平台管理员</h2>
                <p className="hint">这里管理能登录 /super 的平台超级管理员账号。</p>
              </div>
              <div className="table-actions">
                <button type="button" onClick={() => { void loadPlatformAdmins() }}>刷新</button>
                <button className="primary" type="button" onClick={() => setSuperAdminCreateOpen(true)}>创建Super Admin</button>
              </div>
            </div>
            {message && <p className="message">{message}</p>}
            <section className="report-card compact admin-v2-card">
              <div className="table-wrap">
                <table>
                  <thead><tr><th>邮箱</th><th>角色</th><th>状态</th><th>创建时间</th></tr></thead>
                  <tbody>
                    {platformAdmins.map((admin) => (
                      <tr key={admin.id}>
                        <td>{admin.email}</td>
                        <td>{admin.role}</td>
                        <td>{admin.enabled ? '启用' : '停用'}</td>
                        <td>{admin.createdAt}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </section>
          </>
        )}
        {activeSection === 'appointments' && (
          <>
            <div className="section-head">
              <div>
                <h2>预约列表</h2>
                <p className="hint">这里查看官网首页提交的预约演示信息。</p>
              </div>
              <div className="table-actions">
                <button type="button" onClick={() => { void loadAppointments() }}>刷新</button>
              </div>
            </div>
            {message && <p className="message">{message}</p>}
            <section className="report-card compact admin-v2-card">
              <div className="table-wrap">
                <table>
                  <thead><tr><th>姓名</th><th>机构</th><th>电话号</th><th>微信号</th><th>城市</th><th>来源</th><th>提交时间</th></tr></thead>
                  <tbody>
                    {appointments.map((item) => (
                      <tr key={item.id}>
                        <td>{item.name}</td>
                        <td>{item.organization}</td>
                        <td>{item.phone}</td>
                        <td>{item.wechat}</td>
                        <td>{item.city}</td>
                        <td>{item.sourcePath}</td>
                        <td>{item.createdAt}</td>
                      </tr>
                    ))}
                    {appointments.length === 0 && (
                      <tr><td colSpan={7}>暂无预约记录</td></tr>
                    )}
                  </tbody>
                </table>
              </div>
            </section>
          </>
        )}
      </div>
      {createOpen && (
        <div className="modal-backdrop" onMouseDown={(event) => {
          if (event.target === event.currentTarget) setCreateOpen(false)
        }}>
          <section className="modal create-project-modal" role="dialog" aria-modal="true" aria-label="创建项目">
            <div className="section-head compact-head">
              <div>
                <h3>创建项目</h3>
                <p className="hint">填写后会立即发布项目，并生成该项目的初始管理员。</p>
              </div>
              <button type="button" onClick={() => setCreateOpen(false)}>关闭</button>
            </div>
            <form className="grid-form" onSubmit={createProject}>
              <label>项目名称<input required value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} /></label>
              <label>Slug<input required value={form.slug} onChange={(e) => setForm({ ...form, slug: e.target.value })} /></label>
              <label>封面标题<input required value={form.landingTitle} onChange={(e) => setForm({ ...form, landingTitle: e.target.value })} /></label>
              <label>报告标题<input required value={form.reportTitle} onChange={(e) => setForm({ ...form, reportTitle: e.target.value })} /></label>
              <label className="span-2">封面描述<textarea required rows={4} value={form.landingDescription} onChange={(e) => setForm({ ...form, landingDescription: e.target.value })} /></label>
              <label>管理员邮箱<input required type="email" value={form.adminEmail} onChange={(e) => setForm({ ...form, adminEmail: e.target.value })} /></label>
              <label>管理员密码<input required minLength={8} type="password" value={form.adminPassword} onChange={(e) => setForm({ ...form, adminPassword: e.target.value })} /></label>
              <label>初始邀请码<input value={form.initialInviteCode} onChange={(e) => setForm({ ...form, initialInviteCode: e.target.value })} /></label>
              <div className="form-actions span-2">
                <button type="button" onClick={() => setCreateOpen(false)}>取消</button>
                <button className="primary" type="submit">创建并发布</button>
              </div>
            </form>
          </section>
        </div>
      )}
      {superAdminCreateOpen && (
        <div className="modal-backdrop" onMouseDown={(event) => {
          if (event.target === event.currentTarget) setSuperAdminCreateOpen(false)
        }}>
          <section className="modal small-modal" role="dialog" aria-modal="true" aria-label="创建Super Admin">
            <div className="section-head compact-head">
              <div>
                <h3>创建Super Admin</h3>
                <p className="hint">新账号将拥有 /super 平台后台权限。</p>
              </div>
              <button type="button" onClick={() => setSuperAdminCreateOpen(false)}>关闭</button>
            </div>
            <form onSubmit={createPlatformAdmin}>
              <label>管理员邮箱<input required type="email" value={superAdminForm.email} onChange={(e) => setSuperAdminForm({ ...superAdminForm, email: e.target.value })} /></label>
              <label>管理员密码<input required minLength={8} type="password" value={superAdminForm.password} onChange={(e) => setSuperAdminForm({ ...superAdminForm, password: e.target.value })} /></label>
              <div className="form-actions">
                <button type="button" onClick={() => setSuperAdminCreateOpen(false)}>取消</button>
                <button className="primary" type="submit">创建</button>
              </div>
            </form>
          </section>
        </div>
      )}
    </section>
  )
}

async function downloadPdf(
  event: MouseEvent<HTMLElement>,
  reportId: number,
  token: string,
  projectSlug = DEFAULT_PROJECT_SLUG,
  coverType: 'personal' | 'group' = 'personal',
  groupName = '',
) {
  event.preventDefault()
  await downloadPdfById(reportId, token, projectSlug, coverType, groupName)
}

async function downloadPdfById(
  reportId: number,
  token: string,
  projectSlug = DEFAULT_PROJECT_SLUG,
  coverType: 'personal' | 'group' = 'personal',
  groupName = '',
) {
  const params = new URLSearchParams()
  params.set('coverType', coverType)
  if (coverType === 'group') params.set('groupName', groupName)
  const response = await fetch(`${API_BASE}${scopedApiPath(projectSlug, `/reports/${reportId}/pdf`)}?${params.toString()}`, { headers: { Authorization: `Bearer ${token}` } })
  if (!response.ok) {
    const raw = await response.text()
    let msg = '下载报告失败'
    try {
      const parsed = raw ? JSON.parse(raw) : null
      if (parsed && typeof parsed === 'object' && 'message' in parsed) msg = String((parsed as { message?: string }).message || msg)
    } catch { /* ignore */ }
    window.alert(msg)
    throw new Error(msg)
  }
  const contentType = response.headers.get('content-type') || ''
  if (!contentType.toLowerCase().includes('pdf')) {
    const msg = '下载失败：返回内容不是 PDF'
    window.alert(msg)
    throw new Error(msg)
  }
  const blob = await response.blob()
  if (blob.size === 0) {
    const msg = '下载失败：PDF 内容为空'
    window.alert(msg)
    throw new Error(msg)
  }
  const url = URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  const serverName = getFilenameFromDisposition(response.headers.get('content-disposition'))
  link.download = serverName || `${projectSlug}-report-${reportId}.pdf`
  link.click()
  URL.revokeObjectURL(url)
}

function getFilenameFromDisposition(disposition: string | null): string | null {
  if (!disposition) return null
  const utf8Match = disposition.match(/filename\*=UTF-8''([^;]+)/i)
  if (utf8Match?.[1]) {
    try {
      return decodeURIComponent(utf8Match[1])
    } catch {
      return utf8Match[1]
    }
  }
  const plainMatch = disposition.match(/filename=\"?([^\";]+)\"?/i)
  return plainMatch?.[1] || null
}

async function downloadGroupPdf(event: MouseEvent<HTMLElement>, reportId: number, token: string, projectSlug = DEFAULT_PROJECT_SLUG) {
  event.preventDefault()
  const groupName = window.prompt('请输入团体名称')?.trim() || ''
  if (!groupName) {
    window.alert('团体名称不能为空')
    return
  }
  await downloadPdf(event, reportId, token, projectSlug, 'group', groupName)
}

function AdminV2Page({ admin, setAdmin, api, reload, token, loading, adminTab, projectSlug }: { admin: AdminState; setAdmin: (state: AdminState) => void; api: <T>(path: string, init?: RequestInit) => Promise<T>; reload: () => void; token: string; loading: boolean; adminTab: 'users' | 'ops' | 'monitor' | 'protocol'; projectSlug: string }) {
  const [invite, setInvite] = useState({ code: '', maxUses: 50, expiresAt: '' })
  const [projectAdmin, setProjectAdmin] = useState({ email: '', password: '' })
  const [query, setQuery] = useState('')
  const [selectedIds, setSelectedIds] = useState<number[]>([])
  const [selectedUser, setSelectedUser] = useState<Record<string, string | number | boolean> | null>(null)
  const [userAssessments, setUserAssessments] = useState<UserAssessmentReportOverview | null>(null)
  const [statusItems, setStatusItems] = useState<UserAssessmentStatus[]>([])
  const [userInsightOpen, setUserInsightOpen] = useState(false)
  const [userDetail, setUserDetail] = useState<Record<string, string | number | boolean> | null>(null)
  const [reportPreview, setReportPreview] = useState<{ title: string; summary: string; submittedAt: string } | null>(null)
  const [resetUser, setResetUser] = useState<Record<string, string | number | boolean> | null>(null)
  const [newPassword, setNewPassword] = useState('')
  const [filters, setFilters] = useState({ organization: '', level: '', from: '', to: '' })
  const [message, setMessage] = useState('')
  const [monitorItems, setMonitorItems] = useState({ submissions: true, logs: false })
  const [groupModalOpen, setGroupModalOpen] = useState(false)
  const [legalBundle, setLegalBundle] = useState<LegalBundle | null>(null)
  const [consentRows, setConsentRows] = useState<ConsentRow[]>([])
  const [consentEmailQuery, setConsentEmailQuery] = useState('')
  const [protocolLoading, setProtocolLoading] = useState(false)
  const [protocolError, setProtocolError] = useState('')

  const loadProtocol = async () => {
    setProtocolLoading(true)
    setProtocolError('')
    try {
      const [doc, rows] = await Promise.all([
        api<LegalBundle>('/admin/legal/combined'),
        api<ConsentRow[]>('/admin/legal/consents'),
      ])
      const normalized = normalizeLegalBundle(doc)
      if (!normalized) throw new Error('协议格式不正确')
      setLegalBundle(normalized)
      setConsentRows(rows)
    } catch (error) {
      setProtocolError(error instanceof Error ? error.message : '协议管理加载失败')
    } finally {
      setProtocolLoading(false)
    }
  }

  useEffect(() => {
    if (adminTab === 'protocol') {
      void loadProtocol()
    }
  }, [adminTab])

  const resolveExportError = async (response: Response, fallback: string) => {
    const raw = await response.text()
    let message = fallback
    try {
      const parsed = raw ? JSON.parse(raw) : null
      if (parsed && typeof parsed === 'object' && 'message' in parsed) {
        message = String((parsed as { message?: string }).message || message)
      }
    } catch { /* ignore */ }
    if (!raw && response.status >= 500) return '导出失败：服务内部错误（可能为服务器字体或模板渲染异常）'
    if (!raw && response.status === 403) return '导出失败：当前账号没有该导出权限'
    return message
  }

  const resolveFetchError = (error: unknown, fallback: string) => {
    if (error instanceof TypeError) {
      return '导出失败：后端服务不可用或网络连接失败'
    }
    if (error instanceof Error && error.message) return error.message
    return fallback
  }

  const users = useMemo(() => {
    return admin.users.filter((item) => {
      if (String(item.role || '') !== 'USER') return false
      const text = `${String(item.email || '')} ${String(item.name || '')} ${String(item.organization || '')}`.toLowerCase()
      return !query || text.includes(query.toLowerCase())
    })
  }, [admin.users, query])

  const toggleSelected = (id: number) => {
    setSelectedIds((prev) => (prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]))
  }

  const selectUser = async (row: Record<string, string | number | boolean>) => {
    const id = Number(row.id)
    setSelectedUser(row)
    const [overview, statuses] = await Promise.all([
      api<UserAssessmentReportOverview>(`/admin/users/${id}/assessment-reports`),
      api<UserAssessmentStatus[]>(`/admin/users/${id}/assessments`),
    ])
    setUserAssessments(overview)
    setStatusItems(statuses)
    setUserInsightOpen(true)
  }

  const reopenOne = async (testPaperId: string) => {
    if (!selectedUser) return
    await api(`/admin/users/${Number(selectedUser.id)}/assessments/${encodeURIComponent(testPaperId)}/reopen`, {
      method: 'PATCH',
      body: JSON.stringify({ note: '管理员手动重开' }),
    })
    const [overview, statuses] = await Promise.all([
      api<UserAssessmentReportOverview>(`/admin/users/${Number(selectedUser.id)}/assessment-reports`),
      api<UserAssessmentStatus[]>(`/admin/users/${Number(selectedUser.id)}/assessments`),
    ])
    setUserAssessments(overview)
    setStatusItems(statuses)
    setMessage('已重开该量表')
  }

  const createInvite = async (event: FormEvent) => {
    event.preventDefault()
    try {
      await api('/admin/invitation-codes', { method: 'POST', body: JSON.stringify(invite) })
      setInvite({ code: '', maxUses: 50, expiresAt: '' })
      setMessage('邀请码已创建')
      reload()
    } catch (error) {
      setMessage(error instanceof Error ? error.message : '创建邀请码失败')
    }
  }

  const createProjectAdmin = async (event: FormEvent) => {
    event.preventDefault()
    try {
      await api('/admin/users/admin', { method: 'POST', body: JSON.stringify(projectAdmin) })
      setProjectAdmin({ email: '', password: '' })
      setMessage('项目管理员已创建')
    } catch (error) {
      setMessage(error instanceof Error ? error.message : '创建项目管理员失败')
    }
  }

  const toggleInvite = async (id: number) => {
    await api(`/admin/invitation-codes/${id}/toggle`, { method: 'PATCH' })
    reload()
  }

  const toggleUser = async (id: number) => {
    await api(`/admin/users/${id}/toggle`, { method: 'PATCH' })
    reload()
  }

  const openSubmissionByUser = async (row: Record<string, string | number | boolean>) => {
    const tryKeys = ['userId', 'user_id', 'uid']
    let userId = 0
    for (const key of tryKeys) {
      const value = Number(row[key] ?? 0)
      if (Number.isFinite(value) && value > 0) {
        userId = value
        break
      }
    }
    if (!userId && row.email) {
      const matched = admin.users.find((item) => String(item.email || '') === String(row.email))
      if (matched?.id) userId = Number(matched.id)
    }
    if (!userId) {
      setMessage('未找到该答题记录对应的用户，请先刷新后台数据后重试')
      return
    }
    const matchedUser = admin.users.find((item) => Number(item.id) === userId)
    if (matchedUser) {
      await selectUser(matchedUser)
      return
    }
    const detailUser = await api<Record<string, string | number | boolean>>(`/admin/users/${userId}`)
    await selectUser(detailUser)
  }

  const resetPassword = async (event: FormEvent) => {
    event.preventDefault()
    if (!resetUser) return
    await api(`/admin/users/${resetUser.id}/password`, { method: 'PATCH', body: JSON.stringify({ password: newPassword }) })
    setMessage('用户密码已重置')
    setResetUser(null)
    setNewPassword('')
  }

  const applyFilters = async () => {
    const params = new URLSearchParams()
    if (filters.organization) params.set('organization', filters.organization)
    if (filters.level) params.set('level', filters.level)
    if (filters.from) params.set('from', filters.from)
    if (filters.to) params.set('to', filters.to)
    const queryString = params.toString() ? `?${params.toString()}` : ''
    const [usersData, submissions, reports] = await Promise.all([
      api<AdminState['users']>(`/admin/users${filters.organization ? `?organization=${encodeURIComponent(filters.organization)}` : ''}`),
      api<AdminState['submissions']>(`/admin/submissions${queryString}`),
      api<AdminState['reports']>(`/admin/reports${queryString}`),
    ])
    setAdmin({ ...admin, users: usersData, submissions, reports })
    setMessage('筛选结果已更新')
  }

  const downloadUserExport = async (userId: number, format: ExportFormat, reportId?: number) => {
    try {
      const params = new URLSearchParams()
      params.set('format', format)
      if (reportId) params.set('reportId', String(reportId))
      const response = await fetch(`${API_BASE}${scopedApiPath(projectSlug, `/admin/users/${userId}/exports/download`)}?${params.toString()}`, {
        headers: { Authorization: `Bearer ${token}` },
      })
      if (!response.ok) {
        throw new Error(await resolveExportError(response, '导出失败，请检查权限或筛选条件'))
      }
      const blob = await response.blob()
      const url = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      const serverName = getFilenameFromDisposition(response.headers.get('content-disposition'))
      link.download = serverName || `${projectSlug}-user-${userId}.${format}`
      link.click()
      URL.revokeObjectURL(url)
    } catch (error) {
      setMessage(resolveFetchError(error, '导出失败'))
    }
  }

  const downloadBatchUsers = async (format: ExportFormat) => {
    if (selectedIds.length === 0) {
      setMessage('请先选择用户')
      return
    }
    try {
      const params = new URLSearchParams()
      params.set('format', format)
      selectedIds.forEach((id) => params.append('userIds', String(id)))
      const response = await fetch(`${API_BASE}${scopedApiPath(projectSlug, '/admin/users/exports/batch')}?${params.toString()}`, {
        headers: { Authorization: `Bearer ${token}` },
      })
      if (!response.ok) throw new Error(await resolveExportError(response, '批量导出失败'))
      const blob = await response.blob()
      const url = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      link.download = `${projectSlug}-users-${format}.zip`
      link.click()
      URL.revokeObjectURL(url)
      setMessage('批量导出已生成')
    } catch (error) {
      setMessage(resolveFetchError(error, '批量导出失败'))
    }
  }

  const exportMonitor = async (format: ExportFormat) => {
    const itemList = Object.entries(monitorItems).filter(([, checked]) => checked).map(([key]) => key)
    if (itemList.length === 0) {
      setMessage('请至少选择一个导出项')
      return
    }
    const params = new URLSearchParams()
    params.set('items', itemList.join(','))
    params.set('format', format)
    if (filters.organization) params.set('organization', filters.organization)
    if (filters.level) params.set('level', filters.level)
    if (filters.from) params.set('from', filters.from)
    if (filters.to) params.set('to', filters.to)
    try {
      const response = await fetch(`${API_BASE}${scopedApiPath(projectSlug, '/admin/monitor/exports/download')}?${params.toString()}`, {
        headers: { Authorization: `Bearer ${token}` },
      })
      if (!response.ok) throw new Error(await resolveExportError(response, '监控导出失败'))
      const blob = await response.blob()
      const url = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      link.download = `${projectSlug}-monitor-${format}.zip`
      link.click()
      URL.revokeObjectURL(url)
    } catch (error) {
      setMessage(resolveFetchError(error, '监控导出失败'))
    }
  }

  const exportOps = async (type: 'invites' | 'admins' | 'ops_snapshot', format: ExportFormat) => {
    try {
      const response = await fetch(`${API_BASE}${scopedApiPath(projectSlug, '/admin/ops/exports/download')}?type=${type}&format=${format}`, {
        headers: { Authorization: `Bearer ${token}` },
      })
      if (!response.ok) throw new Error(await resolveExportError(response, '运维导出失败'))
      const blob = await response.blob()
      const url = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      link.download = `${projectSlug}-ops-${type}.${format}`
      link.click()
      URL.revokeObjectURL(url)
    } catch (error) {
      setMessage(resolveFetchError(error, '运维导出失败'))
    }
  }

  const exportRiskCsv = async () => {
    try {
      const params = new URLSearchParams()
      selectedIds.forEach((id) => params.append('userIds', String(id)))
      const query = params.toString()
      const response = await fetch(`${API_BASE}${scopedApiPath(projectSlug, '/admin/risk-export.csv')}${query ? `?${query}` : ''}`, {
        headers: { Authorization: `Bearer ${token}` },
      })
      if (!response.ok) throw new Error(await resolveExportError(response, '状态导出失败'))
      const blob = await response.blob()
      const url = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      link.download = `${projectSlug}-risk-summary.csv`
      link.click()
      URL.revokeObjectURL(url)
    } catch (error) {
      setMessage(resolveFetchError(error, '状态导出失败'))
    }
  }

  const exportAuditAll = async () => {
    try {
      const response = await fetch(`${API_BASE}${scopedApiPath(projectSlug, '/admin/audit-logs/export-all.csv')}`, {
        headers: { Authorization: `Bearer ${token}` },
      })
      if (!response.ok) throw new Error(await resolveExportError(response, '操作日志导出失败'))
      const blob = await response.blob()
      const url = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      link.download = `${projectSlug}-audit-logs-all.csv`
      link.click()
      URL.revokeObjectURL(url)
    } catch (error) {
      setMessage(resolveFetchError(error, '操作日志导出失败'))
    }
  }

  const exportAllProjectData = async () => {
    try {
      const response = await fetch(`${API_BASE}${scopedApiPath(projectSlug, '/admin/exports/all-data')}`, {
        headers: { Authorization: `Bearer ${token}` },
      })
      if (!response.ok) throw new Error(await resolveExportError(response, '全部数据导出失败'))
      const blob = await response.blob()
      const url = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      const serverName = getFilenameFromDisposition(response.headers.get('content-disposition'))
      link.download = serverName || `${projectSlug}-all-data.zip`
      link.click()
      URL.revokeObjectURL(url)
      setMessage('项目全部数据导出已生成')
    } catch (error) {
      setMessage(resolveFetchError(error, '全部数据导出失败'))
    }
  }

  return (
    <section className="admin-shell">
      {adminTab === 'users' && <div className="admin-v2-layout">
        <div className="filter-bar admin-v2-filterbar compact">
          <input placeholder="搜索用户（姓名/邮箱/机构）" value={query} onChange={(e) => setQuery(e.target.value)} />
          <input placeholder="公司/机构筛选" value={filters.organization} onChange={(e) => setFilters({ ...filters, organization: e.target.value })} />
          <input placeholder="报告等级筛选" value={filters.level} onChange={(e) => setFilters({ ...filters, level: e.target.value })} />
          <input type="date" value={filters.from} onChange={(e) => setFilters({ ...filters, from: e.target.value })} />
          <input type="date" value={filters.to} onChange={(e) => setFilters({ ...filters, to: e.target.value })} />
          <button className="primary" type="button" onClick={applyFilters}>筛选</button>
          <span className="admin-v2-meta-inline">已选{selectedIds.length} / 列表{users.length}</span>
        </div>
        <section className="admin-v2-panel">
            <div className="section-head admin-v2-section-head">
              <h3>用户列表</h3>
              <div className="table-actions">
                <span>{users.length} 人</span>
                <button type="button" onClick={() => { void exportRiskCsv() }}>状态导出CSV</button>
                <button type="button" onClick={() => setGroupModalOpen(true)}>批量导出</button>
              </div>
            </div>
            <div className="table-wrap">
              <table>
                <thead><tr><th><input type="checkbox" checked={users.length > 0 && selectedIds.length === users.length} onChange={(e) => setSelectedIds(e.target.checked ? users.map((u) => Number(u.id)) : [])} /></th><th>姓名</th><th>邮箱</th><th>风险等级</th><th>机构</th><th>完成</th><th>操作</th></tr></thead>
                <tbody>
                  {users.map((row) => (
                    <tr key={String(row.id)}>
                      <td><input type="checkbox" checked={selectedIds.includes(Number(row.id))} onChange={() => toggleSelected(Number(row.id))} /></td>
                      <td>{String(row.name || '-')}</td>
                      <td>{String(row.email || '-')}</td>
                      <td>
                        <span className={`risk-badge ${riskLevelClass(String(row.riskLevel || ''))}`}>
                          {String(row.riskLevel || '-')}
                        </span>
                      </td>
                      <td>{String(row.organization || '-')}</td>
                      <td>{String(row.completion || '0/3')}</td>
                      <td className="table-actions">
                        <button type="button" onClick={() => { void selectUser(row) }}>查看详情</button>
                        <button type="button" onClick={() => setResetUser(row)}>重置密码</button>
                        <button type="button" onClick={() => { void toggleUser(Number(row.id)) }}>{row.enabled === true ? '禁用' : '启用'}</button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </section>
      </div>}
      {loading && <p className="message">后台数据加载中...</p>}
      {adminTab === 'ops' && <section className="report-card compact admin-v2-card">
        <h3>后台运维</h3>
        <div className="ops-block">
          <h4>创建项目管理员</h4>
          <form className="inline-form project-admin-create" onSubmit={createProjectAdmin}>
            <input required type="email" placeholder="管理员邮箱" value={projectAdmin.email} onChange={(e) => setProjectAdmin({ ...projectAdmin, email: e.target.value })} />
            <input required type="password" minLength={8} placeholder="初始密码" value={projectAdmin.password} onChange={(e) => setProjectAdmin({ ...projectAdmin, password: e.target.value })} />
            <button className="primary" type="submit">创建管理员</button>
          </form>
        </div>
        <div className="ops-block">
          <h4>邀请码</h4>
          <form className="inline-form" onSubmit={createInvite}>
            <input placeholder="新邀请码" value={invite.code} onChange={(e) => setInvite({ ...invite, code: e.target.value })} />
            <input type="number" min={1} value={invite.maxUses} onChange={(e) => setInvite({ ...invite, maxUses: Number(e.target.value) })} />
            <input placeholder="过期时间 ISO，可空" value={invite.expiresAt} onChange={(e) => setInvite({ ...invite, expiresAt: e.target.value })} />
            <button className="primary" type="submit">创建邀请码</button>
          </form>
        </div>
        <div className="table-actions" style={{ marginBottom: 12 }}>
          <button type="button" onClick={() => { void exportOps('invites', 'csv') }}>邀请码CSV</button>
          <button type="button" onClick={() => { void exportOps('invites', 'xlsx') }}>邀请码Excel</button>
          <button type="button" onClick={() => { void exportOps('invites', 'docx') }}>邀请码Word</button>
          <button type="button" onClick={() => { void exportOps('admins', 'csv') }}>管理员CSV</button>
          <button type="button" onClick={() => { void exportOps('admins', 'docx') }}>管理员Word</button>
          <button type="button" onClick={() => { void exportOps('ops_snapshot', 'pdf') }}>运维快照PDF</button>
          <button type="button" onClick={() => { void exportOps('ops_snapshot', 'docx') }}>运维快照Word</button>
          <button type="button" onClick={() => { void exportAllProjectData() }}>导出全部数据</button>
        </div>
        <AdminTable title="邀请码列表" rows={admin.invites} extra={(row) => <button type="button" onClick={() => { void toggleInvite(Number(row.id)) }}>{row.enabled === true ? '停用' : '启用'}</button>} />
      </section>}
      {adminTab === 'monitor' && <div className="admin-v2-stack">
        <section className="report-card compact admin-v2-card">
          <h3>监控导出</h3>
          <div className="table-actions monitor-export-controls">
            <label><input type="checkbox" checked={monitorItems.submissions} onChange={(e) => setMonitorItems({ ...monitorItems, submissions: e.target.checked })} />答卷记录</label>
            <label><input type="checkbox" checked={monitorItems.logs} onChange={(e) => setMonitorItems({ ...monitorItems, logs: e.target.checked })} />操作日志</label>
            <button type="button" onClick={() => { void exportMonitor('csv') }}>CSV</button>
            <button type="button" onClick={() => { void exportMonitor('xlsx') }}>Excel</button>
            <button type="button" onClick={() => { void exportMonitor('pdf') }}>PDF汇总</button>
            <button type="button" onClick={() => { void exportMonitor('docx') }}>Word汇总</button>
          </div>
        </section>
        <AdminTable title="答题记录（快捷）" rows={admin.submissions.slice(0, 20)} extra={(row) => <button type="button" onClick={() => { void openSubmissionByUser(row) }}>详情（含个人信息/报告）</button>} />
        <section className="report-card compact admin-v2-card">
          <div className="section-head">
            <h3>操作日志（最近100条）</h3>
            <button type="button" onClick={() => { void exportAuditAll() }}>导出全部日志CSV</button>
          </div>
          <AdminTable title="操作日志（最近100条）" rows={admin.logs} />
        </section>
      </div>}
      {adminTab === 'protocol' && <section className="report-card compact admin-v2-card">
        <h3>协议管理</h3>
        {protocolLoading && <p className="hint">加载中...</p>}
        {protocolError && (
          <div className="table-actions">
            <p className="message">{protocolError}</p>
            <button type="button" onClick={() => { void loadProtocol() }}>重试</button>
          </div>
        )}
        {legalBundle && (
          <>
            <div className="grid-form">
              <label>用户协议标题<input value={legalBundle.userAgreement.title} onChange={(e) => setLegalBundle({ ...legalBundle, userAgreement: { ...legalBundle.userAgreement, title: e.target.value } })} /></label>
              <label>用户协议版本<input value={legalBundle.userAgreement.version} onChange={(e) => setLegalBundle({ ...legalBundle, userAgreement: { ...legalBundle.userAgreement, version: e.target.value } })} /></label>
            </div>
            <label>用户协议内容（Markdown）</label>
            <textarea className="legal-editor" rows={10} value={legalBundle.userAgreement.contentMd} onChange={(e) => setLegalBundle({ ...legalBundle, userAgreement: { ...legalBundle.userAgreement, contentMd: e.target.value } })} />
            <div className="grid-form" style={{ marginTop: 16 }}>
              <label>隐私条款标题<input value={legalBundle.privacyPolicy.title} onChange={(e) => setLegalBundle({ ...legalBundle, privacyPolicy: { ...legalBundle.privacyPolicy, title: e.target.value } })} /></label>
              <label>隐私条款版本<input value={legalBundle.privacyPolicy.version} onChange={(e) => setLegalBundle({ ...legalBundle, privacyPolicy: { ...legalBundle.privacyPolicy, version: e.target.value } })} /></label>
            </div>
            <label>隐私条款内容（Markdown）</label>
            <textarea className="legal-editor" rows={10} value={legalBundle.privacyPolicy.contentMd} onChange={(e) => setLegalBundle({ ...legalBundle, privacyPolicy: { ...legalBundle.privacyPolicy, contentMd: e.target.value } })} />
            <div className="table-actions" style={{ marginTop: 10 }}>
              <button className="primary" type="button" onClick={async () => {
                try {
                  await api<LegalDocument>('/admin/legal/user-agreement', {
                    method: 'PUT',
                    body: JSON.stringify({
                      title: legalBundle.userAgreement.title,
                      version: legalBundle.userAgreement.version,
                      contentMd: legalBundle.userAgreement.contentMd,
                    }),
                  })
                  await api<LegalDocument>('/admin/legal/privacy-policy', {
                    method: 'PUT',
                    body: JSON.stringify({
                      title: legalBundle.privacyPolicy.title,
                      version: legalBundle.privacyPolicy.version,
                      contentMd: legalBundle.privacyPolicy.contentMd,
                    }),
                  })
                  await loadProtocol()
                  setMessage('协议已保存')
                } catch (error) {
                  setMessage(error instanceof Error ? error.message : '保存失败')
                }
              }}>保存协议</button>
              <button type="button" onClick={() => { void loadProtocol() }}>刷新</button>
            </div>
            <p className="hint">用户协议更新时间：{legalBundle.userAgreement.updatedAt || '-'} | 隐私条款更新时间：{legalBundle.privacyPolicy.updatedAt || '-'}</p>
            <div className="section-head" style={{ marginTop: 18 }}>
              <h3>用户同意记录</h3>
            </div>
            <div className="filter-bar compact">
              <input placeholder="按邮箱筛选" value={consentEmailQuery} onChange={(e) => setConsentEmailQuery(e.target.value)} />
              <button type="button" onClick={async () => {
                const query = consentEmailQuery.trim() ? `?email=${encodeURIComponent(consentEmailQuery.trim())}` : ''
                setConsentRows(await api<ConsentRow[]>(`/admin/legal/consents${query}`))
              }}>查询</button>
            </div>
            <div className="table-wrap">
              <table>
                <thead><tr><th>用户</th><th>协议类型</th><th>版本</th><th>是否同意</th><th>时间戳</th><th>IP</th></tr></thead>
                <tbody>
                  {consentRows.map((row) => (
                    <tr key={row.id}>
                      <td>{row.email}</td>
                      <td>{row.consentType === 'USER_AGREEMENT' ? '用户协议' : row.consentType === 'PRIVACY_POLICY' ? '隐私条款' : row.consentType}</td>
                      <td>{row.version}</td>
                      <td>{row.granted ? '同意' : '拒绝'}</td>
                      <td>{row.createdAt}</td>
                      <td>{row.ipAddress}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </>
        )}
      </section>}
      {message && <p className="message">{message}</p>}
      {userInsightOpen && selectedUser && userAssessments && <UserInsightModal
        selectedUser={selectedUser}
        overview={userAssessments}
        statusItems={statusItems}
        onReopen={reopenOne}
        onDownloadUser={downloadUserExport}
        onClose={() => setUserInsightOpen(false)}
      />}
      {userDetail && <UserModal user={userDetail} onClose={() => setUserDetail(null)} />}
      {reportPreview && <ReportPreviewModal item={reportPreview} onClose={() => setReportPreview(null)} />}
      {resetUser && <ResetPasswordModal user={resetUser} password={newPassword} setPassword={setNewPassword} onSubmit={resetPassword} onClose={() => setResetUser(null)} />}
      {groupModalOpen && (
        <ExportChoiceModal
          title="批量导出格式"
          onClose={() => setGroupModalOpen(false)}
          onChoose={(format) => {
            setGroupModalOpen(false)
            void downloadBatchUsers(format)
          }}
        />
      )}
    </section>
  )
}

function UserInsightModal({
  selectedUser,
  overview,
  statusItems,
  onReopen,
  onDownloadUser,
  onClose,
}: {
  selectedUser: Record<string, string | number | boolean>
  overview: UserAssessmentReportOverview
  statusItems: UserAssessmentStatus[]
  onReopen: (testPaperId: string) => void
  onDownloadUser: (userId: number, format: ExportFormat, reportId?: number) => Promise<void>
  onClose: () => void
}) {
  const [selectedTestPaperId, setSelectedTestPaperId] = useState<string | null>(null)
  const [exportOpen, setExportOpen] = useState(false)
  const selected = overview.assessments.find((item) => item.testPaperId === selectedTestPaperId && item.completed)
  const totalAssessments = overview.assessments.length || 3
  const completedCount = overview.assessments.filter((item) => item.completed).length
  const scoreReady = Boolean(overview.comprehensiveReady)
  const progressText = overview.completionProgress || `${completedCount}/${totalAssessments}`

  const doExport = async (format: ExportFormat) => {
    if (!selected) return
    await onDownloadUser(Number(selectedUser.id), format, Number(selected.reportId || 0))
  }

  return (
    <div className="modal-backdrop" role="dialog" aria-modal="true">
      <section className="modal">
        <div className="section-head">
          <div>
            <h2>用户详情</h2>
            <p className="hint">{String(selectedUser.email || selectedUser.id)}</p>
          </div>
          <button type="button" onClick={onClose}>关闭</button>
        </div>
        <section className="insight-score-card">
          <div>
            <p className="insight-score-label">综合评分</p>
            <h3>{scoreReady ? `${overview.comprehensiveLevel || '-'} · ${overview.comprehensiveScore ?? '-'}分` : '尚未完成全部试卷'}</h3>
            <p className="hint">{scoreReady ? `完成进度（${progressText}）` : `尚未完成全部试卷（${progressText}）`}</p>
            {!!overview.comprehensiveTriggers?.length && (
              <p className="hint">触发依据：{overview.comprehensiveTriggers.join('；')}</p>
            )}
          </div>
          <span className={`insight-score-badge ${scoreReady ? 'ready' : 'pending'}`}>{progressText}</span>
        </section>

        {!selected && (
          <>
            <div className="detail-summary">
              {Object.entries(overview.user).map(([key, value]) => <span key={key}><strong>{fieldLabel(key)}</strong>{String(value)}</span>)}
            </div>
            <div className="table-wrap">
              <table>
                <thead><tr><th>量表</th><th>状态</th><th>历史次数</th><th>最近提交</th><th>报告</th><th>重开</th></tr></thead>
                <tbody>
                  {overview.assessments.map((item) => {
                    const status = statusItems.find((s) => s.testPaperId === item.testPaperId)
                    const statusText = !item.completed ? '未开始' : status?.canRetake ? '已重开' : '已完成'
                    return (
                      <tr key={item.testPaperId}>
                        <td>{item.title}</td>
                        <td>{statusText}</td>
                        <td>{item.submissionCount}</td>
                        <td>{item.latestSubmittedAt || '-'}</td>
                        <td>{item.completed ? <button type="button" onClick={() => setSelectedTestPaperId(item.testPaperId)}>查看报告</button> : '-'}</td>
                        <td><button type="button" disabled={!item.completed} onClick={() => { void onReopen(item.testPaperId) }}>重开</button></td>
                      </tr>
                    )
                  })}
                </tbody>
              </table>
            </div>
          </>
        )}

        {selected && (
          <>
            <div className="section-head">
              <div>
                <h3>{selected.title} 报告</h3>
                <p className="hint">{selected.latestSubmittedAt ? `提交时间：${selected.latestSubmittedAt}` : ''}</p>
              </div>
              <div className="table-actions">
                <button type="button" onClick={() => setSelectedTestPaperId(null)}>返回</button>
                <button type="button" onClick={() => setExportOpen(true)}>导出</button>
              </div>
            </div>
            <AdminReportTemplate report={selected} />
          </>
        )}
      </section>
      {exportOpen && (
        <ExportChoiceModal
          title="选择导出格式"
          onClose={() => setExportOpen(false)}
          onChoose={(format) => {
            setExportOpen(false)
            void doExport(format)
          }}
        />
      )}
    </div>
  )
}

function AdminReportTemplate({
  report,
}: {
  report: UserAssessmentReportOverview['assessments'][number]
}) {
  const isSds = report.testPaperId.includes('2082')
  const isSas = report.testPaperId.includes('2083')
  const score = Number(report.summary || 0)

  const level =
    isSds
      ? (score >= 73 ? '重度抑郁' : score >= 63 ? '中度抑郁' : score >= 53 ? '轻度抑郁' : '正常')
      : isSas
        ? (score >= 70 ? '重度焦虑' : score >= 60 ? '中度焦虑' : score >= 50 ? '轻度焦虑' : '正常')
        : ''

  const intro = isSds
    ? '抑郁自评量表(Self-rating depression scale, 简称 SDS)，是由美国杜克大学教授庄 (William W.K.Zung) 于1965年编制。包括20个项目，每个项目按症状出现的频度分为四级评分，其中10个为正向评分，10个为反向评分。'
    : isSas
      ? '焦虑自评量表 (Self - raing Anxiety Scale, 简称 SAS) 是由美国杜克大学教授庄 (William W.K.Zung) 于1971年编制。包括20个项目，每个项目按症状出现的频度分为四级评分，其中15个正向评分，5个反向评分。'
      : ''

  const threshold = isSds
    ? '53以下正常；53-62轻度抑郁；63-72中度抑郁；73以上重度抑郁。'
    : isSas
      ? '50以下正常；50-59轻度焦虑；60-69中度焦虑；70以上重度焦虑。'
      : ''

  if (!isSds && !isSas) {
    return (
      <div className="report-card compact">
        <h3>结论</h3>
        <p><strong>核心结论：</strong>{report.summary || '暂无结论'}</p>
        <p><strong>建议：</strong>{report.suggestions || '暂无建议'}</p>
      </div>
    )
  }

  return (
    <article className="admin-report-template">
      <section className="module">
        <span className="tag">一、量表简介</span>
        <p className="intro">{intro}</p>
      </section>
      <section className="module">
        <span className="tag">二、结果判定</span>
        <div className="table-wrap">
          <table>
            <thead><tr><th>评定结果</th><th>标准分</th><th>阈值区间</th></tr></thead>
            <tbody>
              <tr><td>{level}</td><td>{report.summary || '-'}</td><td>{threshold}</td></tr>
            </tbody>
          </table>
        </div>
      </section>
      <section className="module">
        <span className="tag">三、指导建议</span>
        <p className="intro">{report.suggestions || '暂无建议'}</p>
      </section>
    </article>
  )
}

function ExportChoiceModal({ title, onClose, onChoose }: {
  title: string
  onClose: () => void
  onChoose: (format: ExportFormat) => void
}) {
  return (
    <div className="modal-backdrop" role="dialog" aria-modal="true">
      <section className="modal small-modal">
        <div className="section-head">
          <h2>{title}</h2>
          <button type="button" onClick={onClose}>关闭</button>
        </div>
        <div className="table-actions">
          <button type="button" onClick={() => onChoose('csv')}>CSV</button>
          <button type="button" onClick={() => onChoose('xlsx')}>Excel</button>
          <button type="button" onClick={() => onChoose('pdf')}>PDF</button>
          <button type="button" onClick={() => onChoose('docx')}>Word</button>
        </div>
      </section>
    </div>
  )
}

function ReportPreviewModal({ item, onClose }: {
  item: { title: string; summary: string; submittedAt: string }
  onClose: () => void
}) {
  return (
    <div className="modal-backdrop" role="dialog" aria-modal="true">
      <section className="modal">
        <div className="section-head">
          <div>
            <p className="eyebrow">Report Preview</p>
            <h2>{item.title} 报告</h2>
            <p className="hint">{item.submittedAt ? `提交时间：${item.submittedAt}` : ''}</p>
          </div>
          <button type="button" onClick={onClose}>关闭</button>
        </div>
        <div className="report-card compact">
          <h3>结论</h3>
          <p>{item.summary}</p>
        </div>
      </section>
    </div>
  )
}

function AdminLegacyPage({ admin, setAdmin, api, reload, token, loading }: { admin: AdminState; setAdmin: (state: AdminState) => void; api: <T>(path: string, init?: RequestInit) => Promise<T>; reload: () => void; token: string; loading: boolean }) {
  const [invite, setInvite] = useState({ code: '', maxUses: 50, expiresAt: '' })
  const [adminUser, setAdminUser] = useState({ email: '', password: '' })
  const [detail, setDetail] = useState<SubmissionDetail | null>(null)
  const [userDetail, setUserDetail] = useState<Record<string, string | number | boolean> | null>(null)
  const [resetUser, setResetUser] = useState<Record<string, string | number | boolean> | null>(null)
  const [userAssessments, setUserAssessments] = useState<UserAssessmentReportOverview | null>(null)
  const [assessmentStatusUser, setAssessmentStatusUser] = useState<Record<string, string | number | boolean> | null>(null)
  const [assessmentStatuses, setAssessmentStatuses] = useState<UserAssessmentStatus[]>([])
  const [newPassword, setNewPassword] = useState('')
  const [filters, setFilters] = useState({ assessment: '', organization: '', level: '', from: '', to: '' })
  const [message, setMessage] = useState('')

  const query = () => {
    const params = new URLSearchParams()
    Object.entries(filters).forEach(([key, value]) => {
      if (value) params.set(key, value)
    })
    return params.toString() ? `?${params.toString()}` : ''
  }

  const createInvite = async (event: FormEvent) => {
    event.preventDefault()
    try {
      await api('/admin/invitation-codes', { method: 'POST', body: JSON.stringify(invite) })
      setInvite({ code: '', maxUses: 50, expiresAt: '' })
      setMessage('邀请码已创建')
      reload()
    } catch (error) {
      setMessage(error instanceof Error ? error.message : '创建失败')
    }
  }

  const createAdmin = async (event: FormEvent) => {
    event.preventDefault()
    try {
      await api('/admin/users/admin', { method: 'POST', body: JSON.stringify(adminUser) })
      setAdminUser({ email: '', password: '' })
      setMessage('管理员账号已创建')
      reload()
    } catch (error) {
      setMessage(error instanceof Error ? error.message : '创建失败')
    }
  }

  const toggleInvite = async (id: number) => {
    await api(`/admin/invitation-codes/${id}/toggle`, { method: 'PATCH' })
    reload()
  }

  const toggleUser = async (id: number) => {
    await api(`/admin/users/${id}/toggle`, { method: 'PATCH' })
    reload()
  }

  const openSubmission = async (id: number) => {
    setDetail(await api<SubmissionDetail>(`/admin/submissions/${id}`))
  }

  const openUser = async (id: number) => {
    setUserDetail(await api<Record<string, string | number | boolean>>(`/admin/users/${id}`))
  }
  const openUserAssessments = async (id: number) => {
    setUserAssessments(await api<UserAssessmentReportOverview>(`/admin/users/${id}/assessment-reports`))
  }

  const openAssessmentStatus = async (user: Record<string, string | number | boolean>) => {
    const userId = Number(user.id)
    setAssessmentStatusUser(user)
    setAssessmentStatuses(await api<UserAssessmentStatus[]>(`/admin/users/${userId}/assessments`))
  }

  const reopenAssessment = async (userId: number, testPaperId: string) => {
    await api(`/admin/users/${userId}/assessments/${encodeURIComponent(testPaperId)}/reopen`, { method: 'PATCH', body: JSON.stringify({ note: '管理员手动重开' }) })
    setMessage('已开放该量表重答')
    if (assessmentStatusUser) {
      setAssessmentStatuses(await api<UserAssessmentStatus[]>(`/admin/users/${userId}/assessments`))
    }
  }

  const resetPassword = async (event: FormEvent) => {
    event.preventDefault()
    if (!resetUser) return
    await api(`/admin/users/${resetUser.id}/password`, { method: 'PATCH', body: JSON.stringify({ password: newPassword }) })
    setMessage('用户密码已重置')
    setResetUser(null)
    setNewPassword('')
  }

  const applyFilters = async () => {
    const [users, submissions, reports] = await Promise.all([
      api<AdminState['users']>(`/admin/users${filters.organization ? `?organization=${encodeURIComponent(filters.organization)}` : ''}`),
      api<AdminState['submissions']>(`/admin/submissions${query()}`),
      api<AdminState['reports']>(`/admin/reports${query()}`),
    ])
    setMessage('筛选已应用')
    setAdmin({ ...admin, users, submissions, reports })
  }

  const clearFilters = async () => {
    setFilters({ assessment: '', organization: '', level: '', from: '', to: '' })
    await reload()
  }

  const exportFile = async (format: 'csv' | 'xlsx') => {
    const response = await fetch(`${API_BASE}/admin/submissions/export.${format}${format === 'xlsx' ? query() : ''}`, { headers: { Authorization: `Bearer ${token}` } })
    const blob = await response.blob()
    const url = URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = url
    link.download = `davos-submissions.${format}`
    link.click()
    URL.revokeObjectURL(url)
  }

  return (
    <section className="admin-shell">
      <div className="section-head">
        <p className="eyebrow">Admin Dashboard</p>
        <h2>后台管理中心</h2>
      </div>
      <div className="admin-metrics">
        <Metric icon={<Users />} label="用户" value={admin.dashboard?.users || 0} />
        <Metric icon={<FileText />} label="答卷" value={admin.dashboard?.submissions || 0} />
        <Metric icon={<BarChart3 />} label="报告" value={admin.dashboard?.reports || 0} />
        <Metric icon={<KeyRound />} label="邀请码" value={admin.dashboard?.invitationCodes || 0} />
      </div>
      {loading && <p className="message">后台数据加载中...</p>}
      <form className="inline-form" onSubmit={createInvite}>
        <input placeholder="新邀请码" value={invite.code} onChange={(e) => setInvite({ ...invite, code: e.target.value })} />
        <input type="number" min={1} value={invite.maxUses} onChange={(e) => setInvite({ ...invite, maxUses: Number(e.target.value) })} />
        <input placeholder="过期时间 ISO，可空" value={invite.expiresAt} onChange={(e) => setInvite({ ...invite, expiresAt: e.target.value })} />
        <button className="primary" type="submit">创建邀请码</button>
      </form>
      <form className="inline-form admin-create" onSubmit={createAdmin}>
        <input type="email" placeholder="管理员邮箱" value={adminUser.email} onChange={(e) => setAdminUser({ ...adminUser, email: e.target.value })} />
        <input type="password" placeholder="初始密码" value={adminUser.password} onChange={(e) => setAdminUser({ ...adminUser, password: e.target.value })} />
        <button className="primary" type="submit">创建管理员</button>
        <button type="button" onClick={() => exportFile('csv')}>导出 CSV</button>
        <button type="button" onClick={() => exportFile('xlsx')}>导出 Excel</button>
      </form>
      <div className="filter-bar">
        <input placeholder="按量表筛选" value={filters.assessment} onChange={(e) => setFilters({ ...filters, assessment: e.target.value })} />
        <input placeholder="按公司/机构筛选" value={filters.organization} onChange={(e) => setFilters({ ...filters, organization: e.target.value })} />
        <input placeholder="按报告等级筛选" value={filters.level} onChange={(e) => setFilters({ ...filters, level: e.target.value })} />
        <input type="date" value={filters.from} onChange={(e) => setFilters({ ...filters, from: e.target.value })} />
        <input type="date" value={filters.to} onChange={(e) => setFilters({ ...filters, to: e.target.value })} />
        <button className="primary" type="button" onClick={applyFilters}>筛选</button>
        <button type="button" onClick={clearFilters}>清空</button>
      </div>
      {message && <p className="message">{message}</p>}
      <AdminTable title="用户列表" rows={admin.users} extra={(row) => <div className="table-actions"><button type="button" onClick={() => openUser(Number(row.id))}>资料</button><button type="button" onClick={() => openUserAssessments(Number(row.id))}>答题与报告</button><button type="button" onClick={() => openAssessmentStatus(row)}>量表状态</button><button type="button" onClick={() => setResetUser(row)}>重置密码</button><button type="button" onClick={() => toggleUser(Number(row.id))}>{row.enabled === true ? '禁用' : '启用'}</button></div>} />
      <AdminTable title="答题记录" rows={admin.submissions} extra={(row) => <button type="button" onClick={() => openSubmission(Number(row.id))}>详情</button>} />
      <AdminTable
        title="报告列表"
        rows={admin.reports}
        extra={(row) => (
          <div className="table-actions">
            <button onClick={(event) => downloadPdf(event, Number(row.id), token)}>个人PDF</button>
            <button onClick={(event) => { void downloadGroupPdf(event, Number(row.id), token) }}>团体PDF</button>
          </div>
        )}
      />
      <AdminTable title="邀请码" rows={admin.invites} extra={(row) => <button type="button" onClick={() => toggleInvite(Number(row.id))}>{row.enabled === true ? '停用' : '启用'}</button>} />
      <AdminTable title="操作日志" rows={admin.logs} />
      {detail && <SubmissionModal detail={detail} token={token} onClose={() => setDetail(null)} />}
      {userDetail && <UserModal user={userDetail} onClose={() => setUserDetail(null)} />}
      {userAssessments && <UserAssessmentsModal data={userAssessments} token={token} onOpenSubmission={openSubmission} onClose={() => setUserAssessments(null)} />}
      {resetUser && <ResetPasswordModal user={resetUser} password={newPassword} setPassword={setNewPassword} onSubmit={resetPassword} onClose={() => setResetUser(null)} />}
      {assessmentStatusUser && <AssessmentStatusModal user={assessmentStatusUser} items={assessmentStatuses} onReopen={reopenAssessment} onClose={() => { setAssessmentStatusUser(null); setAssessmentStatuses([]) }} />}
    </section>
  )
}

function UserAssessmentsModal({
  data,
  token,
  onOpenSubmission,
  onClose,
}: {
  data: UserAssessmentReportOverview
  token: string
  onOpenSubmission: (id: number) => void
  onClose: () => void
}) {
  const openLatestSubmission = (submissionId?: number) => {
    if (submissionId) onOpenSubmission(Number(submissionId))
  }

  const profileKeys = [
    'name', 'gender', 'phone', 'idCard', 'birthMonth',
    'education', 'schoolMajor', 'organization', 'title', 'industry', 'city', 'attendeeType',
    'email', 'role', 'enabled', 'createdAt', 'id'
  ]
  const formatValue = (key: string, value: string | number | boolean) => {
    if (value === '' || value === null || value === undefined) return '未填写'
    if (key === 'enabled') return value ? '已启用' : '已禁用'
    if (key === 'createdAt' && typeof value === 'string') {
      const date = new Date(value)
      if (!Number.isNaN(date.getTime())) {
        return new Intl.DateTimeFormat('zh-CN', {
          year: 'numeric',
          month: '2-digit',
          day: '2-digit',
          hour: '2-digit',
          minute: '2-digit',
          second: '2-digit',
          hour12: false
        }).format(date)
      }
    }
    return String(value)
  }
  const visibleProfile = profileKeys
    .filter((key) => key in data.user)
    .map((key) => [key, data.user[key]] as const)

  return (
    <div className="modal-backdrop" role="dialog" aria-modal="true">
      <section className="modal modal-report-style">
        <div className="section-head">
          <div>
            <p className="eyebrow">User Detail</p>
            <h2>用户详情</h2>
          </div>
          <button type="button" onClick={onClose}>关闭</button>
        </div>

        <section className="report-module">
          <h3 className="report-module-tag">一、基本信息</h3>
          <div className="detail-summary">
            {visibleProfile.map(([key, value]) => (
              <span key={key}>
                <strong>{fieldLabel(key)}</strong>
                {formatValue(key, value)}
              </span>
            ))}
          </div>
        </section>

        <div className="report-card compact report-module">
          <h3 className="report-module-tag">二、综合评估</h3>
          <p>{data.comprehensiveEvaluation}</p>
        </div>

        <div className="table-wrap report-module">
          <div className="report-table-head">
            <h3 className="report-module-tag">三、量表与报告</h3>
          </div>
          <table>
            <thead><tr><th>量表</th><th>完成</th><th>次数</th><th>最近提交</th><th>报告</th></tr></thead>
            <tbody>
              {data.assessments.map((item) => (
                <tr key={item.testPaperId}>
                  <td>{item.title}</td>
                  <td>{item.completed ? '已完成' : '未完成'}</td>
                  <td>{item.submissionCount}</td>
                  <td>{item.latestSubmittedAt || '-'}</td>
                  <td>
                    {item.reportId ? (
                      <div className="table-actions">
                        <button type="button" onClick={(event) => downloadPdf(event, item.reportId || 0, token)}>个人PDF</button>
                        <button type="button" onClick={() => openLatestSubmission(item.latestSubmissionId)}>详情</button>
                      </div>
                    ) : '-'}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </section>
    </div>
  )
}

function AssessmentStatusModal({
  user,
  items,
  onReopen,
  onClose,
}: {
  user: Record<string, string | number | boolean>
  items: UserAssessmentStatus[]
  onReopen: (userId: number, testPaperId: string) => void
  onClose: () => void
}) {
  const userId = Number(user.id)
  return (
    <div className="modal-backdrop" role="dialog" aria-modal="true">
      <section className="modal">
        <div className="section-head">
          <div>
            <p className="eyebrow">Assessment Status</p>
            <h2>量表完成状态</h2>
            <p className="hint">用户：{String(user.email || userId)}</p>
          </div>
          <button type="button" onClick={onClose}>关闭</button>
        </div>
        <div className="table-wrap">
          <table>
            <thead><tr><th>量表</th><th>完成</th><th>历史次数</th><th>最近提交</th><th>重答权限</th><th>操作</th></tr></thead>
            <tbody>
              {items.map((item) => (
                <tr key={item.testPaperId}>
                  <td>{item.title}</td>
                  <td>{item.completed ? '是' : '否'}</td>
                  <td>{item.submissionCount}</td>
                  <td>{item.lastSubmittedAt || '-'}</td>
                  <td>{item.canRetake ? '已开放' : '未开放'}</td>
                  <td>
                    <button type="button" disabled={!item.completed || item.canRetake} onClick={() => onReopen(userId, item.testPaperId)}>
                      开放重答
                    </button>
                  </td>
                </tr>
              ))}
              {!items.length && <tr><td colSpan={6}>暂无数据</td></tr>}
            </tbody>
          </table>
        </div>
      </section>
    </div>
  )
}

function SubmissionModal({ detail, token, onClose }: { detail: SubmissionDetail; token: string; onClose: () => void }) {
  return (
    <div className="modal-backdrop" role="dialog" aria-modal="true">
      <section className="modal">
        <div className="section-head">
          <div>
            <p className="eyebrow">Submission Detail</p>
            <h2>答卷详情 #{String(detail.submission.id)}</h2>
          </div>
          <button type="button" onClick={onClose}>关闭</button>
        </div>
        <div className="detail-summary">
          {Object.entries(detail.submission).map(([key, value]) => <span key={key}><strong>{fieldLabel(key)}</strong>{String(value)}</span>)}
        </div>
        <div className="detail-summary">
          {Object.entries(detail.user).map(([key, value]) => <span key={key}><strong>{fieldLabel(key)}</strong>{String(value)}</span>)}
        </div>
        <div className="report-card compact">
          <h3>报告结论</h3>
          <p>{detail.reportSummary}</p>
          <p>{detail.suggestions}</p>
          {detail.reportId && (
            <>
              <button type="button" onClick={(event) => downloadPdf(event, detail.reportId || 0, token)}>下载个人PDF</button>
            </>
          )}
        </div>
        <div className="table-wrap">
          <table>
            <thead><tr><th>题号</th><th>题目</th><th>答案</th></tr></thead>
            <tbody>
              {detail.answers.map((answer) => <tr key={answer.questionCode}><td>{answer.questionCode}</td><td>{answer.prompt}</td><td>{answer.value}</td></tr>)}
            </tbody>
          </table>
        </div>
      </section>
    </div>
  )
}

function UserModal({ user, onClose }: { user: Record<string, string | number | boolean>; onClose: () => void }) {
  return (
    <div className="modal-backdrop" role="dialog" aria-modal="true">
      <section className="modal">
        <div className="section-head">
          <div>
            <p className="eyebrow">User Profile</p>
            <h2>用户完整资料</h2>
          </div>
          <button type="button" onClick={onClose}>关闭</button>
        </div>
        <div className="detail-summary">
          {Object.entries(user).map(([key, value]) => <span key={key}><strong>{fieldLabel(key)}</strong>{String(value)}</span>)}
        </div>
      </section>
    </div>
  )
}

function ResetPasswordModal({ user, password, setPassword, onSubmit, onClose }: {
  user: Record<string, string | number | boolean>
  password: string
  setPassword: (value: string) => void
  onSubmit: (event: FormEvent) => void
  onClose: () => void
}) {
  return (
    <div className="modal-backdrop" role="dialog" aria-modal="true">
      <form className="modal small-modal" onSubmit={onSubmit}>
        <div className="section-head">
          <div>
            <p className="eyebrow">Reset Password</p>
            <h2>重置密码</h2>
          </div>
          <button type="button" onClick={onClose}>关闭</button>
        </div>
        <p className="hint">用户：{String(user.email || user.id)}</p>
        <label>新密码<input required minLength={8} type="password" value={password} onChange={(e) => setPassword(e.target.value)} /></label>
        <button className="primary full" type="submit">确认重置</button>
      </form>
    </div>
  )
}

function Metric({ icon, label, value }: { icon: ReactNode; label: string; value: number }) {
  return <article>{icon}<span>{label}</span><strong>{value}</strong></article>
}

function AdminTable({ title, rows, extra }: { title: string; rows: Array<Record<string, string | number | boolean>>; extra?: (row: Record<string, string | number | boolean>) => ReactNode }) {
  const keys = Object.keys(rows[0] || {})
  return (
    <section className="data-section">
      <h3>{title}</h3>
      <div className="table-wrap">
        <table>
          <thead><tr>{keys.map((key) => <th key={key}>{key}</th>)}{extra && <th>操作</th>}</tr></thead>
          <tbody>
            {rows.map((row, index) => <tr key={index}>{keys.map((key) => <td key={key}>{String(row[key] ?? '')}</td>)}{extra && <td>{extra(row)}</td>}</tr>)}
            {!rows.length && <tr><td>暂无数据</td></tr>}
          </tbody>
        </table>
      </div>
    </section>
  )
}

function EmptyState({ title, text }: { title: string; text: string }) {
  return <section className="center-shell"><div className="form-card"><Lock /><h2>{title}</h2><p>{text}</p></div></section>
}

function fieldLabel(key: string) {
  const map: Record<string, string> = {
    id: 'ID',
    email: '邮箱',
    role: '角色',
    enabled: '启用状态',
    createdAt: '创建时间',
    name: '姓名',
    gender: '性别',
    idCard: '身份证号',
    phone: '联系方式',
    birthMonth: '出生日期',
    education: '学历',
    schoolMajor: '院校/专业',
    organization: '公司/机构',
    title: '职位',
    industry: '行业',
    city: '城市',
    attendeeType: '参会身份',
    assessment: '量表',
    rawScore: '分数',
    level: '等级',
    submittedAt: '提交时间',
    submissionId: '答卷ID',
  }
  return map[key] || key
}

function riskLevelClass(level: string) {
  if (level === '高风险') return 'high'
  if (level === '中风险') return 'medium'
  if (level === '低风险') return 'low'
  if (level === '正常') return 'normal'
  return 'pending'
}

export default App
