import React from 'react'
import moment from 'moment'
import { Table } from 'antd'
import { SearchBar } from '@lingzhi/calcium-react-components'
import BasePage from 'toss.components/BasePage'
import TabFilter from 'toss.components/TabFilter'
import TableEmptyText from 'toss.components/TableEmptyText'
import { isEmptyValue } from 'toss.utils/validate'
import { lang } from 'toss.utils/lang'
import './styles.scss'

const totalFormatter = total => (
  <span>
    共<span className="text-primary">{total}</span>条
  </span>
)

const defaultState = {
  searchParams: {
    pageNum: 1,
    pageSize: 10,
  },
  tempSearchParams: {},
  tableData: {
    list: [],
    total: 0,
  },
  tableDataLoaded: false,
  selectedItems: [],
  selectedItemKeys: [],
  operatingItems: {},
  loading: false,
}

const defaultSearchBarProps = {
  searchButtonText: lang('搜索'),
  resetButtonText: lang('重置'),
  expandButtonText: lang('展开'),
  collapseButtonText: lang('收起'),
}

export default class TableBasePage extends BasePage {
  constructor(props) {
    super(props)
    this.state = {
      ...defaultState,
      ...this.getDefaultState(),
      searchParams: {
        ...defaultState.searchParams,
        ...this.getDefaultSearchParams(),
      },
    }
    this.__searchBarRef = React.createRef()
  }

  businessChannel = null

  handleBroadcast = message => {
    if (message && message.data && message.data.channel === this.businessChannel) {
      if (message.data.type === 'FORM_PAGE_UPDATE') {
        this.isAlive && this.loadTableData()
      }
    }
  }

  getDefaultState() {
    return {}
  }

  getDefaultSearchParams() {
    return {}
  }

  /**
   * **用于设置复合参数的展开规则**
   * 例如：
   * 搜索栏中若使用了StoreCascader(门店选择器)组件，name是store，
   * 那么最终的搜索字段中会多出一个store属性，它是一个对象，包含包含region、corporation、store三个字段
   * 那么就可以在此定义一个展开函数，用于将这个store下面的三个属性展开到搜索字段中:
   * searchParamsExtractor = {
   *   store: store => {
   *     return {
   *       regionCode: store.region,
   *       corporationCode: store.corporation,
   *       storeCode: store.store,
   *     }
   *   },
   * }
   */
  searchParamsExtractor = {}

  /**
   * **用于指定moment类型参数的格式化规则**
   * 默认为'YYYY-MM-DD HH:mm:ss'
   * 指定为false，则转换成毫秒时间戳
   */
  formatMomentData = 'YYYY-MM-DD HH:mm:ss'

  tableRowKey = 'id'
  withSelectionRow = false
  tabFilterFieldName = 'type'
  pageSizeOptions = ['10', '25', '50', '100']

  getSearchBarProps(props) {
    return props
  }

  resetPageData = () => {
    return this.setState({
      ...defaultState,
      ...this.getDefaultState(),
    })
  }

  resetSearchParams = () => {
    this.setState({
      searchParams: {
        ...defaultState.searchParams,
        ...this.getDefaultSearchParams(),
        pageSize: this.state.searchParams.pageSize,
      },
    })
  }

  updateTempSearchParams = changedParams => {
    this.setState({
      tempSearchParams: {
        ...this.state.tempSearchParams,
        ...changedParams,
      },
    })
  }

  updateSearchParams(changedParams, loadData = true) {
    this.setState(
      {
        searchParams: {
          ...this.state.searchParams,
          ...changedParams,
        },
      },
      () => {
        loadData && this.loadTableData()
      }
    )
  }

  handleSearchParamsChange = data => {
    let changedParams = {}
    Object.keys(data).forEach(key => (changedParams[key] = data[key].value))
    this.updateTempSearchParams(changedParams)
  }

  handleSearchBarReset = () => {
    return this.resetSearchParams()
  }

  handleConfirmSearch = searchParams => {
    this.setState({ selectedItemKeys: [], selectedItems: [] })
    this.updateSearchParams({ ...searchParams, ...this.state.tempSearchParams, pageNum: 1 }, true)
  }

  handleTabFilterChange = tabFilterField => {
    this.setState({ tableData: defaultState.tableData })
    this.handleConfirmSearch({ [this.tabFilterFieldName]: tabFilterField })
  }

  handlePageChange = ({ current: pageNum, pageSize }) => {
    this.updateSearchParams({ pageNum, pageSize }, true)
  }

  handleTableDataLoadFailure = error => {
    console.warn(error)
  }

  handleTableDataExportFailure = error => {
    console.warn(error)
  }

  handleItemSelect = (record, selected) => {
    let nextSelectedItems = []
    const { selectedItems } = this.state

    if (selected) {
      nextSelectedItems = [...selectedItems, record].filter((item, index, array) => {
        return array.findIndex(subItem => subItem[this.tableRowKey] === item[this.tableRowKey]) === index
      })
    } else {
      nextSelectedItems = selectedItems.filter(item => item[this.tableRowKey] !== record[this.tableRowKey])
    }

    this.setState({
      selectedItems: nextSelectedItems,
      selectedItemKeys: nextSelectedItems.map(item => item[this.tableRowKey]),
    })
  }

  handleWholePageItemsSelect = (selected, records, changeRecords) => {
    let nextSelectedItems = []
    const { selectedItems } = this.state

    if (selected) {
      nextSelectedItems = [...selectedItems, ...changeRecords].filter((item, index, array) => {
        return array.findIndex(subItem => subItem[this.tableRowKey] === item[this.tableRowKey]) === index
      })
    } else {
      nextSelectedItems = selectedItems.filter(item => {
        return !changeRecords.find(subItem => subItem[this.tableRowKey] === item[this.tableRowKey])
      })
    }

    this.setState({
      selectedItems: nextSelectedItems,
      selectedItemKeys: nextSelectedItems.map(item => item[this.tableRowKey]),
    })
  }

