// 核心库
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import { connect } from 'dva'
// 组件库
import XLSX from 'xlsx'
import { Table, Button, Dropdown, Menu, Icon, message } from 'antd'
// 工具库
import { renderExportCell } from '../utils/common'

function ExportToExcel (props) {
  /**
   * 将真实 dom 中的 table 节点转换成 excel 并下载
   * @param {*} tableEle table dom
   */
  const tableToBook = (tableDom, insertDom) => {
    message.info('正在生成表格，请稍等...')
    /**
     * { raw: true } 参数表示保留原始字符串
     * 1、避免将 Date 字符串进行转换，这里就会有时区问题，见：https://github.com/SheetJS/sheetjs/issues/1470
     * 2、避免不必要的数字转换，比如将 18064505555 转换成 1.84E+10
     * ps：如果出现时区问题，可用 moment(date).format() 来解决
     */
    const ws = XLSX.utils.table_to_sheet(insertDom || tableDom, { raw: true })
    insertDom && XLSX.utils.sheet_add_dom(ws, tableDom, { raw: true, origin: -1 })
    // 新建工作簿
    const wb = XLSX.utils.book_new()
    // 将 sheet 添加到工作簿中
    XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')
    XLSX.writeFile(wb, `${props.fileName || '表单列表'}.xlsx`)
  }

  /**
   * 额外插入的表格
   * @param {*} colSpan 插入行需要合并的单元格数
   */
  const insertTableDom = (colSpan) => {
    const insertEle = document.createElement('div')
    if (props.insertRows.length) {
      ReactDOM.render(
        <table>
          <thead>
            {
              props.insertRows.map(row => {
                return (
                  <tr key={row}>
                    <th colSpan={colSpan}>{row}</th>
                  </tr>
                )
              })
            }
          </thead>
        </table>,
        insertEle
      )
    }
    return insertEle
  }

  /**
   * 构建出一个真实 table dom，并通过 tableToBook 生成 excel 表并下载
   * @param {*} columns
   * @param {*} dataSource
   */
  const exportToExcel = (columns, dataSource) => {
    // 构建一个真实 table dom
    let parentEle = document.createElement('div')
    ReactDOM.render(
      <Table
        rowKey='ID'
        columns={columns}
        dataSource={dataSource}
        pagination={false}
      />,
      parentEle
    )
    // 需要额外插入的表格
    let insertEle = insertTableDom(columns.length)
    // 生成 excel 表并下载
    tableToBook(parentEle.querySelector('table'), insertEle.querySelector('table'))
    // 释放变量，防止内存溢出
    parentEle = null
    insertEle = null
  }

  /**
   * 获取所有数据作为 dataSource 数据源
   * @param {*} pagination
   * @param {*} onlineDataSource
   * @param {*} columns
   * @param {*} selectedKeys
   */
  const getData = (pagination, onlineDataSource, columns, selectedKeys) => {
    const { dispatch, config } = props
    dispatch({
      ...config,
      payload: {
        ...config.payload,
        ...pagination
      },
      onlyData: false,
      onComplete: (res) => {
        if (res && res.IsSuccessful) {
          const { Data, TotalCount } = res
          onlineDataSource = [...onlineDataSource, ...Data]
          const { Start, Limit, PageIndex } = pagination
          // 为保证服务器效率，分页加载
          if (Start + Limit < TotalCount) {
            pagination = {
              ...pagination,
              Start: PageIndex * Limit,
              PageIndex: PageIndex + 1
            }
            getData(pagination, onlineDataSource, columns, selectedKeys)
          } else {
            let filterDataSource = onlineDataSource
            if (selectedKeys && selectedKeys.length) {
              filterDataSource = onlineDataSource.filter(ods => selectedKeys.indexOf(ods.ID) !== -1)
            }
            exportToExcel(columns, filterDataSource)
          }
        }
      }
    })
  }

  /**
   * 按条件导出，根据查询条件获取所有数据并导出
   * @param {*} columns
   * @param {*} dataSource
   * @param {*} selectedKeys
   */
  const onlineExport = (columns, dataSource, selectedKeys) => {
    if (!props.config) {
      // 没有接口配置，则按实际传入数据源导出数据
      exportToExcel(columns, dataSource)
      return
    }
    // 根据查询条件获取所有数据并导出
    message.info('数据准备中...')
    getData({
      Start: 0,
      Limit: 1000,
      PageIndex: 1
    }, [], columns, selectedKeys)
  }

  /**
   * 按选择导出
   * @param {*} columns
   * @param {*} dataSource
   * @param {*} selectedKeys
   */
  const selectedExport = (columns, dataSource, selectedKeys) => {
    const filterDataSource = dataSource.filter(ds => selectedKeys.indexOf(ds.ID) !== -1)
    if (filterDataSource.length === selectedKeys.length) {
      // 当前页选中数据导出
      exportToExcel(columns, filterDataSource)
    } else {
      // 所有页选中数据导出
      onlineExport(columns, dataSource, selectedKeys)
    }
  }

  /**
   * 导出操作
   * @param {*} param0
   */
  const handleExport = ({ key }) => {
    const { app, columns, columnConfigs, dataSource, selectedKeys, tableId } = props
    // 现有 table 直接导出
    if (tableId) {
      const tableEle = document.getElementById(tableId)
      const tableDom = tableEle.tagName === 'TABLE' ? tableEle : tableEle.querySelector('table')
      const tableTheadTr = tableDom.querySelector('tr')
      const tableTheadTrChidlren = Array.prototype.slice.call(tableTheadTr && tableTheadTr.children ? tableTheadTr.children : [])
      let insertEle = insertTableDom(tableTheadTrChidlren.reduce(function (sum, obj) { return sum + obj.colSpan }, 0))
      tableToBook(tableDom, insertEle.querySelector('table'))
      insertEle = null
      return
    }
    // 否则 columns 为必须配置
    if (!columns.length) {
      message.error('表格配置错误')
      return
    }
    const exportColumns = []
    columns.forEach(col => {
      // 去除不必要的列
      if (col.key === 'ROW_NO') return
      // 去除 fixed 的 table，因为 fixed 会渲染出多个 table
      let copyCol = {
        ...col,
        fixed: false
      }
      // 去除原先的 render 函数，因为只需要导出数据，而不需要导出各种操作
      if (copyCol.render && copyCol.render instanceof Function) {
        const targetConfig = columnConfigs.find(ccf => ccf.field === col.dataIndex || ccf.field === col.key)
        const appEnums = app.enums || {}
        copyCol = {
          ...copyCol,
          render: text => {
            return renderExportCell(text, targetConfig ? targetConfig.dataSource : [], targetConfig, appEnums)
          }
        }
      }
      exportColumns.push(copyCol)
    })
    switch (key) {
      case 'online':
        onlineExport(exportColumns, dataSource)
        break
      case 'page':
        exportToExcel(exportColumns, dataSource)
        break
      case 'select':
        selectedExport(exportColumns, dataSource, selectedKeys)
        break
      default:
        break
    }
  }

  // 自定义渲染
  if (props.render && props.render instanceof Function) {
    return props.render({ onExport: handleExport })
  }

  return (
    <Dropdown
      overlay={
        <Menu onClick={handleExport}>
          <Menu.Item key='online' disabled={props.tableId}>按条件导出</Menu.Item>
          <Menu.Item key='page'>导出当前页</Menu.Item>
          <Menu.Item key='select' disabled={props.tableId || !props.selectedKeys.length}>按选择导出</Menu.Item>
        </Menu>
      }
    >
      <Button>导出 <Icon type='down' /></Button>
    </Dropdown>
  )
}

ExportToExcel.propTypes = {
  fileName: PropTypes.string,
  insertRows: PropTypes.array,
  columns: PropTypes.array,
  // column 对应的配置，比如可配枚举 [{ field: 'LOG_TYPE', enumName: 'PFSysLogTypeEnum' }]，代表需要用枚举替换值
  columnConfigs: PropTypes.array,
  dataSource: PropTypes.array,
  // 查询配置
  config: PropTypes.object,
  // 按选择导出，如果选中的 key 都在 dataSource 里面，则筛选 dataSource，如果不在则需要加载全部数据
  selectedKeys: PropTypes.array,
  // 直接将现有 table 表格数据导出，如果传了该属性，则该属性优先级最高
  tableId: PropTypes.string,
  // 自定义渲染
  render: PropTypes.func
}

ExportToExcel.defaultProps = {
  fileName: '表单列表',
  insertRows: [],
  columns: [],
  columnConfigs: [],
  dataSource: [],
  config: null,
  selectedKeys: [],
  tableId: ''
}

export default connect(({ app }) => ({ app }))(ExportToExcel)
