<script setup>
import { Delete, Edit, Folder, FolderOpened, Upload } from '@element-plus/icons-vue'
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
  createCategory,
  deleteCategory,
  deleteDataFile,
  downloadDataTemplate,
  getCategoryTree,
  getDataFiles,
  getFileRecords,
  updateCategory,
  uploadDataFile,
} from '@/api/dataManagement'
import DataCurve from './components/DataCurve.vue'

const categoryTree = ref([])
const selectedCategory = ref('all')

const searchForm = reactive({
  filename: '',
})

const fileList = ref([])
const loadingFiles = ref(false)

const currentFile = ref(null)
const recordList = ref([])
const contentLoading = ref(false)
const contentMode = ref('table')

const categoryDialogVisible = ref(false)
const categoryDialogMode = ref('create')
const editingCategoryId = ref('')
const categoryForm = reactive({
  name: '',
})

const uploadDialogVisible = ref(false)
const uploadForm = reactive({
  categoryId: '',
})

const layoutRef = ref(null)
const leftPaneWidth = ref(16)
const middlePaneWidth = ref(38)
const dragState = ref(null)
const pendingDragX = ref(null)
let dragFrameId = 0
const layoutStorageKey = 'data-management-layout-v1'

const MIN_LEFT = 12
const MAX_LEFT = 24
const MIN_MIDDLE = 30
const MAX_MIDDLE = 56
const MIN_RIGHT = 24

const rightPaneWidth = computed(() => {
  const value = 100 - leftPaneWidth.value - middlePaneWidth.value
  return Number(value.toFixed(2))
})

const categoryDialogTitle = computed(() => {
  return categoryDialogMode.value === 'create' ? '新增分类' : '编辑分类'
})

const flatCategoryOptions = computed(() => {
  const root = categoryTree.value.find((item) => String(item.id) === 'all')
  return (root?.children || []).map((item) => ({
    label: item.name,
    value: String(item.id),
  }))
})

const normalizeCategoryTree = (treeData) => {
  const source = Array.isArray(treeData) ? treeData : []
  const oneLevel = source.filter((item) => String(item?.id) !== 'all')

  const children = oneLevel.map((item) => ({
    id: item.id,
    name: item.name,
    parent_id: item.parent_id,
    children: [],
  }))

  return [
    {
      id: 'all',
      name: '全部',
      parent_id: null,
      children,
    },
  ]
}

const setPaneWidths = (nextLeft, nextMiddle, target = 'left') => {
  let left = Math.min(Math.max(nextLeft, MIN_LEFT), MAX_LEFT)
  let middle = Math.min(Math.max(nextMiddle, MIN_MIDDLE), MAX_MIDDLE)

  if (100 - left - middle < MIN_RIGHT) {
    if (target === 'left') {
      left = 100 - middle - MIN_RIGHT
      if (left < MIN_LEFT) {
        left = MIN_LEFT
        middle = 100 - left - MIN_RIGHT
      }
    } else {
      middle = 100 - left - MIN_RIGHT
      if (middle < MIN_MIDDLE) {
        middle = MIN_MIDDLE
        left = 100 - middle - MIN_RIGHT
      }
    }
  }

  leftPaneWidth.value = Number(left.toFixed(2))
  middlePaneWidth.value = Number(middle.toFixed(2))
}

const loadLayoutCache = () => {
  const raw = localStorage.getItem(layoutStorageKey)
  if (!raw) {
    return
  }

  try {
    const parsed = JSON.parse(raw)
    if (typeof parsed.left === 'number' && typeof parsed.middle === 'number') {
      setPaneWidths(parsed.left, parsed.middle, 'middle')
    }
  } catch {
    localStorage.removeItem(layoutStorageKey)
  }
}

const saveLayoutCache = () => {
  localStorage.setItem(
    layoutStorageKey,
    JSON.stringify({
      left: leftPaneWidth.value,
      middle: middlePaneWidth.value,
    }),
  )
}

const applyDragPosition = (clientX) => {
  if (!dragState.value) {
    return
  }

  const delta = ((clientX - dragState.value.startX) / dragState.value.layoutWidth) * 100

  if (dragState.value.target === 'left') {
    setPaneWidths(dragState.value.startLeft + delta, dragState.value.startMiddle, 'left')
  } else {
    setPaneWidths(dragState.value.startLeft, dragState.value.startMiddle + delta, 'middle')
  }
}

const flushDragging = () => {
  dragFrameId = 0
  if (pendingDragX.value === null) {
    return
  }
  applyDragPosition(pendingDragX.value)
}

