<script setup>
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Calendar, Camera, User } from '@element-plus/icons-vue'
import 'element-plus/es/components/message/style/css'
import 'element-plus/es/components/message-box/style/css'

import { fetchActiveAnnouncement, dismissAnnouncement } from '../api/announcements'
import { fetchSocialConfig } from '../api/auth'
import { fetchMyFeedbacks, submitFeedback } from '../api/feedback'
import {
  bindSocial,
  bindEmail,
  changePassword,
  createUserPasskeyOptions,
  createUserPasskeyVerify,
  deleteUserPasskey,
  fetchEmailNotify,
  fetchUserPasskeys,
  fetchUserEmail,
  fetchKdocsSettings,
  fetchSocialBindings,
  reportUserPasskeyClientError,
  unbindSocial,
  unbindEmail,
  updateKdocsSettings,
  updateEmailNotify,
} from '../api/settings'
import SocialLoginButtons from '../components/SocialLoginButtons.vue'
import { useUserStore } from '../stores/user'
import { createPasskey, getPasskeyClientErrorMessage, isPasskeyAvailable } from '../utils/passkey'
import { validateStrongPassword } from '../utils/password'

const route = useRoute()
const router = useRouter()
const userStore = useUserStore()

const isMobile = ref(false)
const drawerOpen = ref(false)
let mediaQuery

const announcementOpen = ref(false)
const announcement = ref(null)
const announcementLoading = ref(false)

const announcementPageToken = (() => {
  try {
    const timeOrigin = window.performance?.timeOrigin
    if (typeof timeOrigin === 'number' && Number.isFinite(timeOrigin)) return String(timeOrigin)
  } catch {
    // ignore
  }
  return String(Date.now())
})()

function announcementOnceKey(announcementId) {
  return `announcement_closed_once_${announcementId}`
}

function announcementPermanentKey(announcementId) {
  return `announcement_closed_${announcementId}`
}

function wasAnnouncementClosedOnce(announcementId) {
  try {
    return window.sessionStorage.getItem(announcementOnceKey(announcementId)) === announcementPageToken
  } catch {
    return false
  }
}

function wasAnnouncementClosedPermanently(announcementId) {
  try {
    return window.localStorage.getItem(announcementPermanentKey(announcementId)) === '1'
  } catch {
    return false
  }
}

function markAnnouncementClosedOnce(announcementId) {
  try {
    window.sessionStorage.setItem(announcementOnceKey(announcementId), announcementPageToken)
  } catch {
    // ignore
  }
}

function markAnnouncementClosedPermanently(announcementId) {
  try {
    window.localStorage.setItem(announcementPermanentKey(announcementId), '1')
  } catch {
    // ignore
  }
}

const feedbackOpen = ref(false)
const feedbackTab = ref('new')
const feedbackSubmitting = ref(false)
const feedbackLoading = ref(false)
const myFeedbacks = ref([])
const feedbackForm = reactive({
  title: '',
  description: '',
  contact: '',
})

const settingsOpen = ref(false)
const settingsTab = ref('email')

const emailLoading = ref(false)
const bindEmailLoading = ref(false)
const emailInfo = reactive({
  email: '',
  email_verified: false,
})
const bindEmailValue = ref('')

const emailNotifyLoading = ref(false)
const emailNotifyEnabled = ref(true)

const passwordLoading = ref(false)
const passwordForm = reactive({
  current_password: '',
  new_password: '',
  confirm_password: '',
})

const kdocsLoading = ref(false)
const kdocsSaving = ref(false)
const kdocsUnitValue = ref('')
const passkeyLoading = ref(false)
const passkeyAddLoading = ref(false)
const passkeyDeviceName = ref('')
const passkeyItems = ref([])
const passkeyRegisterOptions = ref(null)
const passkeyRegisterOptionsAt = ref(0)
const PASSKEY_OPTIONS_PREFETCH_MAX_AGE_MS = 240000

const socialConfig = ref({ enabled: false, providers: [] })
const socialBindings = ref([])
const socialBindingsLoading = ref(false)
const socialBindLoading = ref(false)
const pendingSettingsBindKey = 'zsglpt_social_settings_bind_token'

function syncIsMobile() {
  isMobile.value = Boolean(mediaQuery?.matches)
  if (!isMobile.value) drawerOpen.value = false
}

