<template>
  <div class="dashboard">
    <!-- Top stats row -->
    <div class="stats-row animate-in">
      <div
        v-if="statsLoading"
        v-for="n in 4"
        :key="'sk'+n"
        class="stat-card"
      >
        <div class="skeleton skeleton-circle" style="width:44px;height:44px;border-radius:12px"></div>
        <div class="stat-info" style="gap:4px">
          <div class="skeleton skeleton-text" style="width:48px;height:24px"></div>
          <div class="skeleton skeleton-text short" style="width:64px;height:14px"></div>
        </div>
      </div>
      <div
        v-if="!statsLoading"
        class="stat-card"
        v-for="stat in stats"
        :key="stat.label"
        :class="{ clickable: stat.route }"
        @click="stat.route && $router.push(stat.route)"
      >
        <div class="stat-icon" :style="{ background: stat.bg }">
          <span class="stat-emoji">{{ stat.emoji }}</span>
        </div>
        <div class="stat-info">
          <span class="stat-value">{{ stat.value }}</span>
          <span class="stat-label">{{ stat.label }}</span>
        </div>
      </div>
    </div>

    <div class="main-grid">
      <!-- Left column: Progress + Logs -->
      <div class="col-left">
        <!-- Active tasks list -->
        <div class="card progress-card animate-in animate-in-delay-1">
          <div class="card-head">
            <h3>处理进度</h3>
            <el-tag v-if="visibleTasks.length > 0" size="small" effect="dark">
              {{ visibleTasks.length }} 个任务
            </el-tag>
          </div>

          <div v-if="visibleTasks.length > 0" class="task-cards">
            <div v-for="task in visibleTasks" :key="task.task_id" class="task-card-item">
              <div class="task-card-header">
                <span class="task-name">{{ task.name }}</span>
                <el-tag :type="statusTagType(task.status)" size="small">{{ statusLabel(task.status) }}</el-tag>
              </div>
              <el-progress v-if="task.status === 'running' || task.status === 'pending'" :percentage="task.progress" :stroke-width="8" />
              <div v-if="task.message" class="task-message">{{ task.message }}</div>
              <!-- Error display -->
              <el-alert v-if="task.status === 'failed' && task.error" :title="task.error" type="error" show-icon :closable="false" class="task-error" />
              <!-- Actions -->
              <div class="task-card-actions">
                <el-button v-if="task.status === 'failed'" type="warning" size="small" @click="handleRetry(task.task_id)">重试</el-button>
                <el-button v-if="task.status === 'completed' || task.status === 'failed'" size="small" @click="handleDismiss(task.task_id)">关闭</el-button>
              </div>
              <!-- Log lines for this task -->
              <div v-if="task.log_lines?.length" class="task-logs">
                <div v-for="(log, i) in task.log_lines.slice(-50)" :key="i" class="log-line">{{ log }}</div>
              </div>
            </div>
          </div>
          <div v-else class="empty-state">
            <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#d1d5db" stroke-width="1">
              <circle cx="12" cy="12" r="10"/>
              <polyline points="12,6 12,12 16,14"/>
            </svg>
            <p>等待任务启动</p>
          </div>
        </div>

        <!-- Logs -->
        <div class="card log-card animate-in animate-in-delay-2">
          <div class="card-head">
            <h3>处理日志</h3>
            <div style="display:flex;gap:8px;align-items:center">
              <el-button size="small" link @click="$router.push('/tasks')">查看全部日志</el-button>
              <el-button size="small" link @click="clearLogs">清空</el-button>
            </div>
          </div>
          <div ref="logBox" class="log-box">
            <div v-if="logs.length === 0" class="empty-state small">
              <p>暂无日志</p>
            </div>
            <div
              v-for="(line, i) in logs"
              :key="i"
              class="log-line"
              :class="logCls(line)"
            >
              <span class="log-time">{{ fmtTime(i) }}</span>
              {{ line }}
            </div>
          </div>
        </div>
      </div>

      <!-- Right column: Upload + Actions -->
      <div class="col-right">
        <!-- Upload zone -->
        <div class="card animate-in animate-in-delay-1">
          <div class="card-head">
            <h3>文件上传</h3>
            <el-button size="small" @click="refreshStats" :icon="Refresh">刷新</el-button>
          </div>

          <div
            class="drop-zone"
            :class="{ dragover: isDragOver }"
            @dragover.prevent="isDragOver = true"
            @dragleave="isDragOver = false"
            @drop.prevent="handleDrop"
            @click="triggerInput"
          >
            <div class="drop-icon">
              <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
                <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/>
                <polyline points="17,8 12,3 7,8"/>
                <line x1="12" y1="3" x2="12" y2="15"/>
              </svg>
            </div>
            <p class="drop-text">拖拽文件到此处或 <span class="drop-link">点击选择</span></p>
            <p class="drop-hint">支持 JPG / PNG / BMP (自动OCR) / XLS / XLSX (自动处理)</p>
            <input
              ref="fileInput"
              type="file"
              multiple
              accept=".jpg,.jpeg,.png,.bmp,.xls,.xlsx"
              hidden
              @change="handleSelect"
            />
          </div>

          <!-- Upload progress -->
          <div v-if="uploading" class="upload-section">
            <div class="upload-info">
              <span class="upload-filename">{{ uploadingName }}</span>
              <span class="upload-pct">{{ uploadPct }}%</span>
            </div>
            <div class="upload-bar">
              <div class="upload-bar-fill" :style="{ width: uploadPct + '%' }"></div>
            </div>
          </div>
        </div>

        <!-- Quick actions -->
        <div class="card animate-in animate-in-delay-2">
          <div class="card-head">
            <h3>快捷操作</h3>
          </div>
          <div class="action-grid">
            <button class="action-btn" @click="runPipeline" :disabled="processing">
              <div class="action-icon secondary">
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
                  <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
                </svg>
              </div>
              <div class="action-info">
                <span class="action-name">一键全流程</span>
                <span class="action-desc">OCR识别 → Excel处理 → 生成采购单</span>
              </div>
            </button>
            <button class="action-btn" @click="runOcr" :disabled="processing">
              <div class="action-icon secondary">
                <el-icon :size="20"><Document /></el-icon>
              </div>
              <div class="action-info">
                <span class="action-name">批量OCR识别</span>
                <span class="action-desc">仅识别图片</span>
              </div>
            </button>
            <button class="action-btn" @click="runExcel" :disabled="processing">
              <div class="action-icon secondary">
                <el-icon :size="20"><Grid /></el-icon>
              </div>
              <div class="action-info">
                <span class="action-name">Excel数据处理</span>
                <span class="action-desc">处理Excel生成采购单</span>
              </div>
            </button>
            <button class="action-btn action-btn-danger" @click="clearAll" :disabled="processing">
              <div class="action-icon danger">
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
                  <polyline points="3,6 5,6 21,6"/>
                  <path d="M19,6v14a2,2,0,0,1-2,2H7a2,2,0,0,1-2-2V6M8,6V4a2,2,0,0,1,2-2h4a2,2,0,0,1,2,2V6"/>
                </svg>
              </div>
              <div class="action-info">
                <span class="action-name">清除全部</span>
                <span class="action-desc">删除所有文件和处理记录</span>
              </div>
            </button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, nextTick, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Refresh, Document, Grid } from '@element-plus/icons-vue'
