<template>
  <div>
    <div class="page-header">
      <div>
        <h2>安全中心</h2>
        <p class="subtitle">管理登录安全、子账号风险控制和系统运维。</p>
      </div>
      <el-button @click="$router.push('/accounts')">子账号管理</el-button>
    </div>

    <el-row :gutter="16" style="margin-bottom: 16px">
      <el-col :xs="24" :md="8">
        <el-card shadow="hover" class="summary-card">
          <el-statistic title="当前账号" :value="auth.user?.nickname || auth.user?.username || '-'" />
        </el-card>
      </el-col>
      <el-col :xs="24" :md="8">
        <el-card shadow="hover" class="summary-card">
          <el-statistic title="应用数量" :value="apps.length" />
        </el-card>
      </el-col>
      <el-col :xs="24" :md="8">
        <el-card shadow="hover" class="summary-card">
          <el-statistic title="子账号数量" :value="subAccountCount" />
        </el-card>
      </el-col>
    </el-row>

    <el-card style="margin-bottom: 16px">
      <template #header>账号保护建议</template>
      <el-space wrap>
        <el-tag type="success">强密码</el-tag>
        <el-tag type="warning">邮箱验证</el-tag>
        <el-tag type="info">子账号最小权限</el-tag>
      </el-space>
      <el-divider />
      <el-button type="primary" plain @click="$router.push('/forgot-password')">重置当前账号密码</el-button>
    </el-card>

    <!-- 私有化迁移：仅公有化平台显示 -->
    <el-card v-if="deploymentMode === 'PUBLIC'" style="margin-bottom: 16px">
      <template #header>私有化部署迁移</template>
      <p style="color: #606266; margin: 0 0 16px;">
        将当前账号及应用数据迁移至私有化部署环境。迁移密钥仅在生成时显示一次，请及时保存。
      </p>
      <el-button type="warning" plain @click="openMigrateDialog">生成迁移密钥</el-button>
    </el-card>

    <!-- 系统运维：仅私有化平台显示 -->
    <el-card v-if="deploymentMode === 'PRIVATE'" style="margin-bottom: 16px">
      <template #header>
        <div style="display:flex;align-items:center;justify-content:space-between">
          <span>系统运维</span>
          <el-tag type="info" size="small">私有化部署</el-tag>
        </div>
      </template>
      <!-- 版本信息 -->
      <div style="display:flex;align-items:center;gap:12px;margin-bottom:16px">
        <el-button type="primary" plain size="small" @click="checkUpdate" :loading="checkingUpdate">
          检查更新
        </el-button>
        <el-tag v-if="updateCheckResult && !updateCheckResult.hasUpdate" type="success" size="small">所有服务已是最新</el-tag>
        <el-tag v-else-if="updateCheckResult?.hasUpdate" type="warning" size="small">
          有 {{ changedServiceCount }} 个服务可更新
        </el-tag>
        <span v-if="updateCheckResult?.releasedAt" style="color:#909399;font-size:12px">
          云端发布于 {{ formatTime(updateCheckResult.releasedAt) }}
        </span>
      </div>

      <!-- 版本对比表格（检查后始终显示） -->
      <el-table
        v-if="updateCheckResult"
        :data="serviceVersionRows"
        size="small"
        border
        style="margin-bottom:16px"
      >
        <el-table-column prop="name" label="服务" min-width="160" />
        <el-table-column label="当前版本" min-width="120">
          <template #default="{ row }">
            <el-tag type="info" size="small" class="mono">{{ row.current || '未知' }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="云端最新" min-width="120">
          <template #default="{ row }">
            <el-tag :type="row.changed ? 'warning' : 'success'" size="small" class="mono">
              {{ row.latest || '未知' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="状态" width="100">
          <template #default="{ row }">
            <el-tag :type="row.changed ? 'warning' : 'success'" size="small">
              {{ row.changed ? '有更新' : '最新' }}
            </el-tag>
          </template>
        </el-table-column>
      </el-table>

      <div style="display:flex;gap:8px;flex-wrap:wrap">
        <el-button type="primary" size="small" @click="openOperationDialog('update')">
          {{ updateCheckResult?.hasUpdate ? '一键更新到最新版本' : '一键更新' }}
        </el-button>
        <el-button v-if="updateCheckResult?.hasUpdate" size="small" @click="openSelectiveUpdateDialog">选择性更新</el-button>
        <el-button type="warning" size="small" @click="openOperationDialog('reset')">重置容器</el-button>
      </div>
    </el-card>

    <!-- Config 文件解析 -->
    <el-card style="margin-bottom: 16px">
      <template #header>Config 文件解析</template>
      <p style="color: #606266; margin: 0 0 16px;">
        上传已下载的 Config 文件（.xuqmconfig），解析并验证文件内容。
      </p>
      <el-input
        v-model="configFileContent"
        type="textarea"
        :rows="4"
        placeholder="将 Config 文件内容粘贴到此处，或点击上传文件"
        style="margin-bottom: 12px"
      />
      <div style="display: flex; gap: 12px; flex-wrap: wrap; align-items: center;">
        <el-upload
          ref="configUploadRef"
          action="#"
          :auto-upload="false"
          :on-change="handleConfigFileChange"
          :show-file-list="false"
          accept=".xuqmconfig"
        >
          <el-button type="primary" plain>选择 Config 文件</el-button>
        </el-upload>
        <el-button :loading="parsingConfig" @click="parseConfig">解析</el-button>
        <el-button v-if="configParseResult" @click="clearConfigParse">清除</el-button>
      </div>
      <el-descriptions v-if="configParseResult" :column="isMobile ? 1 : 2" border style="margin-top: 16px">
        <el-descriptions-item label="AppKey">{{ configParseResult.appKey }}</el-descriptions-item>
        <el-descriptions-item label="应用名称">{{ configParseResult.appName || '-' }}</el-descriptions-item>
        <el-descriptions-item label="Android 包名">{{ configParseResult.packageName || '-' }}</el-descriptions-item>
        <el-descriptions-item label="iOS BundleId">{{ configParseResult.iosBundleId || '-' }}</el-descriptions-item>
        <el-descriptions-item label="鸿蒙 BundleName">{{ configParseResult.harmonyBundleName || '-' }}</el-descriptions-item>
        <el-descriptions-item label="Config 版本">{{ configParseResult.revision }}</el-descriptions-item>
        <el-descriptions-item label="签发时间">{{ formatTime(configParseResult.issuedAt) }}</el-descriptions-item>
        <el-descriptions-item label="到期时间">{{ configParseResult.expiresAt ? formatTime(configParseResult.expiresAt) : '长期有效' }}</el-descriptions-item>
        <el-descriptions-item label="服务地址">{{ configParseResult.serverUrl || '-' }}</el-descriptions-item>
      </el-descriptions>
    </el-card>


    <!-- Migration dialogs -->
    <el-dialog v-model="showMigrateDialog" title="私有化部署迁移" width="460px" @closed="closeMigrateDialog">
      <div v-if="!migrateCodeSent">
        <p class="dialog-text">点击发送验证码，系统将向您的注册邮箱发送一次性验证码，验证通过后生成迁移密钥。</p>
        <el-button type="primary" :loading="migrateSendingCode" @click="sendMigrateCode">发送验证码</el-button>
      </div>
      <div v-else>
        <p class="dialog-text">请输入邮箱验证码：</p>
        <el-input v-model="migrateCode" maxlength="6" placeholder="6位验证码" />
      </div>
      <template #footer>
        <el-button @click="showMigrateDialog = false">取消</el-button>
        <el-button v-if="migrateCodeSent" type="primary" :loading="migrateSubmitting" @click="submitMigrateCode">
          生成密钥
        </el-button>
      </template>
    </el-dialog>

    <el-dialog
      v-model="showMigrateKey"
      title="迁移密钥"
      width="480px"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      @closed="migrateKeyValue = ''"
    >
      <el-alert type="warning" :closable="false" show-icon style="margin-bottom: 16px;">
        <template #title>密钥只显示一次，关闭后无法再次查看</template>
        <template #default>如果忘记，请重新生成新的迁移密钥。</template>
      </el-alert>
      <div class="migrate-key-box">{{ migrateKeyValue }}</div>
      <el-button type="primary" plain size="small" style="margin-top: 12px;" @click="copyMigrateKey">
        复制密钥
      </el-button>
      <template #footer>
        <el-button type="danger" @click="showMigrateKey = false">我已保存，关闭</el-button>
      </template>
    </el-dialog>

    <!-- 一键更新 / 重置容器 dialog -->
    <el-dialog
      v-model="showUpdateDialog"
      :title="operationType === 'update' ? '一键更新' : '重置容器'"
      width="600px"
      :close-on-click-modal="!updating && !selfRestarting"
      :close-on-press-escape="!updating && !selfRestarting"
      @closed="resetUpdateDialog"
    >
      <div v-if="!updating && !updateDone && !updateError" style="color:#606266;">
        <p v-if="operationType === 'update'">将拉取最新镜像并重建所有运行中的服务容器，用于升级到新版本。</p>
        <p v-else>将用当前本地镜像重建所有运行中的服务容器，无需下载新镜像，适合修复异常服务。</p>
        <el-descriptions v-if="operationType === 'update'" :column="1" border size="small" style="margin-top:12px;margin-bottom:12px">
          <el-descriptions-item label="当前版本">
            <el-skeleton v-if="versionLoading" :rows="1" animated style="width:160px" />
            <el-tag v-else-if="currentVersion" type="info" size="small">{{ currentVersion }}</el-tag>
            <span v-else style="color:#c0c4cc">—</span>
          </el-descriptions-item>
          <el-descriptions-item label="数据库迁移">自动执行，新版本启动时应用变更</el-descriptions-item>
        </el-descriptions>
        <el-alert type="warning" :closable="false" show-icon>
          <template #title>tenant-service 重启时页面连接会短暂中断（约 10–30 秒）</template>
          <template #default>操作完成后请刷新页面。</template>
        </el-alert>
      </div>

      <div v-if="updating || updateLog.length > 0" class="update-log-wrap">
        <pre ref="logEl" class="update-log">{{ updateLog.join('\n') }}</pre>
      </div>

      <div v-if="selfRestarting" class="reconnect-tip">
        <el-icon class="is-loading"><Loading /></el-icon>
        <span>正在等待服务重启，页面将在服务恢复后自动刷新...</span>
      </div>

      <div v-if="updateDone" style="margin-top:12px">
        <el-alert type="success" :closable="false" show-icon :title="operationType === 'update' ? '更新完成！' : '重置完成！'" />
      </div>
      <div v-if="updateError" style="margin-top:12px">
        <el-alert type="error" :closable="false" show-icon :title="updateError" />
      </div>

      <template #footer>
        <el-button v-if="!updating && !selfRestarting" @click="showUpdateDialog = false">
          {{ updateDone || updateError ? '关闭' : '取消' }}
        </el-button>
        <el-button
          v-if="!updating && !updateDone && !selfRestarting"
          :type="operationType === 'update' ? 'primary' : 'warning'"
          @click="startOperation"
        >
          {{ operationType === 'update' ? '开始更新' : '开始重置' }}
        </el-button>
      </template>
    </el-dialog>

    <!-- 选择性更新 dialog -->
    <el-dialog v-model="showSelectiveUpdate" title="选择性更新" width="500px">
      <p style="color:#606266;margin-bottom:16px">选择要更新的服务（只拉取并重建选中的服务）：</p>
      <el-checkbox-group v-model="selectedServices">
        <div v-for="(info, svc) in (updateCheckResult?.services ?? {})" :key="svc" style="padding:6px 0">
          <el-checkbox :value="svc" :disabled="!info.changed">
            <span>{{ svc }}</span>
            <el-tag v-if="info.changed" type="warning" size="small" style="margin-left:8px">
              {{ info.current }} → {{ info.latest }}
            </el-tag>
            <el-tag v-else type="info" size="small" style="margin-left:8px">无变更</el-tag>
          </el-checkbox>
        </div>
      </el-checkbox-group>
      <template #footer>
        <el-button @click="showSelectiveUpdate = false">取消</el-button>
        <el-button type="primary" :disabled="!selectedServices.length" @click="startSelectiveUpdate">
          更新选中服务 ({{ selectedServices.length }})
        </el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Loading } from '@element-plus/icons-vue'
import { accountApi } from '@/api/account'
import { appApi, type App } from '@/api/app'
import { migrateApi } from '@/api/migrate'
import { getDeploymentStatus, getSystemVersion, streamSystemUpdate, streamSelectiveUpdate, streamSystemReset, checkForUpdates } from '@/api/system'
import { useAuthStore } from '@/stores/auth'
import { formatTime } from '@/utils/date'

const auth = useAuthStore()
const apps = ref<App[]>([])
const loading = ref(false)
const subAccountCount = ref(0)
const deploymentMode = ref<'PUBLIC' | 'PRIVATE' | null>(null)

// Config file parse
const configFileContent = ref('')
const parsingConfig = ref(false)
const configParseResult = ref<{
  schemaVersion: number
  configId: string
  revision: number
  issuedAt: string
  expiresAt: string | null
  appKey: string
  appName: string
  packageName: string
  iosBundleId: string
  harmonyBundleName: string
  serverUrl: string
} | null>(null)
const configUploadRef = ref<any>(null)
const isMobile = ref(false)
function updateViewport() {
  isMobile.value = window.innerWidth < 768
}

function handleConfigFileChange(file: any) {
  const reader = new FileReader()
  reader.onload = (e) => {
    configFileContent.value = (e.target?.result as string) || ''
  }
  reader.readAsText(file.raw)
}

async function parseConfig() {
  if (!configFileContent.value.trim()) {
    ElMessage.warning('请输入或上传 Config 文件内容')
    return
  }
  parsingConfig.value = true
  try {
    const res = await appApi.parseConfigFile(configFileContent.value.trim())
    configParseResult.value = res.data.data
    ElMessage.success('解析成功')
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.message || '解析失败，请检查文件内容')
  } finally {
    parsingConfig.value = false
  }
}

function clearConfigParse() {
  configFileContent.value = ''
  configParseResult.value = null
  if (configUploadRef.value) {
    configUploadRef.value.clearFiles()
  }
}

async function loadData() {
  loading.value = true
  try {
    const [appsRes, subRes] = await Promise.all([appApi.list(), accountApi.list()])
    apps.value = appsRes.data.data
    subAccountCount.value = subRes.data.data.length
  } finally {
    loading.value = false
  }
}

// ── Migration ─────────────────────────────────────────────────────────────

const showMigrateDialog = ref(false)
const showMigrateKey = ref(false)
const migrateCodeSent = ref(false)
const migrateSendingCode = ref(false)
const migrateSubmitting = ref(false)
const migrateCode = ref('')
const migrateKeyValue = ref('')

function openMigrateDialog() {
  migrateCodeSent.value = false
  migrateCode.value = ''
  showMigrateDialog.value = true
}

function closeMigrateDialog() {
  migrateCode.value = ''
  migrateCodeSent.value = false
}

async function sendMigrateCode() {
  migrateSendingCode.value = true
  try {
    await migrateApi.requestCode()
    migrateCodeSent.value = true
    ElMessage.success('验证码已发送到邮箱')
  } finally {
    migrateSendingCode.value = false
  }
}

async function submitMigrateCode() {
  if (!migrateCode.value.trim()) { ElMessage.warning('请输入验证码'); return }
  migrateSubmitting.value = true
  try {
    const res = await migrateApi.generateKey(migrateCode.value.trim())
    migrateKeyValue.value = res.data.data.migrationKey
    showMigrateDialog.value = false
    showMigrateKey.value = true
  } finally {
    migrateSubmitting.value = false
  }
}

async function copyMigrateKey() {
  try {
    await navigator.clipboard.writeText(migrateKeyValue.value)
    ElMessage.success('已复制到剪贴板')
  } catch {
    ElMessage.warning('复制失败，请手动选择并复制')
  }
}

// ── System Update ──────────────────────────────────────────────────────────

const showUpdateDialog = ref(false)
const operationType = ref<'update' | 'reset'>('update')
const updating = ref(false)
const updateDone = ref(false)
const updateError = ref('')
const selfRestarting = ref(false)
const updateLog = ref<string[]>([])
const logEl = ref<HTMLPreElement | null>(null)
const currentVersion = ref('')
const versionLoading = ref(false)
const checkingUpdate = ref(false)
const updateCheckResult = ref<import('@/api/system').UpdateCheckResult | null>(null)

const SERVICE_DISPLAY_NAMES: Record<string, string> = {
  'tenant-service': '租户服务',
  'im-service': '即时通讯服务',
  'push-service': '推送服务',
  'update-service': '版本管理服务',
  'file-service': '文件服务',
  'xuqm-bugcollect-service': '崩溃收集服务',
  'demo-service': 'Demo 服务',
  'tenant-web': '租户平台 (Web)',
  'ops-web': '运营平台 (Web)',
  'nginx': 'Nginx 网关',
}

const serviceVersionRows = computed(() => {
  if (!updateCheckResult.value) return []
  return Object.entries(updateCheckResult.value.services).map(([name, info]) => ({
    name: SERVICE_DISPLAY_NAMES[name] ?? name,
    key: name,
    current: info.current,
    latest: info.latest,
    changed: info.changed,
  }))
})

const changedServiceCount = computed(() =>
  serviceVersionRows.value.filter(r => r.changed).length
)

async function checkUpdate() {
  checkingUpdate.value = true
  try {
    const result = await checkForUpdates()
    updateCheckResult.value = result
    currentVersion.value = result.currentVersion
    if (result.hasUpdate) {
      ElMessage.info(`发现新版本 ${result.latestVersion}`)
    } else {
      ElMessage.success('已是最新版本')
    }
  } catch {
    ElMessage.error('检查更新失败')
  } finally {
    checkingUpdate.value = false
  }
}

async function openOperationDialog(type: 'update' | 'reset') {
  operationType.value = type
  showUpdateDialog.value = true
  if (type === 'update' && !currentVersion.value) {
    versionLoading.value = true
    try {
      const info = await getSystemVersion()
      currentVersion.value = info.currentVersion
    } catch {
      currentVersion.value = ''
    } finally {
      versionLoading.value = false
    }
  }
}

// ── Selective Update ──
const showSelectiveUpdate = ref(false)
const selectedServices = ref<string[]>([])

function openSelectiveUpdateDialog() {
  // 默认选中所有有变更的服务
  const changed = updateCheckResult.value?.services
    ? Object.entries(updateCheckResult.value.services)
        .filter(([_, info]) => info.changed)
        .map(([svc]) => svc)
    : []
  selectedServices.value = changed
  showSelectiveUpdate.value = true
}

async function startSelectiveUpdate() {
  if (!selectedServices.value.length) return
  showSelectiveUpdate.value = false
  // 复用更新对话框显示日志
  operationType.value = 'update'
  showUpdateDialog.value = true
  updating.value = true
  updateLog.value = []
  updateDone.value = false
  updateError.value = ''
  selfRestarting.value = false

  let seenRestartSelf = false
  try {
    await streamSelectiveUpdate(selectedServices.value, (line) => {
      if (line === 'RESTART_SELF') {
        seenRestartSelf = true
        selfRestarting.value = true
        updating.value = false
        pollForRecovery()
        return
      }
      updateLog.value.push(line)
      nextTick(() => {
        if (logEl.value) logEl.value.scrollTop = logEl.value.scrollHeight
      })
    })
    if (!selfRestarting.value) {
      updating.value = false
      updateDone.value = true
    }
  } catch (e: any) {
    if (seenRestartSelf || selfRestarting.value) {
      if (!selfRestarting.value) {
        selfRestarting.value = true
        updating.value = false
        pollForRecovery()
      }
      return
    }
    updating.value = false
    updateError.value = e?.message ?? '选择性更新失败'
  }
}

function resetUpdateDialog() {
  if (updating.value) return
  updateLog.value = []
  updateDone.value = false
  updateError.value = ''
  selfRestarting.value = false
  currentVersion.value = ''
}

async function startOperation() {
  updating.value = true
  updateLog.value = []
  updateDone.value = false
  updateError.value = ''
  selfRestarting.value = false

  const streamFn = operationType.value === 'update' ? streamSystemUpdate : streamSystemReset
  const failMsg = operationType.value === 'update' ? '更新失败' : '重置失败'

  let seenRestartSelf = false
  try {
    await streamFn((line) => {
      if (line === 'RESTART_SELF') {
        seenRestartSelf = true
        selfRestarting.value = true
        updating.value = false
        pollForRecovery()
        return
      }
      updateLog.value.push(line)
      nextTick(() => {
        if (logEl.value) logEl.value.scrollTop = logEl.value.scrollHeight
      })
    })
    if (!selfRestarting.value) {
      updating.value = false
      updateDone.value = true
    }
  } catch (e: any) {
    // Connection drop during self-restart is expected
    if (seenRestartSelf || selfRestarting.value) {
      if (!selfRestarting.value) {
        selfRestarting.value = true
        updating.value = false
        pollForRecovery()
      }
      return
    }
    updating.value = false
    updateError.value = e?.message ?? failMsg
  }
}

async function pollForRecovery() {
  const deadline = Date.now() + 120_000
  let attempt = 0
  while (Date.now() < deadline) {
    attempt++
    await new Promise(r => setTimeout(r, 3000))
    try {
      await getDeploymentStatus()
      selfRestarting.value = false
      updateDone.value = true
      updateLog.value.push(operationType.value === 'update'
        ? '>>> tenant-service 已重启，更新完成 ✓'
        : '>>> tenant-service 已重启，重置完成 ✓')
      // Auto-reload after 2s to refresh all page state
      setTimeout(() => {
        window.location.reload()
      }, 2000)
      return
    } catch {
      if (attempt % 5 === 0) {
        updateLog.value.push(`>>> 等待服务恢复中... (第 ${attempt} 次尝试)`)
        nextTick(() => {
          if (logEl.value) logEl.value.scrollTop = logEl.value.scrollHeight
        })
      }
    }
  }
  selfRestarting.value = false
  updateError.value = '等待服务重启超时（2 分钟），请手动刷新页面确认更新结果'
  updateLog.value.push('>>> 等待超时，请手动刷新页面检查服务状态')
}

const fmt = formatTime

onMounted(async () => {
  updateViewport()
  window.addEventListener('resize', updateViewport)
  loadData()
  try {
    const status = await getDeploymentStatus()
    deploymentMode.value = status.mode
    if (status.mode === 'PRIVATE') {
      const info = await getSystemVersion()
      currentVersion.value = info.currentVersion
    }
  } catch {
    deploymentMode.value = 'PUBLIC'
  }
})

onUnmounted(() => {
  window.removeEventListener('resize', updateViewport)
})
</script>

<style scoped>
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 16px;
}
.subtitle {
  margin: 6px 0 0;
  color: #606266;
}
.summary-card {
  min-height: 110px;
}
.migrate-key-box {
  font-family: monospace;
  font-size: 14px;
  word-break: break-all;
  background: #f5f7fa;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  padding: 12px 16px;
  line-height: 1.6;
  user-select: all;
}
.update-log-wrap {
  margin-top: 12px;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  background: #1d2129;
}
.update-log {
  margin: 0;
  padding: 12px 16px;
  font-family: monospace;
  font-size: 12px;
  color: #c9d1d9;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 320px;
  overflow-y: auto;
  line-height: 1.6;
}
.reconnect-tip {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  color: #909399;
  font-size: 14px;
}
</style>