onMounted(() => {
  mediaQuery = window.matchMedia('(max-width: 768px)')
  mediaQuery.addEventListener?.('change', syncIsMobile)
  syncIsMobile()

  userStore.refreshVipInfo().catch(() => {
    window.location.href = '/login'
  })

  loadAnnouncement()
})

onBeforeUnmount(() => {
  mediaQuery?.removeEventListener?.('change', syncIsMobile)
})

const menuItems = [
  { path: '/app/accounts', label: '账号管理', icon: User },
  { path: '/app/schedules', label: '定时任务', icon: Calendar },
  { path: '/app/screenshots', label: '截图管理', icon: Camera },
]

const activeMenu = computed(() => route.path)

async function go(path) {
  await router.push(path)
  drawerOpen.value = false
}

async function logout() {
  let confirmed = false
  try {
    await ElMessageBox.confirm('确定退出登录吗？', '退出登录', {
      confirmButtonText: '退出',
      cancelButtonText: '取消',
      type: 'warning',
    })
    confirmed = true
  } catch (error) {
    const reason = String(error || '').toLowerCase()
    if (reason === 'cancel' || reason === 'close') return
    try {
      confirmed = window.confirm('确定退出登录吗？')
    } catch {
      confirmed = false
    }
  }

  if (!confirmed) return

  await userStore.logout()
  window.location.href = '/login'
}

function openFeedbackForm() {
  feedbackTab.value = 'new'
  feedbackForm.title = ''
  feedbackForm.description = ''
  feedbackForm.contact = ''
  feedbackOpen.value = true
}

async function openMyFeedbacks() {
  feedbackTab.value = 'list'
  feedbackOpen.value = true
  await loadMyFeedbacks()
}

async function loadMyFeedbacks() {
  feedbackLoading.value = true
  try {
    const list = await fetchMyFeedbacks()
    myFeedbacks.value = Array.isArray(list) ? list : []
  } catch {
    myFeedbacks.value = []
  } finally {
    feedbackLoading.value = false
  }
}

function feedbackStatusLabel(status) {
  if (status === 'replied') return '已回复'
  if (status === 'closed') return '已关闭'
  return '待处理'
}

function feedbackStatusTagType(status) {
  if (status === 'replied') return 'success'
  if (status === 'closed') return 'info'
  return 'warning'
}

async function submitFeedbackForm() {
  const title = feedbackForm.title.trim()
  const description = feedbackForm.description.trim()
  const contact = feedbackForm.contact.trim()

  if (!title || !description) {
    ElMessage.error('标题和描述不能为空')
    return
  }

  feedbackSubmitting.value = true
  try {
    const res = await submitFeedback({ title, description, contact })
    ElMessage.success(res?.message || '反馈提交成功')
    feedbackOpen.value = false
    feedbackForm.title = ''
    feedbackForm.description = ''
    feedbackForm.contact = ''
  } catch (e) {
    const data = e?.response?.data
    ElMessage.error(data?.error || '提交失败')
  } finally {
    feedbackSubmitting.value = false
  }
}

async function openSettings() {
  settingsOpen.value = true
  settingsTab.value = 'email'
  await loadSettings()
}

async function loadSettings() {
  await Promise.all([loadEmailInfo(), loadEmailNotify(), loadKdocsSettings(), loadPasskeys(), loadSocialBindings()])
}

function socialBindRedirectUri() {
  const url = new URL(window.location.href)
  url.pathname = '/social-bind-callback'
  url.search = ''
  url.hash = ''
  return url.toString()
}

async function loadSocialBindings() {
  socialBindingsLoading.value = true
  try {
    const [config, bindings] = await Promise.all([fetchSocialConfig(), fetchSocialBindings()])
    socialConfig.value = {
      enabled: Boolean(config?.enabled),
      providers: Array.isArray(config?.providers) ? config.providers : [],
    }
    socialBindings.value = Array.isArray(bindings?.items) ? bindings.items : []
    await consumePendingSocialBind()
  } catch {
    socialConfig.value = { enabled: false, providers: [] }
    socialBindings.value = []
  } finally {
    socialBindingsLoading.value = false
  }
}