import { useProcessingStore } from '../stores/processing'
import api from '../api'

const ps = useProcessingStore()

const isDragOver = ref(false)
const uploading = ref(false)
const uploadPct = ref(0)
const uploadingName = ref('')
const processing = ref(false)
const fileInput = ref<HTMLInputElement>()
const logBox = ref<HTMLElement>()
const statsLoading = ref(true)

const detailedStats = ref({
  input_images: 0,
  output_excel: 0,
  unprocessed_images: 0,
  unprocessed_excel: 0,
  completed_results: 0,
  total_processed: 0,
})

const visibleTasks = computed(() =>
  ps.taskSource !== 'sync' ? ps.activeTaskList : []
)
const logs = computed(() => ps.logs.slice(0, 50))

const stats = computed(() => [
  {
    label: '未处理图片',
    value: detailedStats.value.unprocessed_images,
    emoji: '🖼️',
    bg: '#f4f4f5',
    route: '/files/images',
  },
  {
    label: '待处理Excel',
    value: detailedStats.value.unprocessed_excel,
    emoji: '📊',
    bg: '#f4f4f5',
    route: '/files/tables',
  },
  {
    label: '已完成采购单',
    value: detailedStats.value.completed_results,
    emoji: '✅',
    bg: '#f0fdf4',
    route: '/files/orders',
  },
  {
    label: '已处理总数',
    value: detailedStats.value.total_processed,
    emoji: '📦',
    bg: '#f4f4f5',
    route: null,
  },
])

