<template>
  <el-alert
    :title="alertMsg"
    type="warning"
    show-icon
    :closable="false"
    style="margin-bottom: 30px"
  />
  <div v-loading="loading" class="card-container" style="display: flex; width: 100%">
    <el-card>
      <template #header>
        <div class="card-header">
          <span>备份</span>
          <el-tag :type="handleBackupStatus().tag">{{ handleBackupStatus().msg }}</el-tag>
        </div>
      </template>
      <div>
        <el-button type="primary" size="small" :disabled="backup.status" @click="handleBackup"
          >系统备份</el-button
        >
        <div v-show="backup.status" style="margin-top: 20px">
          <el-link type="primary" :underline="false" style="margin-bottom: 10px">备份进度</el-link>
          <el-progress :percentage="backup.progress" striped striped-flow :duration="10" />
        </div>
      </div>
    </el-card>
    <el-card>
      <template #header>
        <div class="card-header">
          <span>下载</span>
          <el-tag :type="!downloadBackup.status ? 'info' : 'primary'">{{
            !downloadBackup.status ? '未下载' : '下载中'
          }}</el-tag>
        </div>
      </template>
      <div>
        <el-button
          type="primary"
          size="small"
          :disabled="downloadBackup.status"
          @click="handleDownBackup"
          >下载备份</el-button
        >
        <div v-if="downloadBackup.status" style="margin-top: 20px">
          <el-link type="primary" :underline="false" style="margin-bottom: 10px">下载进度</el-link>
          <el-progress :percentage="downloadBackup.progress" striped striped-flow :duration="10" />
        </div>
      </div>
    </el-card>
    <el-card>
      <template #header>
        <div class="card-header">
          <span>还原</span>
          <el-tag :type="handleReductionBackupStatus()?.tag">{{
            handleReductionBackupStatus()?.msg
          }}</el-tag>
        </div>
      </template>
      <div>
        <el-button
          type="primary"
          size="small"
          :disabled="reductionBackup.status"
          @click="handleSelectFile"
          >还原系统</el-button
        >
        <div v-if="reductionBackup.status" style="margin-top: 20px">
          <el-link type="primary" :underline="false" style="margin-bottom: 10px">上传进度</el-link>
          <el-progress
            :percentage="reductionBackup.progress"
            :status="reductionBackup.progress == 100 ? 'success' : ''"
            striped
            striped-flow
            :duration="10"
          />
        </div>
        <div v-if="reductionBackup.reductionStatus" style="margin-top: 20px">
          <el-link type="primary" :underline="false" style="margin-bottom: 10px">备份进度</el-link>
          <el-progress
            :percentage="reductionBackup.reductionProgress"
            :status="reductionBackup.reductionProgress == 100 ? 'success' : ''"
            striped
            striped-flow
            :duration="10"
          />
        </div>
      </div>
    </el-card>
  </div>
  <input ref="uploadRef" type="file" style="display: none" @change="handleFileChange" />
</template>

<script setup lang="ts">
import { useBackupStore } from '@/stores/modules/backup'
import { useTenantStore } from '@/stores/modules/tenant'
import { dayjs } from 'element-plus'
import { saveAs } from 'file-saver'
import axios from 'axios'

const backupStore = useBackupStore()
const tenantStore = useTenantStore()
const tenantId = computed(() => tenantStore.getTenantId)
const backup = computed(() => backupStore.getBackup)
const downloadBackup = computed(() => backupStore.getDownloadBackup)
const reductionBackup = computed(() => backupStore.getReductionBackup)
const loading = ref(true)
const uploadFile = ref()
const uploadRef = ref()
const polling = ref()
const reductionPolling = ref()

const alertMsg =
  '请不要同时操作，等待某个功能操作进度条结束后再进行其他操作； 系统备份进行下载与还原功能时，禁止刷新页面，否则会导致操作失败，请耐心等待！'

const handleBackupStatus = () => {
  const progress = backup.value.progress
  const status: any = {
    100: { tag: 'success', msg: '已备份' },
    0: { tag: 'info', msg: '未备份' }
  }
  return status[progress] || { tag: 'primary', msg: '备份中' }
}

const handleReductionBackupStatus = () => {
  const { status, reductionStatus } = reductionBackup.value

  const statusMap: any = {
    'false-false': { tag: 'info', msg: '未同步' },
    'true-false': { tag: 'primary', msg: '上传中' },
    'true-true': { tag: 'primary', msg: '还原中' }
  }

  return statusMap[`${status}-${reductionStatus}`] || null
}