  toggleOperating = (record, operating) => {
    const { operatingItems } = this.state
    this.setState({
      operatingItems: {
        ...operatingItems,
        [record[this.tableRowKey]]: operating,
      },
    })
  }

  tableDataLoader = async () => {
    return {
      list: [],
      total: 0,
    }
  }

  tableDataExporter = async () => {
    return true
  }

  resloveSearchParams = searchParams => {
    const resolvedSearchParams = {}

    Object.keys(searchParams).forEach(key => {
      if (!isEmptyValue(searchParams[key])) {
        if (moment.isMoment(searchParams[key])) {
          resolvedSearchParams[key] = this.formatMomentData ? searchParams[key].format(this.formatMomentData) : searchParams[key].valueOf()
        } else if (this.searchParamsExtractor[key]) {
          const extractedParams = this.searchParamsExtractor[key](searchParams[key])
          Object.keys(extractedParams).forEach(subKey => {
            !isEmptyValue(extractedParams[subKey]) && (resolvedSearchParams[subKey] = extractedParams[subKey])
          })
        } else if (typeof searchParams[key] === 'string') {
          resolvedSearchParams[key] = searchParams[key].trim()
        } else {
          resolvedSearchParams[key] = searchParams[key]
        }
      }
    })

    return resolvedSearchParams
  }

  loadTableData = async params => {
    try {
      const { searchParams, tableData } = this.state
      const mergedSearchParams = { ...searchParams, ...params }
      const resolvedSearchParams = this.resloveSearchParams(mergedSearchParams)

      this.setState({ loading: true })
      const nextTableData = await this.tableDataLoader(resolvedSearchParams, mergedSearchParams, tableData)
      if (nextTableData && nextTableData.list) {
        this.setState({ loading: false, tableDataLoaded: true, tableData: nextTableData })
      } else {
        throw {
          code: 500,
          message: '数据错误',
        }
      }

      return nextTableData
    } catch (error) {
      this.setState({ loading: false })
      this.handleTableDataLoadFailure(error)
    }
  }

  exportTableData = async () => {
    try {
      const { searchParams, tempSearchParams, tableData } = this.state
      const mergedSearchParams = { ...searchParams, ...tempSearchParams }
      const resolvedSearchParams = this.resloveSearchParams(mergedSearchParams)

      resolvedSearchParams.pageNum = 1
      resolvedSearchParams.pageSize = tableData.total

      this.setState({ exporting: true })
      await this.tableDataExporter(resolvedSearchParams)
      this.setState({ exporting: false })
    } catch (error) {
      this.setState({ exporting: false })
      this.handleTableDataExportFailure(error)
    }
  }

  getSearchFields = () => {
    return []
  }

  getTabelProps = props => {
    return props
  }

  getTableSelectable = () => {
    return false
  }

  getTableCheckboxProps = () => {
    return null
  }

  getTableColumns = () => {
    return []
  }

  getTabFilterItems = () => {
    return []
  }

  getTableRowSelection = () => {
    return {}
  }

  getPaginationProps = props => {
    const { searchParams, tableData } = this.state

    return {
      total: tableData.total,
      current: searchParams.pageNum,
      pageSize: searchParams.pageSize,
      showTotal: totalFormatter,
      pageSizeOptions: this.pageSizeOptions,
      showSizeChanger: true,
      showQuickJumper: true,
      ...props,
    }
  }

  renderSearchBar = props => {
    const searchBarProps = this.getSearchBarProps({ ...defaultSearchBarProps, ...props })
    return (
      <SearchBar
        className="table-page-search-bar"
        items={this.getSearchFields()}
        onChange={this.handleSearchParamsChange}
        onReset={this.handleSearchBarReset}
        onConfirmSearch={this.handleConfirmSearch}
        ref={this.__searchBarRef}
        {...searchBarProps}
      />
    )
  }

  renderTabFilter = props => {
    return (
      <TabFilter
        items={this.getTabFilterItems()}
        value={this.state.searchParams[this.tabFilterFieldName]}
        onChange={this.handleTabFilterChange}
        {...props}
      />
    )
  }

  renderDataTable = props => {
    const tableSelectable = this.getTableSelectable()
    const { tableDataLoaded, tableData, selectedItemKeys } = this.state

    const rowSelection =
      this.withSelectionRow || tableSelectable
        ? {
            hideDefaultSelections: true,
            selectedRowKeys: selectedItemKeys,
            getCheckboxProps: this.getTableCheckboxProps,
            onSelect: this.handleItemSelect,
            onSelectAll: this.handleWholePageItemsSelect,
            ...this.getTableRowSelection(),
          }
        : null

    return (
      <Table
        className={`table-page-table ${tableSelectable ? 'selectable' : 'non-selectable'}`}
        rowKey={this.tableRowKey}
        dataSource={tableData.list}
        columns={this.getTableColumns()}
        pagination={this.getPaginationProps()}
        onChange={this.handlePageChange}
        rowSelection={rowSelection}
        locale={{ emptyText: <TableEmptyText dataLoaded={tableDataLoaded} /> }}
        {...this.getTabelProps(props)}
      />
    )
  }

  componentDidMount() {
    super.componentDidMount()

    if (this.businessChannel) {
      this.subscribeBroadcast(this.handleBroadcast)
    }
  }

  componentWillUnmount() {
    super.componentWillUnmount()

    if (this.businessChannel) {
      this.unsubscribeBroadcast(this.handleBroadcast)
    }
  }
}