function fmtTime(i: number): string {
  const d = new Date()
  d.setSeconds(d.getSeconds() - (logs.value.length - i))
  return d.toTimeString().slice(0, 8)
}

function logCls(line: string): string {
  if (line.includes('失败') || line.includes('错误')) return 'err'
  if (line.includes('完成')) return 'ok'
  return ''
}

function clearLogs(): void {
  ps.logs.splice(0)
}

function statusTagType(status: string): string {
  const map: Record<string, string> = { pending: 'info', running: '', completed: 'success', failed: 'danger' }
  return map[status] || 'info'
}

function statusLabel(status: string): string {
  const map: Record<string, string> = { pending: '等待中', running: '运行中', completed: '已完成', failed: '失败' }
  return map[status] || status
}

async function handleRetry(taskId: string): Promise<void> {
  try {
    await ps.retryTask(taskId)
    ElMessage.success('已重新提交任务')
  } catch {
    ElMessage.error('重试失败')
  }
}

function handleDismiss(taskId: string): void {
  ps.removeTask(taskId)
}

async function refreshStats(): Promise<void> {
  statsLoading.value = true
  try {
    const res = await api.get('/files/stats/detailed')
    detailedStats.value = res.data
  } catch {
    // silent
  } finally {
    statsLoading.value = false
  }
}

function triggerInput(): void {
  fileInput.value?.click()
}

async function handleDrop(e: DragEvent): Promise<void> {
  isDragOver.value = false
  if (e.dataTransfer?.files) {
    await upload(Array.from(e.dataTransfer.files))
  }
}

async function handleSelect(e: Event): Promise<void> {
  const el = e.target as HTMLInputElement
  if (el.files) {
    await upload(Array.from(el.files))
    el.value = ''
  }
}

function getTargetDir(fileName: string): string {
  const ext = fileName.split('.').pop()?.toLowerCase() || ''
  if (['jpg', 'jpeg', 'png', 'bmp'].includes(ext)) return 'input'
  if (['xls', 'xlsx'].includes(ext)) return 'output'
  return 'input'
}

function getFileTypeLabel(fileName: string): string {
  const ext = fileName.split('.').pop()?.toLowerCase() || ''
  if (['jpg', 'jpeg', 'png', 'bmp'].includes(ext)) return 'OCR'
  if (['xls', 'xlsx'].includes(ext)) return 'Excel'
  return ''
}

async function upload(files: File[]): Promise<void> {
  uploading.value = true
  uploadPct.value = 0
  const uploadedFiles: { name: string; type: string }[] = []
  const failedFiles: string[] = []
  for (let i = 0; i < files.length; i++) {
    const file = files[i]
    uploadingName.value = file.name
    const target = getTargetDir(file.name)
    const fd = new FormData()
    fd.append('file', file)
    try {
      await api.post(`/files/upload?target=${target}`, fd, {
        onUploadProgress: (e) => {
          uploadPct.value = Math.round(
            ((i + (e.loaded / (e.total || 1))) / files.length) * 100
          )
        },
      })
      const typeLabel = getFileTypeLabel(file.name)
      uploadedFiles.push({ name: file.name, type: typeLabel })
    } catch (err: any) {
      failedFiles.push(file.name)
    }
  }
  uploading.value = false
  uploadingName.value = ''
  uploadPct.value = 0
  refreshStats()

  // Show upload results
  if (uploadedFiles.length > 0) {
    ElMessage.success(`${uploadedFiles.length} 个文件上传成功`)
  }
  if (failedFiles.length > 0) {
    ElMessage.error(`${failedFiles.length} 个文件上传失败: ${failedFiles.join(', ')}`)
  }

  // Auto-process: pipeline for images, excel for Excel files
  if (uploadedFiles.length > 0) {
    const hasImages = uploadedFiles.some(f => f.type === 'OCR')
    const hasExcel = uploadedFiles.some(f => f.type === 'Excel')
    if (hasImages) {
      await doAction('/processing/pipeline')
    } else if (hasExcel) {
      await doAction('/processing/excel')
    }
  }
}