const stopDragging = () => {
  window.removeEventListener('mousemove', handleDragging)
  window.removeEventListener('mouseup', stopDragging)
  window.removeEventListener('mouseleave', stopDragging)
  window.removeEventListener('blur', stopDragging)

  document.body.style.userSelect = ''
  document.body.style.cursor = ''

  if (dragFrameId) {
    cancelAnimationFrame(dragFrameId)
    dragFrameId = 0
  }

  pendingDragX.value = null

  if (dragState.value) {
    saveLayoutCache()
  }
  dragState.value = null
}

const handleDragging = (event) => {
  if (!dragState.value) {
    return
  }

  pendingDragX.value = event.clientX

  if (!dragFrameId) {
    dragFrameId = requestAnimationFrame(flushDragging)
  }
}

const startDragging = (target, event) => {
  if (window.innerWidth <= 980 || !layoutRef.value) {
    return
  }

  event.preventDefault()
  stopDragging()

  dragState.value = {
    target,
    startX: event.clientX,
    startLeft: leftPaneWidth.value,
    startMiddle: middlePaneWidth.value,
    layoutWidth: layoutRef.value.clientWidth,
  }

  document.body.style.userSelect = 'none'
  document.body.style.cursor = 'col-resize'

  window.addEventListener('mousemove', handleDragging)
  window.addEventListener('mouseup', stopDragging)
  window.addEventListener('mouseleave', stopDragging)
  window.addEventListener('blur', stopDragging)
}

const loadCategoryTree = async () => {
  const treeData = await getCategoryTree()
  categoryTree.value = normalizeCategoryTree(treeData)

  const allIds = [
    ...categoryTree.value.map((item) => String(item.id)),
    ...categoryTree.value.flatMap((item) => (item.children || []).map((child) => String(child.id))),
  ]

  if (!allIds.includes(String(selectedCategory.value))) {
    selectedCategory.value = 'all'
  }
}

const loadFileList = async () => {
  loadingFiles.value = true
  try {
    const data = await getDataFiles({
      category_id: selectedCategory.value && selectedCategory.value !== 'all' ? selectedCategory.value : '',
      filename: searchForm.filename.trim(),
    })
    fileList.value = data
  } finally {
    loadingFiles.value = false
  }
}

const handleCategoryClick = async (node) => {
  selectedCategory.value = node.id
  currentFile.value = null
  recordList.value = []
  await loadFileList()
}

const openCreateCategoryDialog = () => {
  categoryDialogMode.value = 'create'
  editingCategoryId.value = ''
  categoryForm.name = ''
  categoryDialogVisible.value = true
}

const openEditCategoryDialog = (node) => {
  categoryDialogMode.value = 'edit'
  editingCategoryId.value = node.id
  categoryForm.name = node.name
  categoryDialogVisible.value = true
}

const submitCategory = async () => {
  const categoryName = categoryForm.name.trim()
  if (!categoryName) {
    ElMessage.warning('请输入分类名称')
    return
  }

  if (categoryDialogMode.value === 'create') {
    await createCategory({
      name: categoryName,
      parent_id: null,
    })
  } else {
    await updateCategory(editingCategoryId.value, {
      name: categoryName,
    })
  }

  categoryDialogVisible.value = false
  ElMessage.success('分类保存成功')
  await loadCategoryTree()
}

const handleDeleteCategory = async (node) => {
  try {
    await ElMessageBox.confirm(`确定删除分类“${node.name}”吗？`, '提示', {
      type: 'warning',
    })
    await deleteCategory(node.id)
    ElMessage.success('分类已删除')
    if (selectedCategory.value === node.id) {
      selectedCategory.value = 'all'
    }
    await loadCategoryTree()
    await loadFileList()
  } catch {
    // 用户取消删除时不提示错误
  }
}

const handleSearch = async () => {
  await loadFileList()
}

const handleReset = async () => {
  searchForm.filename = ''
  await loadFileList()
}

const openUploadDialog = () => {
  uploadForm.categoryId = selectedCategory.value && selectedCategory.value !== 'all' ? String(selectedCategory.value) : ''
  uploadDialogVisible.value = true
}

const beforeUploadExcel = (file) => {
  if (!uploadForm.categoryId) {
    ElMessage.warning('请先在弹窗中选择分类')
    return false
  }

  const lowerName = file.name.toLowerCase()
  const valid = lowerName.endsWith('.xlsx') || lowerName.endsWith('.xls') || lowerName.endsWith('.csv')
  if (!valid) {
    ElMessage.warning('仅支持数据文件（.xlsx/.xls/.csv）')
  }
  return valid
}

const handleUpload = async (uploadRequest) => {
  if (!uploadForm.categoryId) {
    ElMessage.warning('请先选择分类后再上传')
    uploadRequest.onError(new Error('未选择分类'))
    return
  }

  const formData = new FormData()
  formData.append('file', uploadRequest.file)
  formData.append('category_id', String(uploadForm.categoryId))

  try {
    await uploadDataFile(formData)
    selectedCategory.value = String(uploadForm.categoryId)
    uploadDialogVisible.value = false
    ElMessage.success('上传成功')
    await loadFileList()
    uploadRequest.onSuccess()
  } catch (error) {
    uploadRequest.onError(error)
  }
}