async function consumePendingSocialBind() {
  let token = ''
  try {
    token = window.sessionStorage.getItem(pendingSettingsBindKey) || ''
  } catch {
    token = ''
  }
  if (!token || socialBindLoading.value) return
  socialBindLoading.value = true
  try {
    await bindSocial({ bind_token: token })
    window.sessionStorage.removeItem(pendingSettingsBindKey)
    ElMessage.success('快捷登录已绑定')
    const bindings = await fetchSocialBindings()
    socialBindings.value = Array.isArray(bindings?.items) ? bindings.items : []
  } catch (e) {
    const data = e?.response?.data
    ElMessage.error(data?.error || '快捷登录绑定失败')
  } finally {
    socialBindLoading.value = false
  }
}

async function onUnbindSocial(item) {
  try {
    await ElMessageBox.confirm(`确定解绑${item?.provider_label || '快捷登录'}吗？`, '解绑快捷登录', {
      confirmButtonText: '解绑',
      cancelButtonText: '取消',
      type: 'warning',
    })
  } catch {
    return
  }
  try {
    await unbindSocial(item.provider)
    ElMessage.success('已解绑')
    await loadSocialBindings()
  } catch (e) {
    const data = e?.response?.data
    ElMessage.error(data?.error || '解绑失败')
  }
}

async function loadEmailInfo() {
  emailLoading.value = true
  try {
    const data = await fetchUserEmail()
    emailInfo.email = data?.email || ''
    emailInfo.email_verified = Boolean(data?.email_verified)
    bindEmailValue.value = emailInfo.email || ''
  } catch {
    emailInfo.email = ''
    emailInfo.email_verified = false
    bindEmailValue.value = ''
  } finally {
    emailLoading.value = false
  }
}

async function loadEmailNotify() {
  emailNotifyLoading.value = true
  try {
    const data = await fetchEmailNotify()
    emailNotifyEnabled.value = Boolean(data?.enabled)
  } catch {
    emailNotifyEnabled.value = true
  } finally {
    emailNotifyLoading.value = false
  }
}

async function loadKdocsSettings() {
  kdocsLoading.value = true
  try {
    const data = await fetchKdocsSettings()
    kdocsUnitValue.value = data?.kdocs_unit || ''
  } catch {
    kdocsUnitValue.value = ''
  } finally {
    kdocsLoading.value = false
  }
}

async function saveKdocsSettings() {
  kdocsSaving.value = true
  try {
    await updateKdocsSettings({ kdocs_unit: kdocsUnitValue.value.trim() })
    ElMessage.success('已更新表格县区设置')
  } catch {
    // handled by interceptor
  } finally {
    kdocsSaving.value = false
  }
}

async function loadPasskeys() {
  passkeyLoading.value = true
  try {
    const data = await fetchUserPasskeys()
    passkeyItems.value = Array.isArray(data?.items) ? data.items : []
    if (passkeyItems.value.length < 3) {
      await prefetchPasskeyRegisterOptions()
    } else {
      passkeyRegisterOptions.value = null
      passkeyRegisterOptionsAt.value = 0
    }
  } catch {
    passkeyItems.value = []
    passkeyRegisterOptions.value = null
    passkeyRegisterOptionsAt.value = 0
  } finally {
    passkeyLoading.value = false
  }
}

function getCachedPasskeyRegisterOptions() {
  if (!passkeyRegisterOptions.value) return null
  if (Date.now() - Number(passkeyRegisterOptionsAt.value || 0) > PASSKEY_OPTIONS_PREFETCH_MAX_AGE_MS) return null
  return passkeyRegisterOptions.value
}

async function prefetchPasskeyRegisterOptions() {
  try {
    const res = await createUserPasskeyOptions({})
    passkeyRegisterOptions.value = res
    passkeyRegisterOptionsAt.value = Date.now()
  } catch {
    passkeyRegisterOptions.value = null
    passkeyRegisterOptionsAt.value = 0
  }
}