async function doAction(endpoint: string): Promise<void> {
  processing.value = true
  try {
    await ps.startTask(endpoint)
  } catch (err: any) {
    ElMessage.error(err.response?.data?.detail || '启动失败')
  } finally {
    processing.value = false
  }
}

const runPipeline = () => doAction('/processing/pipeline')
const runOcr = () => doAction('/processing/ocr-batch')
const runExcel = () => doAction('/processing/excel')

async function clearAll(): Promise<void> {
  try {
    await ElMessageBox.confirm(
      '将删除 input、output、result 目录下所有文件，并清除全部处理记录。此操作不可撤销，是否继续？',
      '清除全部数据',
      { type: 'warning', confirmButtonText: '确认清除', cancelButtonText: '取消' }
    )
  } catch { return }

  processing.value = true
  try {
    await Promise.all([
      api.post('/files/clear/input'),
      api.post('/files/clear/output'),
      api.post('/files/clear/result'),
      api.delete('/tasks'),
      api.post('/files/relations/sync'),
    ])
    ElMessage.success('已清除所有文件和处理记录')
    refreshStats()
  } catch (err: any) {
    ElMessage.error(err.response?.data?.detail || '清除失败')
  } finally {
    processing.value = false
  }
}

// Auto-refresh stats when any task completes or fails
watch(
  () => visibleTasks.value.map(t => t.status),
  (statuses) => {
    if (statuses.some(s => s === 'completed' || s === 'failed')) {
      refreshStats()
    }
  }
)

// Auto-scroll log panel
watch(
  logs,
  async () => {
    await nextTick()
    if (logBox.value) {
      logBox.value.scrollTop = logBox.value.scrollHeight
    }
  },
  { deep: true }
)

onMounted(() => {
  refreshStats()
})
</script>

<style scoped>
.dashboard {
  width: 100%;
}

/* ── Stats row ── */
.stats-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  margin-bottom: 20px;
}

.stat-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 18px;
  background: var(--bg-card);
  border-radius: var(--radius);
  border: 1px solid var(--border-light);
  transition: border-color 0.15s ease;
}

.stat-card:hover {
  border-color: #d4d4d8;
}

.stat-card.clickable {
  cursor: pointer;
}

.stat-card.clickable:hover {
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary);
}

.stat-icon {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.stat-emoji {
  font-size: 20px;
  line-height: 1;
}

.stat-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.stat-value {
  font-size: 24px;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1;
  font-family: var(--font-mono);
}

.stat-label {
  font-size: 13px;
  color: var(--text-muted);
}

/* ── Main grid ── */
.main-grid {
  display: grid;
  grid-template-columns: 1fr 400px;
  gap: 16px;
  align-items: start;
}

/* ── Card ── */
.card {
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 12px;
  transition: border-color 0.15s ease;
}

.card:hover {
  border-color: #d4d4d8;
}

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}

.card-head h3 {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: -0.01em;
}

/* ── Drop zone ── */
.drop-zone {
  border: 1px dashed #d4d4d8;
  border-radius: var(--radius);
  padding: 32px 20px;
  text-align: center;
  cursor: pointer;
  transition: all 0.15s ease;
  background: #fafafa;
}

.drop-zone:hover,
.drop-zone.dragover {
  border-color: #a1a1aa;
  background: #f4f4f5;
}

.drop-zone.dragover {
  border-color: var(--primary);
}

.drop-icon {
  color: #a1a1aa;
  margin-bottom: 12px;
  transition: color 0.15s;
}

.drop-zone:hover .drop-icon {
  color: var(--text-primary);
}