const handleDownloadTemplate = async () => {
  const blob = await downloadDataTemplate()
  const url = window.URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = 'data_template.xlsx'
  document.body.appendChild(link)
  link.click()
  link.remove()
  window.URL.revokeObjectURL(url)
}

const handleViewFile = async (row) => {
  currentFile.value = row
  contentLoading.value = true
  try {
    const result = await getFileRecords(row.id, { limit: 500 })
    recordList.value = result.records
  } finally {
    contentLoading.value = false
  }
}

const handleDeleteFile = async (row) => {
  try {
    await ElMessageBox.confirm(`确定删除文件“${row.filename}”吗？`, '提示', {
      type: 'warning',
    })
    await deleteDataFile(row.id)
    ElMessage.success('文件已删除')

    if (currentFile.value?.id === row.id) {
      currentFile.value = null
      recordList.value = []
    }
    await loadFileList()
  } catch {
    // 用户取消删除时不提示错误
  }
}

onMounted(async () => {
  loadLayoutCache()
  await loadCategoryTree()
  selectedCategory.value = selectedCategory.value || 'all'
  await loadFileList()
})

onBeforeUnmount(() => {
  stopDragging()
})
</script>

<template>
  <section class="data-page" :class="{ dragging: !!dragState }">
    <div ref="layoutRef" class="data-layout">
      <el-card class="pane-card pane-card--tree" shadow="hover" :style="{ width: `${leftPaneWidth}%` }">
        <template #header>
          <div class="pane-header">
            <div>
              <span>分类树</span>
            </div>
            <el-button type="primary" plain size="small" @click="openCreateCategoryDialog">
              新增
            </el-button>
          </div>
        </template>

        <el-tree
          :data="categoryTree"
          node-key="id"
          highlight-current
          default-expand-all
          :current-node-key="selectedCategory || undefined"
          :expand-on-click-node="false"
          @node-click="handleCategoryClick"
        >
          <template #default="{ data }">
            <div class="tree-node">
              <div class="tree-main">
                <el-icon class="tree-icon">
                  <component :is="data.id === 'all' ? FolderOpened : Folder" />
                </el-icon>
                <span class="tree-name">{{ data.name }}</span>
              </div>
              <span v-if="data.id !== 'all'" class="tree-actions">
                <el-button link type="primary" :icon="Edit" @click.stop="openEditCategoryDialog(data)" />
                <el-button link type="danger" :icon="Delete" @click.stop="handleDeleteCategory(data)" />
              </span>
            </div>
          </template>
        </el-tree>
      </el-card>

      <div class="pane-divider" @mousedown.prevent="(event) => startDragging('left', event)" />

      <el-card class="pane-card" shadow="hover" :style="{ width: `${middlePaneWidth}%` }">
        <template #header>
          <div class="pane-header">
            <span>文件列表</span>
          </div>
        </template>

        <el-form :inline="true" class="search-form">
          <el-form-item>
            <el-input v-model="searchForm.filename" placeholder="文件名" clearable />
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="handleSearch">搜索</el-button>
            <el-button @click="handleReset">重置</el-button>
            <el-button type="info" plain @click="handleDownloadTemplate">下载模板</el-button>
            <el-button type="primary" :icon="Upload" @click="openUploadDialog">上传文件</el-button>
          </el-form-item>
        </el-form>

        <el-table :data="fileList" border stripe v-loading="loadingFiles" height="calc(100vh - 250px)">
          <el-table-column prop="filename" label="文件名" min-width="160" />
          <el-table-column prop="uploaded_at" label="上传时间" min-width="170" />
          <el-table-column prop="data_count" label="数据量" width="90" />
          <el-table-column label="操作" width="150" fixed="right">
            <template #default="{ row }">
              <el-button link type="primary" @click="handleViewFile(row)">查看</el-button>
              <el-button link type="danger" @click="handleDeleteFile(row)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-card>

      <div class="pane-divider" @mousedown.prevent="(event) => startDragging('middle', event)" />

      <el-card class="pane-card" shadow="hover" :style="{ width: `${rightPaneWidth}%` }">
        <template #header>
          <div class="pane-header">
            <span>
              文件内容
              <span v-if="currentFile" class="file-title">- {{ currentFile.filename }}</span>
            </span>
            <el-radio-group v-model="contentMode" size="small">
              <el-radio-button value="table">表格</el-radio-button>
              <el-radio-button value="curve">曲线</el-radio-button>
            </el-radio-group>
          </div>
        </template>

        <div class="content-wrap" v-loading="contentLoading">
          <el-empty v-if="!currentFile" description="请选择并查看一个文件" />

          <el-table
            v-else-if="contentMode === 'table'"
            :data="recordList"
            border
            stripe
            height="calc(100vh - 250px)"
          >
            <el-table-column prop="time" label="时间" min-width="140" />
            <el-table-column prop="current" label="电流" min-width="100" />
            <el-table-column prop="voltage" label="电压" min-width="100" />
            <el-table-column prop="set_temperature" label="设定温度" min-width="100" />
            <el-table-column prop="actual_temperature" label="实际温度" min-width="100" />
          </el-table>

          <DataCurve v-else :records="recordList" />
        </div>
      </el-card>
    </div>

    <el-dialog v-model="uploadDialogVisible" title="上传数据文件" width="460px">
      <el-form label-position="top">
        <el-form-item label="选择分类" required>
          <el-select v-model="uploadForm.categoryId" placeholder="请选择要上传到的分类" style="width: 100%">
            <el-option
              v-for="item in flatCategoryOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>

        <el-form-item label="上传文件" required>
          <el-upload
            class="upload-panel"
            drag
            :show-file-list="false"
            :http-request="handleUpload"
            :before-upload="beforeUploadExcel"
            accept=".xlsx,.xls,.csv"
          >
            <el-icon class="upload-panel__icon"><Upload /></el-icon>
            <div class="el-upload__text">拖拽文件到此处，或点击选择 Excel / CSV 文件</div>
            <template #tip>
              <div class="el-upload__tip">支持 .xlsx / .xls / .csv，上传前请先选择分类</div>
            </template>
          </el-upload>
        </el-form-item>
      </el-form>

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

    <el-dialog v-model="categoryDialogVisible" :title="categoryDialogTitle" width="420px">
      <el-form label-position="top">
        <el-form-item label="分类名称" required>
          <el-input v-model="categoryForm.name" maxlength="100" show-word-limit />
        </el-form-item>
      </el-form>

      <template #footer>
        <el-button @click="categoryDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitCategory">保存</el-button>
      </template>
    </el-dialog>
  </section>