async function onAddPasskey() {
  if (!isPasskeyAvailable()) {
    ElMessage.error('当前浏览器或环境不支持Passkey（需 HTTPS）')
    return
  }
  if (passkeyItems.value.length >= 3) {
    ElMessage.error('最多可绑定3台设备')
    return
  }

  passkeyAddLoading.value = true
  try {
    let optionsRes = getCachedPasskeyRegisterOptions()
    if (!optionsRes) {
      optionsRes = await createUserPasskeyOptions({})
    }
    const credential = await createPasskey(optionsRes?.publicKey || {})
    await createUserPasskeyVerify({ credential, device_name: passkeyDeviceName.value.trim() })
    passkeyRegisterOptions.value = null
    passkeyRegisterOptionsAt.value = 0
    passkeyDeviceName.value = ''
    ElMessage.success('Passkey设备添加成功')
    await loadPasskeys()
  } catch (e) {
    try {
      await reportUserPasskeyClientError({
        stage: 'register',
        source: 'user-settings',
        name: e?.name || '',
        message: e?.message || '',
        code: e?.code || '',
        user_agent: navigator.userAgent || '',
      })
    } catch {
      // ignore report failure
    }
    passkeyRegisterOptions.value = null
    passkeyRegisterOptionsAt.value = 0
    await prefetchPasskeyRegisterOptions()
    const data = e?.response?.data
    const message =
      data?.error ||
      getPasskeyClientErrorMessage(e, 'Passkey注册')
    ElMessage.error(message)
  } finally {
    passkeyAddLoading.value = false
  }
}

async function onDeletePasskey(item) {
  try {
    await ElMessageBox.confirm(`确定删除设备「${item?.device_name || '未命名设备'}」吗？`, '删除Passkey设备', {
      confirmButtonText: '删除',
      cancelButtonText: '取消',
      type: 'warning',
    })
  } catch {
    return
  }

  try {
    await deleteUserPasskey(item.id)
    ElMessage.success('设备已删除')
    await loadPasskeys()
  } catch (e) {
    const data = e?.response?.data
    ElMessage.error(data?.error || '删除失败')
  }
}

async function onBindEmail() {
  const email = bindEmailValue.value.trim().toLowerCase()
  if (!email) {
    ElMessage.error('请输入邮箱地址')
    return
  }
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  if (!emailRegex.test(email)) {
    ElMessage.error('邮箱格式不正确')
    return
  }

  bindEmailLoading.value = true
  try {
    const res = await bindEmail({ email })
    ElMessage.success(res?.message || '验证邮件已发送')
    emailInfo.email = email
    emailInfo.email_verified = false
  } catch (e) {
    const data = e?.response?.data
    ElMessage.error(data?.error || '绑定失败')
  } finally {
    bindEmailLoading.value = false
  }
}

async function onUnbindEmail() {
  try {
    await ElMessageBox.confirm('确定要解绑当前邮箱吗？', '解绑邮箱', {
      confirmButtonText: '解绑',
      cancelButtonText: '取消',
      type: 'warning',
    })
  } catch {
    return
  }

  try {
    const res = await unbindEmail()
    if (res?.success) {
      ElMessage.success(res?.message || '邮箱已解绑')
      await loadEmailInfo()
      return
    }
    ElMessage.error(res?.error || '解绑失败')
  } catch (e) {
    const data = e?.response?.data
    ElMessage.error(data?.error || '解绑失败')
  }
}

async function onToggleEmailNotify(value) {
  const previous = emailNotifyEnabled.value
  emailNotifyEnabled.value = Boolean(value)
  emailNotifyLoading.value = true
  try {
    const res = await updateEmailNotify({ enabled: Boolean(value) })
    if (res?.success) {
      ElMessage.success('已更新')
      return
    }
    emailNotifyEnabled.value = previous
    ElMessage.error(res?.error || '更新失败')
  } catch (e) {
    emailNotifyEnabled.value = previous
    const data = e?.response?.data
    ElMessage.error(data?.error || '更新失败')
  } finally {
    emailNotifyLoading.value = false
  }
}

async function onChangePassword() {
  const currentPassword = passwordForm.current_password
  const newPassword = passwordForm.new_password
  const confirmPassword = passwordForm.confirm_password

  if (!currentPassword || !newPassword || !confirmPassword) {
    ElMessage.error('请填写完整信息')
    return
  }
  const passwordCheck = validateStrongPassword(newPassword)
  if (!passwordCheck.ok) {
    ElMessage.error(passwordCheck.message)
    return
  }
  if (newPassword !== confirmPassword) {
    ElMessage.error('两次输入的新密码不一致')
    return
  }

  passwordLoading.value = true
  try {
    const res = await changePassword({ current_password: currentPassword, new_password: newPassword })
    if (res?.success) {
      ElMessage.success('密码修改成功')
      passwordForm.current_password = ''
      passwordForm.new_password = ''
      passwordForm.confirm_password = ''
      return
    }
    ElMessage.error(res?.error || '修改失败')
  } catch (e) {
    const data = e?.response?.data
    ElMessage.error(data?.error || '修改失败')
  } finally {
    passwordLoading.value = false
  }
}