const handleBackup = () => {
  if (downloadBackup.value.status)
    return ElMessage.warning('下载备份中，请等待下载完成后备份系统！')
  ElMessageBox.confirm(
    `${backup.value.progress == 100 ? '已存在备份系统，是否要重新备份' : '是否要备份'}系统？`,
    '提示',
    {
      confirmButtonText: '确认',
      cancelButtonText: '取消',
      type: 'warning'
    }
  )
    .then(() => {
      backupStore.backup.status = true
      backupStore.backup.progress = undefined
      axios
        .get(`/admin/api/docker/backSystem?tenantId=${tenantId.value}`, { timeout: 5000 })
        .catch(() => {
          backupStore.setDownloadProgress(tenantId.value)
          startPolling(3000)
        })
    })
    .catch(() => {
      ElMessage({
        type: 'info',
        message: '取消备份'
      })
    })
}

const handleDownBackup = () => {
  if (backup.value.status) return ElMessage.warning('备份中，请等待备份完成后下载备份！')
  if (backup.value.progress === 0) return ElMessage.warning('未备份系统，请先备份系统！')
  ElMessageBox.confirm('确认下载系统备份', '提示', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning'
  })
    .then(async () => {
      downloadFile('/admin/api/docker/backDownload', 1024 * 1024 * 10, tenantId.value)
    })
    .catch(() => {
      ElMessage({
        type: 'info',
        message: '取消下载系统备份'
      })
    })
}

const startPolling = (interval: number) => {
  const checkProgress = () => {
    if (backup.value.progress === 100 || backup.value.progress === 0) {
      stopPolling()
      return true // 表示已完成
    }
    return false // 表示尚未完成
  }

  const poll = () => {
    backupStore.setDownloadProgress(tenantId.value)
    // console.log('🚀 ~ startPolling ~ backup.value.progress:', backup.value.progress)
    if (!checkProgress()) {
      polling.value = setTimeout(poll, interval)
    }
  }
  poll() // 开始轮询
}

const stopPolling = () => {
  backupStore.backup.progress = undefined
  backupStore.backup.status = false
  clearTimeout(polling.value)
}

const startReductionPolling = (interval: number) => {
  const checkProgress = () => {
    if (
      reductionBackup.value.reductionProgress >= 100 ||
      reductionBackup.value.reductionProgress == undefined
    ) {
      stopReductionPolling()
      return true // 表示已完成
    }

    return false // 表示尚未完成
  }

  const poll = () => {
    backupStore.setReductionProgress(tenantId.value)
    if (!checkProgress()) {
      polling.value = setTimeout(poll, interval)
    }
  }
  poll() // 开始轮询
}

const stopReductionPolling = () => {
  console.log('停止还原系统')
  // 已经上传完成了,更新上传进度
  backupStore.setReductionBackupProgress(0, 1)
  backupStore.setReductionBackupStatus(false, 1)
  // 开始还原进度
  backupStore.setReductionBackupProgress(0, 0)
  backupStore.setReductionBackupStatus(false, 0)
  clearTimeout(reductionPolling.value)
}

const downloadFile = async (url: string, chunkSize: any, tenantId: string) => {
  backupStore.setDownloadBackupStatus(true)
  backupStore.setDownloadBackupProgress(0)
  const fullUrl = `${url}?tenantId=${tenantId}`
  // 获取文件的总大小
  const headResponse = await axios.head(fullUrl)
  const totalSize = parseInt(headResponse.headers['content-length'], 10)
  const numChunks = Math.ceil(totalSize / chunkSize)
  const chunks: any[] = []
  for (let i = 0; i < numChunks; i++) {
    const start = i * chunkSize
    const end = Math.min(start + chunkSize - 1, totalSize - 1)
    try {
      const chunkResponse = await axios.get(fullUrl, {
        headers: {
          Range: `bytes=${start}-${end}`
        },
        responseType: 'blob'
      })
      if (chunkResponse.status !== 206) {
        throw new Error(`Error fetching chunk ${i + 1}: ${chunkResponse.statusText}`)
      }
      // 提取 Blob 数据
      chunks.push(chunkResponse.data)
      //   console.log(`Downloaded chunk ${i + 1}/${numChunks}`)
      downloadBackup.value.progress = parseFloat((((i + 1) / numChunks) * 100).toFixed(2))
    } catch (error) {
      console.error(error)
      return
    }
  }
  // 创建一个新的 Blob 对象，用于合并所有的分片
  const finalBlob = new Blob(chunks, { type: 'application/zip;charset=utf-8' })
  saveAs(finalBlob, `${tenantStore.getTenantName}-${dayjs().format('YYYY-MM-DD')}.zip`)
  backupStore.setDownloadBackupStatus(false)
  ElMessage.success('文件下载成功')
}

const handleSelectFile = async () => {
  const { data } = await axios.get(
    `/admin/api/docker/revertSystemCheck?tenantId=${tenantStore.getTenantId}`
  )
  if (data.code === 200)
    ElMessageBox.confirm('确认还原此系统？', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }).then(() => {
      axios
        .get(`/admin/api/docker/revertSystem?tenantId=${tenantId.value}`, { timeout: 5000 })
        .catch(() => {
          backupStore.setReductionProgress(tenantId.value)
          setTimeout(() => {
            handleReductionPolling()
          }, 3000)
        })
    })
  else if (data.code === 201) uploadRef.value.click()
  else if (data.code === 202)
    ElMessage.warning('当前环境已经部署，如果要还原，请先清空当前环境docker的容器与镜像！')
}

