<!-- 导出 Excel 文件 -->
<template>
  <ElButton
    :type="type"
    :size="size"
    :loading="isExporting"
    :disabled="disabled || !hasData"
    v-ripple
    @click="handleExport"
  >
    <template #loading>
      <ElIcon class="is-loading">
        <Loading />
      </ElIcon>
      {{ loadingText }}
    </template>
    <slot>{{ buttonText }}</slot>
  </ElButton>
</template>

<script setup lang="ts">
  import * as XLSX from 'xlsx'
  import FileSaver from 'file-saver'
  import { ref, computed, nextTick } from 'vue'
  import { ElMessage } from 'element-plus'
  import { Loading } from '@element-plus/icons-vue'
  import type { ButtonType } from 'element-plus'
  import { useThrottleFn } from '@vueuse/core'

  defineOptions({ name: 'ArtExcelExport' })

  /** 导出数据类型 */
  type ExportValue = string | number | boolean | null | undefined | Date

  interface ExportData {
    [key: string]: ExportValue
  }

  /** 列配置 */
  interface ColumnConfig {
    /** 列标题 */
    title: string
    /** 列宽度 */
    width?: number
    /** 数据格式化函数 */
    formatter?: (value: ExportValue, row: ExportData, index: number) => string
  }

  /** 导出配置选项 */
  interface ExportOptions {
    /** 数据源 */
    data: ExportData[]
    /** 文件名（不含扩展名） */
    filename?: string
    /** 工作表名称 */
    sheetName?: string
    /** 按钮类型 */
    type?: ButtonType
    /** 按钮尺寸 */
    size?: 'large' | 'default' | 'small'
    /** 是否禁用 */
    disabled?: boolean
    /** 按钮文本 */
    buttonText?: string
    /** 加载中文本 */
    loadingText?: string
    /** 是否自动添加序号列 */
    autoIndex?: boolean
    /** 序号列标题 */
    indexColumnTitle?: string
    /** 列配置映射 */
    columns?: Record<string, ColumnConfig>
    /** 表头映射（简化版本，向后兼容） */
    headers?: Record<string, string>
    /** 最大导出行数 */
    maxRows?: number
    /** 是否显示成功消息 */
    showSuccessMessage?: boolean
    /** 是否显示错误消息 */
    showErrorMessage?: boolean
    /** 工作簿配置 */
    workbookOptions?: {
      /** 创建者 */
      creator?: string
      /** 最后修改者 */
      lastModifiedBy?: string
      /** 创建时间 */
      created?: Date
      /** 修改时间 */
      modified?: Date
    }
  }

  const props = withDefaults(defineProps<ExportOptions>(), {
    filename: () => `export_${new Date().toISOString().slice(0, 10)}`,
    sheetName: 'Sheet1',
    type: 'primary',
    size: 'default',
    disabled: false,
    buttonText: '导出 Excel',
    loadingText: '导出中...',
    autoIndex: false,
    indexColumnTitle: '序号',
    columns: () => ({}),
    headers: () => ({}),
    maxRows: 100000,
    showSuccessMessage: true,
    showErrorMessage: true,
    workbookOptions: () => ({})
  })

  const emit = defineEmits<{
    'before-export': [data: ExportData[]]
    'export-success': [filename: string, rowCount: number]
    'export-error': [error: ExportError]
    'export-progress': [progress: number]
  }>()

  /** 导出错误类型 */
  class ExportError extends Error {
    constructor(
      message: string,
      public code: string,
      public details?: any
    ) {
      super(message)
      this.name = 'ExportError'
    }
  }

  const isExporting = ref(false)

  /** 是否有数据可导出 */
  const hasData = computed(() => Array.isArray(props.data) && props.data.length > 0)

  /** 验证导出数据 */
  const validateData = (data: ExportData[]): void => {
    if (!Array.isArray(data)) {
      throw new ExportError('数据必须是数组格式', 'INVALID_DATA_TYPE')
    }

    if (data.length === 0) {
      throw new ExportError('没有可导出的数据', 'NO_DATA')
    }

    if (data.length > props.maxRows) {
      throw new ExportError(`数据行数超过限制（${props.maxRows}行）`, 'EXCEED_MAX_ROWS', {
        currentRows: data.length,
        maxRows: props.maxRows
      })
    }
  }

  /** 格式化单元格值 */
  const formatCellValue = (
    value: ExportValue,
    key: string,
    row: ExportData,
    index: number
  ): string => {
    // 使用列配置的格式化函数
    const column = props.columns[key]
    if (column?.formatter) {
      return column.formatter(value, row, index)
    }

    // 处理特殊值
    if (value === null || value === undefined) {
      return ''
    }

    if (value instanceof Date) {
      return value.toLocaleDateString('zh-CN')
    }

    if (typeof value === 'boolean') {
      return value ? '是' : '否'
    }

    return String(value)
  }

  /** 处理数据 */
  const processData = (data: ExportData[]): Record<string, string>[] => {
    const processedData = data.map((item, index) => {
      const processedItem: Record<string, string> = {}

      // 添加序号列
      if (props.autoIndex) {
        processedItem[props.indexColumnTitle] = String(index + 1)
      }

      // 处理数据列
      Object.entries(item).forEach(([key, value]) => {
        // 获取列标题
        let columnTitle = key
        if (props.columns[key]?.title) {
          columnTitle = props.columns[key].title
        } else if (props.headers[key]) {
          columnTitle = props.headers[key]
        }

        // 格式化值
        processedItem[columnTitle] = formatCellValue(value, key, item, index)
      })

      return processedItem
    })

    return processedData
  }

  /** 计算列宽度 */
  const calculateColumnWidths = (data: Record<string, string>[]): XLSX.ColInfo[] => {
    if (data.length === 0) return []

    const sampleSize = Math.min(data.length, 100) // 只取前100行计算列宽
    const columns = Object.keys(data[0])

    return columns.map((column) => {
      // 使用配置的列宽度
      const configWidth = Object.values(props.columns).find((col) => col.title === column)?.width

      if (configWidth) {
        return { wch: configWidth }
      }

      // 自动计算列宽度
      const maxLength = Math.max(
        column.length, // 标题长度
        ...data.slice(0, sampleSize).map((row) => String(row[column] || '').length)
      )

      // 限制最小和最大宽度
      const width = Math.min(Math.max(maxLength + 2, 8), 50)
      return { wch: width }
    })
  }

  /** 导出到 Excel */
  const exportToExcel = async (
    data: ExportData[],
    filename: string,
    sheetName: string
  ): Promise<void> => {
    try {
      emit('export-progress', 10)

      // 处理数据
      const processedData = processData(data)
      emit('export-progress', 30)

      // 创建工作簿
      const workbook = XLSX.utils.book_new()

      // 设置工作簿属性
      if (props.workbookOptions) {
        workbook.Props = {
          Title: filename,
          Subject: '数据导出',
          Author: props.workbookOptions.creator || 'Art Design Pro',
          Manager: props.workbookOptions.lastModifiedBy || '',
          Company: '系统导出',
          Category: '数据',
          Keywords: 'excel,export,data',
          Comments: '由系统自动生成',
          CreatedDate: props.workbookOptions.created || new Date(),
          ModifiedDate: props.workbookOptions.modified || new Date()
        }
      }

      emit('export-progress', 50)

      // 创建工作表
      const worksheet = XLSX.utils.json_to_sheet(processedData)

      // 设置列宽度
      worksheet['!cols'] = calculateColumnWidths(processedData)

      emit('export-progress', 70)

      // 添加工作表到工作簿
      XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)

      emit('export-progress', 85)

      // 生成 Excel 文件
      const excelBuffer = XLSX.write(workbook, {
        bookType: 'xlsx',
        type: 'array',
        compression: true
      })

      // 创建 Blob 并下载
      const blob = new Blob([excelBuffer], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
      })

      emit('export-progress', 95)

      // 使用时间戳确保文件名唯一
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
      const finalFilename = `${filename}_${timestamp}.xlsx`

      FileSaver.saveAs(blob, finalFilename)

      emit('export-progress', 100)

      // 等待下载开始
      await nextTick()

      return Promise.resolve()
    } catch (error) {
      throw new ExportError(`Excel 导出失败: ${(error as Error).message}`, 'EXPORT_FAILED', error)
    }
  }

  /** 处理导出 */
  const handleExport = useThrottleFn(async () => {
    if (isExporting.value) return

    isExporting.value = true

    try {
      // 验证数据
      validateData(props.data)

      // 触发导出前事件
      emit('before-export', props.data)

      // 执行导出
      await exportToExcel(props.data, props.filename, props.sheetName)

      // 触发成功事件
      emit('export-success', props.filename, props.data.length)

      // 显示成功消息
      if (props.showSuccessMessage) {
        ElMessage.success({
          message: `成功导出 ${props.data.length} 条数据`,
          duration: 3000
        })
      }
    } catch (error) {
      const exportError =
        error instanceof ExportError
          ? error
          : new ExportError(`导出失败: ${(error as Error).message}`, 'UNKNOWN_ERROR', error)

      // 触发错误事件
      emit('export-error', exportError)

      // 显示错误消息
      if (props.showErrorMessage) {
        ElMessage.error({
          message: exportError.message,
          duration: 5000
        })
      }

      console.error('Excel 导出错误:', exportError)
    } finally {
      isExporting.value = false
      emit('export-progress', 0)
    }
  }, 1000)

  // 暴露方法供父组件调用
  defineExpose({
    exportData: handleExport,
    isExporting: readonly(isExporting),
    hasData
  })
</script>

<style scoped>
  .is-loading {
    animation: rotating 2s linear infinite;
  }

  @keyframes rotating {
    0% {
      transform: rotate(0deg);
    }

    100% {
      transform: rotate(360deg);
    }
  }
</style>