</template>

<style lang="scss" scoped>
.data-page {
  height: 100%;
  background: var(--bg-page);

  &.dragging {
    user-select: none;
    cursor: col-resize;
  }
}

.data-layout {
  height: 100%;
  display: flex;
  align-items: stretch;
  gap: 0;
  background: var(--bg-white);
  border: 1px solid var(--border-color);
  box-shadow: var(--shadow-card);
}

.pane-divider {
  width: 4px;
  flex-shrink: 0;
  background: var(--border-color);
  cursor: col-resize;
  transition: background 0.15s;
  position: relative;

  &::after {
    content: '';
    position: absolute;
    inset: 0 -2px;
  }

  &:hover,
  .dragging & {
    background: var(--primary-border);
  }
}

.pane-card {
  height: 100%;
  min-width: 0;
  border-radius: 0;
  border: none;
  border-right: 1px solid var(--border-color);
  box-shadow: none !important;
  background: var(--bg-white);

  :deep(.el-card__header) {
    padding: 0 16px;
    height: 44px;
    display: flex;
    align-items: center;
    border-bottom: 1px solid var(--border-color);
    background: #F7F8FA;
  }

  :deep(.el-card__body) {
    height: calc(100% - 44px);
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px 16px;
  }

  &--tree {
    :deep(.el-card__body) {
      padding: 8px 0;
      overflow-y: auto;
    }
  }
}

.pane-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
}

.file-title {
  font-size: 13px;
  color: var(--text-secondary);
  font-weight: 400;
  margin-left: 4px;
}

.tree-node {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-right: 4px;
}

.tree-main {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.tree-icon {
  font-size: 14px;
  color: var(--primary);
  flex: 0 0 auto;
}

.tree-name {
  font-size: 13px;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tree-actions {
  display: none;
  flex-shrink: 0;

  .el-button {
    padding: 0 3px;
    font-size: 13px;
    color: var(--text-tertiary);

    &:hover {
      color: var(--primary);
    }
  }
}

.tree-node:hover .tree-actions {
  display: inline-flex;
}

.upload-panel {
  width: 100%;
}

.upload-panel__icon {
  font-size: 28px;
  color: var(--primary);
  margin-bottom: 8px;
}

.search-form {
  margin-bottom: 4px;
  flex-shrink: 0;
}

.content-wrap {
  flex: 1;
  min-height: 0;
}

@media (max-width: 980px) {
  .data-layout {
    display: block;
    overflow-y: auto;
  }

  .pane-divider {
    display: none;
  }

  .pane-card {
    width: 100% !important;
    min-height: 420px;
    margin-bottom: 12px;
    border-right: none;
    border-bottom: 1px solid var(--border-color);
  }
}
</style>