async function loadAnnouncement() {
  announcementLoading.value = true
  try {
    const data = await fetchActiveAnnouncement()
    const ann = data?.announcement
    if (!ann?.id) return

    if (wasAnnouncementClosedPermanently(ann.id)) return
    if (wasAnnouncementClosedOnce(ann.id)) return

    announcement.value = ann
    announcementOpen.value = true
  } catch {
    // ignore
  } finally {
    announcementLoading.value = false
  }
}

function closeAnnouncementOnce() {
  const ann = announcement.value
  if (ann?.id) markAnnouncementClosedOnce(ann.id)
  announcementOpen.value = false
}

async function dismissAnnouncementPermanently() {
  const ann = announcement.value
  if (!ann?.id) {
    announcementOpen.value = false
    return
  }
  markAnnouncementClosedPermanently(ann.id)
  try {
    const res = await dismissAnnouncement(ann.id)
    if (res?.success) ElMessage.success('已永久关闭')
  } catch {
    // ignore
  } finally {
    announcementOpen.value = false
  }
}
</script>

<template>
  <el-container class="layout-root">
    <el-aside v-if="!isMobile" width="220px" class="layout-aside">
      <div class="brand">
        <div class="brand-title">知识管理平台</div>
        <div class="brand-sub app-muted">用户中心</div>
      </div>

      <el-menu :default-active="activeMenu" class="aside-menu" router @select="go">
        <el-menu-item v-for="item in menuItems" :key="item.path" :index="item.path">
          <el-icon><component :is="item.icon" /></el-icon>
          <span>{{ item.label }}</span>
        </el-menu-item>
      </el-menu>
    </el-aside>

    <el-container>
      <el-header class="layout-header">
        <div class="header-left">
          <el-button v-if="isMobile" text class="header-menu-btn" @click="drawerOpen = true">
            菜单
          </el-button>
          <div class="header-title">用户控制台</div>
        </div>

        <div class="header-right">
          <div class="user-meta">
            <el-tag v-if="userStore.isVip" type="success" size="small" effect="light">VIP</el-tag>
            <el-tag v-else type="info" size="small" effect="light">普通</el-tag>
            <span class="user-name">{{ userStore.username || '用户' }}</span>
            <span v-if="userStore.isVip && userStore.vipDaysLeft <= 7 && userStore.vipDaysLeft > 0" class="vip-warn">
              ({{ userStore.vipDaysLeft }}天后到期)
            </span>
          </div>
          <el-button text type="primary" @click="openFeedbackForm">反馈</el-button>
          <el-button text @click="openSettings">设置</el-button>
          <el-button type="primary" plain @click="logout">退出</el-button>
        </div>
      </el-header>

      <el-main class="layout-main">
        <RouterView />
      </el-main>
    </el-container>

    <el-drawer v-model="drawerOpen" size="240px" :with-header="false">
      <div class="drawer-brand">
        <div class="brand-title">知识管理平台</div>
        <div class="brand-sub app-muted">用户中心</div>
      </div>
      <div class="drawer-user">
        <el-tag v-if="userStore.isVip" type="success" size="small" effect="light">VIP</el-tag>
        <el-tag v-else type="info" size="small" effect="light">普通</el-tag>
        <span class="user-name">{{ userStore.username || '用户' }}</span>
      </div>
      <el-menu :default-active="activeMenu" class="aside-menu" router @select="go">
        <el-menu-item v-for="item in menuItems" :key="item.path" :index="item.path">
          <el-icon><component :is="item.icon" /></el-icon>
          <span>{{ item.label }}</span>
        </el-menu-item>
      </el-menu>
      <div class="drawer-actions">
        <el-button text type="primary" style="width: 100%" @click="openFeedbackForm">问题反馈</el-button>
        <el-button text style="width: 100%" @click="openSettings">个人设置</el-button>
        <el-button type="primary" plain style="width: 100%" @click="logout">退出登录</el-button>
      </div>
    </el-drawer>

    <el-dialog v-model="announcementOpen" width="min(560px, 92vw)" :title="announcement?.title || '系统公告'">
      <div class="announcement-body" v-loading="announcementLoading">
        <div class="announcement-content">{{ announcement?.content || '' }}</div>
        <div v-if="announcement?.image_url" class="announcement-image">
          <img :src="announcement.image_url" alt="公告图片" loading="lazy" />
        </div>
      </div>
      <template #footer>
        <el-button @click="closeAnnouncementOnce">当次关闭</el-button>
        <el-button type="primary" @click="dismissAnnouncementPermanently">永久关闭</el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="feedbackOpen" title="问题反馈" width="min(720px, 92vw)">
      <el-tabs v-model="feedbackTab" @tab-change="(name) => name === 'list' && loadMyFeedbacks()">
        <el-tab-pane label="提交反馈" name="new">
          <el-form label-position="top">
            <el-form-item label="标题">
              <el-input v-model="feedbackForm.title" placeholder="简要描述问题" maxlength="100" show-word-limit />
            </el-form-item>
            <el-form-item label="详细描述">
              <el-input v-model="feedbackForm.description" type="textarea" :rows="5" placeholder="请详细描述您遇到的问题" maxlength="2000" show-word-limit />
            </el-form-item>
            <el-form-item label="联系方式（可选）">
              <el-input v-model="feedbackForm.contact" placeholder="方便我们联系您" />
            </el-form-item>
          </el-form>
        </el-tab-pane>
        <el-tab-pane label="我的反馈" name="list">
          <el-skeleton v-if="feedbackLoading" :rows="6" animated />
          <template v-else>
            <el-empty v-if="myFeedbacks.length === 0" description="暂无反馈" />
            <el-collapse v-else accordion>
              <el-collapse-item v-for="item in myFeedbacks" :key="item.id" :name="String(item.id)">
                <template #title>
                  <div class="feedback-title">
                    <span class="feedback-title-text">{{ item.title }}</span>
                    <el-tag size="small" effect="light" :type="feedbackStatusTagType(item.status)">
                      {{ feedbackStatusLabel(item.status) }}
                    </el-tag>
                    <span class="feedback-time app-muted">{{ item.created_at || '' }}</span>
                  </div>
                </template>
                <div class="feedback-body">
                  <div class="feedback-section">
                    <div class="feedback-label app-muted">描述</div>
                    <div class="feedback-text">{{ item.description }}</div>
                  </div>
                  <div v-if="item.admin_reply" class="feedback-section">
                    <div class="feedback-label app-muted">管理员回复</div>
                    <div class="feedback-text">{{ item.admin_reply }}</div>
                  </div>
                </div>
              </el-collapse-item>
            </el-collapse>
          </template>
        </el-tab-pane>
      </el-tabs>

      <template #footer>
        <el-button @click="feedbackOpen = false">关闭</el-button>
        <el-button v-if="feedbackTab === 'list'" @click="loadMyFeedbacks">刷新</el-button>
        <el-button v-if="feedbackTab === 'new'" type="primary" :loading="feedbackSubmitting" @click="submitFeedbackForm">提交</el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="settingsOpen" title="个人设置" width="min(720px, 92vw)">
      <el-tabs v-model="settingsTab">
        <el-tab-pane label="邮箱绑定" name="email">
          <div v-loading="emailLoading" class="settings-section">
            <el-alert
              v-if="emailInfo.email && emailInfo.email_verified"
              type="success"
              :closable="false"
              title="邮箱已绑定并验证"
              show-icon
              class="settings-alert"
            >
              <template #default>
                <div class="email-row">
                  <div class="email-value">{{ emailInfo.email }}</div>
                  <el-button type="danger" text @click="onUnbindEmail">解绑</el-button>
                </div>
              </template>
            </el-alert>

            <el-alert
              v-else-if="emailInfo.email"
              type="warning"
              :closable="false"
              title="邮箱待验证：请查收验证邮件（含垃圾箱）"
              show-icon
              class="settings-alert"
            />

            <el-form label-position="top">
              <el-form-item label="邮箱地址">
                <el-input v-model="bindEmailValue" placeholder="name@example.com" />
              </el-form-item>
              <el-button type="primary" :loading="bindEmailLoading" @click="onBindEmail">发送验证邮件</el-button>
            </el-form>

            <el-divider />

            <div class="notify-row">
              <div>
                <div class="notify-title">任务完成通知</div>
                <div class="app-muted notify-desc">定时任务完成后发送邮件</div>
              </div>
              <el-switch
                :model-value="emailNotifyEnabled"
                :disabled="!emailInfo.email_verified || emailNotifyLoading"
                inline-prompt
                active-text="开"
                inactive-text="关"
                @change="onToggleEmailNotify"
              />
            </div>
            <el-alert
              v-if="!emailInfo.email_verified"
              type="info"
              :closable="false"
              title="绑定并验证邮箱后可开启邮件通知。"
              show-icon
              class="settings-hint"
            />
          </div>
        </el-tab-pane>

        <el-tab-pane label="修改密码" name="password">
          <div class="settings-section">
            <el-form label-position="top">
              <el-form-item label="当前密码">
                <el-input v-model="passwordForm.current_password" type="password" show-password autocomplete="current-password" />
              </el-form-item>
              <el-form-item label="新密码（至少8位且包含字母和数字）">
                <el-input v-model="passwordForm.new_password" type="password" show-password autocomplete="new-password" />
              </el-form-item>
              <el-form-item label="确认新密码">
                <el-input
                  v-model="passwordForm.confirm_password"
                  type="password"
                  show-password
                  autocomplete="new-password"
                  @keyup.enter="onChangePassword"
                />
              </el-form-item>
              <el-button type="primary" :loading="passwordLoading" @click="onChangePassword">确认修改</el-button>
            </el-form>
          </div>
        </el-tab-pane>

        <el-tab-pane label="Passkey设备" name="passkeys">
          <div class="settings-section" v-loading="passkeyLoading">
            <el-alert
              type="info"
              :closable="false"
              title="最多可绑定3台设备，用于无密码登录。"
              show-icon
              class="settings-alert"
            />

            <el-form inline>
              <el-form-item label="设备备注">
                <el-input
                  v-model="passkeyDeviceName"
                  placeholder="例如：我的iPhone / 办公Mac"
                  maxlength="40"
                  show-word-limit
                />
              </el-form-item>
              <el-form-item>
                <el-button type="primary" :loading="passkeyAddLoading" @click="onAddPasskey">
                  添加Passkey设备
                </el-button>
              </el-form-item>
            </el-form>

            <el-empty v-if="passkeyItems.length === 0" description="暂无Passkey设备" />
            <el-table v-else :data="passkeyItems" size="small" style="width: 100%">
              <el-table-column prop="device_name" label="设备备注" min-width="160" />
              <el-table-column prop="credential_id_preview" label="凭据ID" min-width="180" />
              <el-table-column prop="last_used_at" label="最近使用" min-width="140" />
              <el-table-column prop="created_at" label="创建时间" min-width="140" />
              <el-table-column label="操作" width="100" fixed="right">
                <template #default="{ row }">
                  <el-button type="danger" text @click="onDeletePasskey(row)">删除</el-button>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </el-tab-pane>

        <el-tab-pane label="快捷登录" name="social">
          <div class="settings-section" v-loading="socialBindingsLoading || socialBindLoading">
            <el-empty v-if="!socialConfig.enabled" description="暂未启用快捷登录" />
            <template v-else>
              <div class="social-binding-list">
                <div v-for="item in socialBindings" :key="item.provider" class="social-binding-row">
                  <div class="social-binding-main">
                    <div class="social-binding-title">
                      {{ item.provider_label }}
                      <el-tag v-if="item.bound" size="small" type="success">已绑定</el-tag>
                      <el-tag v-else size="small" type="info">未绑定</el-tag>
                    </div>
                    <div v-if="item.bound" class="app-muted social-binding-meta">
                      {{ item.nickname || '已授权账号' }}
                    </div>
                  </div>
                  <el-button v-if="item.bound" type="danger" text @click="onUnbindSocial(item)">解绑</el-button>
                </div>
              </div>

              <SocialLoginButtons
                :providers="socialConfig.providers"
                mode="bind"
                :redirect-uri="socialBindRedirectUri()"
                block
                @error="(message) => ElMessage.error(message)"
              />
            </template>
          </div>
        </el-tab-pane>

        <el-tab-pane label="表格上传" name="kdocs">
          <div v-loading="kdocsLoading" class="settings-section">
            <el-form label-position="top">
              <el-form-item label="县区（可选）">
                <el-input v-model="kdocsUnitValue" placeholder="留空使用系统默认县区" />
              </el-form-item>
              <el-button type="primary" :loading="kdocsSaving" @click="saveKdocsSettings">保存</el-button>
            </el-form>
            <el-alert
              type="info"
              :closable="false"
              title="自动上传开关在“账号管理”页面设置（测试功能）。"
              show-icon
              class="settings-hint"
            />
          </div>
        </el-tab-pane>

        <el-tab-pane label="VIP信息" name="vip">
          <div class="settings-section">
            <el-alert
              :type="userStore.isVip ? 'success' : 'info'"
              :closable="false"
              :title="userStore.isVip ? '当前为 VIP 会员' : '当前为普通用户'"
              show-icon
              class="settings-alert"
            />
            <div v-if="userStore.isVip" class="vip-info">
              <div class="vip-line">
                <span class="app-muted">到期时间</span>
                <span>{{ userStore.vipExpireTime || '未知' }}</span>
              </div>
              <div class="vip-line">
                <span class="app-muted">剩余天数</span>
                <span>{{ userStore.vipDaysLeft }}</span>
              </div>
            </div>
            <div v-else class="vip-info">
              <div class="app-muted">升级方式：请通过“反馈”联系管理员开通。</div>
            </div>
          </div>
        </el-tab-pane>
      </el-tabs>

      <template #footer>
        <el-button @click="settingsOpen = false">关闭</el-button>
      </template>
    </el-dialog>
  </el-container>