.drop-text {
  font-size: 14px;
  color: var(--text-secondary);
}

.drop-link {
  color: var(--text-primary);
  font-weight: 600;
}

.drop-hint {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 6px;
}

/* ── Upload progress ── */
.upload-section {
  margin-top: 14px;
}

.upload-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 6px;
}

.upload-filename {
  font-size: 12px;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 240px;
}

.upload-pct {
  font-size: 12px;
  font-weight: 600;
  font-family: var(--font-mono);
  color: var(--text-primary);
}

.upload-bar {
  height: 4px;
  background: #f4f4f5;
  border-radius: 999px;
  overflow: hidden;
}

.upload-bar-fill {
  height: 100%;
  background: var(--primary);
  border-radius: 999px;
  transition: width 0.3s ease;
}

/* ── Action buttons ── */
.action-grid {
  display: grid;
  grid-template-columns: repeat(1, 1fr);
  gap: 8px;
}

.action-btn {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border: 1px solid #e4e4e7;
  border-radius: var(--radius-sm);
  background: #ffffff;
  cursor: pointer;
  transition: all 0.15s ease;
  text-align: left;
}

.action-btn:hover:not(:disabled) {
  background: #f4f4f5;
  border-color: #d4d4d8;
}

.action-btn:active:not(:disabled) {
  background: #e4e4e7;
}

.action-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.action-icon {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.action-icon.secondary {
  background: #f4f4f5;
  color: #525252;
}

.action-icon.danger {
  background: rgba(239,68,68,0.08);
  color: #ef4444;
}

.action-btn-danger:hover:not(:disabled) {
  border-color: #fca5a5;
  background: #fef2f2;
}

.action-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.action-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
}

.action-desc {
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Task cards ── */
.task-cards {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.task-card-item {
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  background: #fafafa;
  transition: border-color 0.15s ease;
}

.task-card-item:hover {
  border-color: #d4d4d8;
}

.task-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

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

.task-message {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 8px;
}

.task-error {
  margin-top: 8px;
}

.task-card-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

.task-logs {
  margin-top: 10px;
  max-height: 200px;
  overflow-y: auto;
  background: #09090b;
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.6;
}

.task-logs .log-line {
  color: #a1a1aa;
  padding: 0;
  word-break: break-all;
}

/* ── Progress area ── */
.progress-card {
  display: flex;
  flex-direction: column;
}

.progress-area {
  padding: 4px 0;
}

.progress-bar-wrapper {
  margin-bottom: 12px;
}

.progress-bar-track {
  width: 100%;
  height: 6px;
  background: #f4f4f5;
  border-radius: 999px;
  overflow: hidden;
}

.progress-bar-fill {
  height: 100%;
  border-radius: 999px;
  transition: width 0.5s ease;
  min-width: 0;
}

.progress-meta {
  display: flex;
  align-items: center;
  gap: 12px;
}

.progress-pct {
  font-size: 20px;
  font-weight: 700;
  font-family: var(--font-mono);
  line-height: 1;
}

.progress-msg {
  font-size: 13px;
  color: var(--text-muted);
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Empty state ── */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 32px 0;
  color: var(--text-muted);
  font-size: 14px;
}

.empty-state.small {
  padding: 20px 0;
}

/* ── Logs ── */
.log-card {
  display: flex;
  flex-direction: column;
  max-height: 600px;
}

.log-box {
  flex: 1;
  max-height: 600px;
  min-height: 400px;
  overflow-y: auto;
  background: #09090b;
  border-radius: var(--radius-sm);
  padding: 14px;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.7;
}

.log-line {
  color: #a1a1aa;
  padding: 1px 0;
}

.log-line.err {
  color: #ef4444;
}

.log-line.ok {
  color: #22c55e;
}

.log-time {
  color: #525252;
  margin-right: 8px;
  font-size: 11px;
  user-select: none;
}

/* ── Responsive ── */
@media (max-width: 1024px) {
  .stats-row {
    grid-template-columns: repeat(2, 1fr);
  }

  .main-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 640px) {
  .stats-row {
    grid-template-columns: 1fr;
  }

  .action-grid {
    grid-template-columns: 1fr;
  }
}
</style>