const handleFileChange = (event: any) => {
  const files = event.target.files
  if (files.length > 1) {
    ElMessage.warning('只能上传一个文件')
    uploadFile.value = ''
    return
  }
  if (files.length === 1) {
    if (files[0].type !== 'application/zip') {
      ElMessage.warning('只允许上传 .zip 文件')
      uploadFile.value = ''
    } else {
      uploadFile.value = files[0]
      uploadFileInChunks(files[0])
    }
  }
}

const uploadFileInChunks = async (file: any) => {
  if (!uploadFile.value) return ElMessage.warning('请先选择文件')
  backupStore.setReductionBackupStatus(true)
  const chunkSize = 1024 * 1024 * 2
  const totalChunks = Math.ceil(file.size / chunkSize)
  for (let index = 0; index < totalChunks; index++) {
    const start = index * chunkSize
    const end = Math.min(start + chunkSize, file.size)
    const chunk = file.slice(start, end)
    const success = await uploadChunk(chunk, file.name, file.size, index + 1, totalChunks)
    if (!success) return
    if (success === 200) {
      // 上传分片完成后，执行调用系统还原接口
      return
    }
    backupStore.setReductionBackupProgress(Math.round(((index + 1) / totalChunks) * 100))
  }
}

const uploadChunk = async (
  chunk: File,
  fileName: string,
  totalSize: string,
  index: any,
  totalChunks: any
) => {
  const formData = new FormData()
  formData.append('data', chunk)
  formData.append('name', fileName)
  formData.append('totalSize', totalSize)
  formData.append('total', totalChunks)
  formData.append('index', index)
  formData.append('tenantId', tenantStore.getTenantId)

  try {
    const response = await axios.post('/admin/api/docker/doPost', formData, {
      headers: { 'Content-Type': 'multipart/form-data' }
    })

    if (response.data.status === 201) {
      return true
    } else if (response.data.status === 502) {
      // 上传失败，正在重试块上传...
      await delay(200)
      return uploadChunk(chunk, fileName, totalSize, index, totalChunks) // Retry
    } else if (response.data.status === 200) {
      await mergeChunks()
      return 200
    } else return false
  } catch (error) {
    console.error('Error uploading chunk:', error)
    ElMessage.error('文件上传失败')
    backupStore.setReductionBackupStatus(false)
    backupStore.setReductionBackupProgress(0)
    return false
  }
}

const mergeChunks = async () => {
  try {
    const response = await axios.get('/admin/api/docker/merge', {
      params: { tenantId: tenantStore.getTenantId }
    })
    if (response.data.code == 200) {
      axios
        .get(`/admin/api/docker/revertSystem?tenantId=${tenantId.value}`, { timeout: 5000 })
        .catch((err) => {
          backupStore.setReductionProgress(tenantId.value)
          setTimeout(() => {
            handleReductionPolling()
          }, 3000)
        })
      ElMessage.success('文件上传成功')
    }
    console.log('Merge response:', response.data)
  } catch (error) {
    console.error('Error merging chunks:', error)
  }
}

const handleReductionPolling = () => {
  if (
    reductionBackup.value.reductionProgress == undefined ||
    reductionBackup.value.reductionProgress >= 100
  )
    return
  // 已经上传完成了,更新上传进度
  backupStore.setReductionBackupProgress(100, 1)
  backupStore.setReductionBackupStatus(true, 1)
  // 开始还原进度
  backupStore.setReductionBackupProgress(0, 0)
  backupStore.setReductionBackupStatus(true, 0)
  startReductionPolling(5000)
}

const delay = (ms: number) => {
  return new Promise((resolve) => setTimeout(resolve, ms)) // Delay function for retries
}

onBeforeRouteLeave(() => {
  console.log('切换离开页面了')
  if (backup.value.status) stopPolling()
  if (reductionBackup.value.reductionStatus) stopReductionPolling()
})

// onBeforeUnmount(() => {
//   console.log('卸载了当前页面')
//   if (backup.value.status) stopPolling()
//   if (reductionBackup.value.reductionStatus) stopReductionPolling()
// })

const init = async () => {
  backupStore.setDownloadProgress(tenantId.value)
  if (backup.value.progress != 100 && backup.value.progress != 0) startPolling(3000)
  backupStore.setReductionProgress(tenantId.value)
  if (reductionBackup.value.reductionProgress <= 100) handleReductionPolling()
  loading.value = false
}

init()
</script>

<style scoped>
.card-container {
  display: flex;
  justify-content: space-between;
}

.el-card {
  flex: 1;
}
.el-card:not(:last-child) {
  margin-right: 20px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>