</template>

<style scoped>
.layout-root {
  height: 100%;
}

.layout-aside {
  background: #ffffff;
  border-right: 1px solid var(--app-border);
}

.brand,
.drawer-brand {
  padding: 18px 16px 10px;
}

.brand-title {
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.2px;
}

.brand-sub {
  margin-top: 2px;
  font-size: 12px;
}

.aside-menu {
  border-right: none;
}

.layout-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: rgba(246, 247, 251, 0.6);
  backdrop-filter: saturate(180%) blur(10px);
  border-bottom: 1px solid var(--app-border);
}

.header-left {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.header-title {
  font-size: 14px;
  font-weight: 800;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.header-menu-btn {
  padding-left: 0;
  padding-right: 0;
}

.header-right {
  display: flex;
  align-items: center;
  gap: 12px;
}

.user-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.user-name {
  font-size: 13px;
  font-weight: 700;
  max-width: 180px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.vip-warn {
  font-size: 12px;
  color: var(--app-muted);
  white-space: nowrap;
}

.layout-main {
  padding: 16px;
}

.drawer-user {
  padding: 0 16px 10px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.drawer-actions {
  padding: 12px 16px 4px;
  border-top: 1px solid var(--app-border);
}

.announcement-body {
  min-height: 80px;
}

.announcement-content {
  white-space: pre-wrap;
  line-height: 1.6;
  font-size: 14px;
}

.announcement-image {
  margin-top: 12px;
  display: flex;
  justify-content: center;
}

.announcement-image img {
  max-width: 100%;
  max-height: 320px;
  border-radius: 10px;
  border: 1px solid var(--app-border);
  object-fit: contain;
}

.feedback-title {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-width: 0;
}

.feedback-title-text {
  font-weight: 800;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.feedback-time {
  margin-left: auto;
  font-size: 12px;
  white-space: nowrap;
}

.feedback-body {
  padding: 6px 0 2px;
}

.feedback-section + .feedback-section {
  margin-top: 12px;
}

.feedback-label {
  font-size: 12px;
  margin-bottom: 6px;
}

.feedback-text {
  white-space: pre-wrap;
  line-height: 1.6;
  font-size: 13px;
}

.settings-section {
  padding: 6px 2px 2px;
}

.settings-alert {
  margin-bottom: 12px;
}

.email-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.email-value {
  font-weight: 800;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.notify-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.notify-title {
  font-weight: 800;
}

.notify-desc {
  margin-top: 4px;
  font-size: 12px;
}

.settings-hint {
  margin-top: 10px;
}

.social-binding-list {
  display: grid;
  gap: 10px;
  margin-bottom: 14px;
}

.social-binding-row {
  min-height: 56px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--app-border);
  border-radius: 10px;
  background: #fff;
}

.social-binding-main {
  min-width: 0;
}

.social-binding-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 800;
}

.social-binding-meta {
  margin-top: 4px;
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vip-info {
  margin-top: 12px;
  display: grid;
  gap: 10px;
}

.vip-line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

@media (max-width: 768px) {
  .layout-header {
    flex-wrap: wrap;
    height: auto;
    padding-top: 10px;
    padding-bottom: 10px;
  }

  .header-right {
    width: 100%;
    justify-content: flex-end;
  }

  .layout-main {
    padding: 12px;
  }

  .user-name {
    max-width: 120px;
  }
}
</style>
