import { tokenize } from "amis-formula"
import { flatMap } from "lodash"
import uniqWith from "lodash/uniqWith"
import { IColumn } from "../../store/table"
import { getCellValue, CROSS_SPLIT_CHAR, handleSort } from "../../store/utils/commonTableFunction"
import { cartesianProduct } from "../../utils/utils"

export const EMPTY_GROUP_VALUE = '空白'

export const INDEX_NAME = 'SF_INDEX'

export const REG_EXP = new RegExp(`\\.|:|${CROSS_SPLIT_CHAR}`, 'g')

const worker = new Worker('./public/worker/dataCross.js')

interface ICrossField {
  name: string
  titleExpr?: string
  order?: 'ASC' | 'DESC'
}

export interface ICross {
  columnFields: ICrossField[]
  rowFields: Pick<ICrossField, 'name'>[]
  valueFields: string
  /** valueField位置 0:表头 1:表体 */
  positionType: 0 | 1,
}

interface IColumnType {
  type: string
  align: string
  sortable: boolean
  map?: object
  fixed?: string
  isNumerical?: boolean
  quickEdit?: any
}

type IColumnField = { name: string, order?: 'ASC' | 'DESC', orderName?: string, titleExpr?: string } & IColumn

export interface ICrossColumn extends IColumnType {
  label: string
  field: string
  name: string
  groupName: string
  groupLabels: string[]
  isCountField: boolean
}

const uniqArr = (fields: IColumnField[], datas: any[]) => {
  const map = new Map<string, { order: 'asc' | 'desc' }>(fields.map(field => [(field.orderName ?? field.name), { order: (field.order?.toLowerCase() ?? 'asc') as 'asc' | 'desc' }]))
  const sortData = handleSort(datas.slice(), fields.map(field => field.orderName ?? field.name), map)
  return uniqWith(sortData.map(data => {
    const obj: any = {}
    for (const field of fields) {
      obj[field.name] = data[field.name]
    }
    return obj
  }), (a, b) => {
    for (const field of fields) {
      if (a[field.name] !== b[field.name]) {
        return false
      }
    }
    return true
  })
}

export const buildCrossColumn = (rowFields: IColumnField[], colFields: IColumnField[], valueFields: IColumnField[], datas: any[], countSum: boolean) => {
  const ret: ICrossColumn[] = []
  const level = colFields.length + 1
  const uniqColArr = uniqArr(colFields, datas)
  for (const rowField of rowFields) {
    const { type, label, name, align, sortable, map, fixed, pristine } = rowField
    const groupName = new Array(level - 1).fill(name).join(',')
    const groupLabels = new Array(level - 1).fill(name)
    ret.push({
      ...pristine,
      type,
      label,
      name,
      field: name,
      groupName,
      groupLabels,
      map,
      fixed,
      align,
      sortable,
      isCountField: false
    })
  }
  const product: [any, IColumnField, string[]][] = cartesianProduct(uniqColArr, valueFields).map(([item, field]: [any, IColumnField]) => {
    const cols: string[] = []
    for (let i = 0; i < colFields.length; i++) {
      const field = colFields[i]
      if (i == 0) {
        cols.push(String(item[field.name] ?? EMPTY_GROUP_VALUE).replace(REG_EXP, ''))
      } else {
        const vals = []
        for (let j = 0; j <= i; j++) {
          const preField = colFields[j]
          vals.push(String(item[preField.name] ?? EMPTY_GROUP_VALUE).replace(REG_EXP, ''))
        }
        cols.push(vals.join(CROSS_SPLIT_CHAR))
      }
    }
    return [item, field, cols.concat(`${cols[cols.length - 1]}${CROSS_SPLIT_CHAR}${field.name}`)]
  })
  for (const [uniqCol, valueField, item] of product) {
    const { type, label, name, align, sortable } = valueField
    const { quickEdit, ...rest } = valueField.pristine
    const has$Expr = colFields.some(field => field.titleExpr && !/\$\{[^\}]*\}/g.test(field.titleExpr))
    const groupName = has$Expr ? colFields.map(field => field.titleExpr).join(CROSS_SPLIT_CHAR) : level == 1 ? '' : item.slice(0, level - 1).map(val => val.replaceAll(',', '')).reverse().join(',')
    const groupLabels = colFields.map(field => {
      const val = getCellValue(uniqCol[field.name], field) || EMPTY_GROUP_VALUE
      return field.titleExpr ? field.titleExpr.replaceAll(`$\{${field.name}\}`, val) : val
    }).reverse()
    ret.push({
      ...rest,
      type,
      label: /\$\{[^\}]*\}/g.test(label) ? (tokenize(label, uniqCol) || EMPTY_GROUP_VALUE) : label,
      name: item[level - 1],
      field: name,
      groupName,
      groupLabels,
      align,
      sortable,
      quickEdit: quickEdit ? { ...quickEdit, name: item[level - 1] } : undefined,
      isNumerical: true,
      isCountField: false
    })
  }
  if (countSum) {
    for (const valueField of valueFields) {
      const { type, label, name, align, sortable, pristine } = valueField
      const groupName = new Array(colFields.length).fill('SF_COUNT').join(',')
      const groupLabels = new Array(colFields.length).fill('合计')
      const rLabel = label.replace(REG_EXP, '')
      ret.push({
        ...pristine,
        type,
        label: rLabel,
        name: `${rLabel}${CROSS_SPLIT_CHAR}${groupName}`,
        field: name,
        groupName,
        groupLabels,
        align,
        sortable,
        isNumerical: false,
        isCountField: true
      })
    }
  }
  return ret
}

export const buildCrossData = (rowFields: IColumnField[], colFields: IColumnField[], crossColumns: ICrossColumn[], datas: any[], countSum: boolean) => {
  const uniqRowArr = uniqArr(rowFields, datas)
  return new Promise<any[]>((resolve, reject) => {
    worker.postMessage({ uniqRowArr, rowFields, colFields, crossColumns, datas, countSum, regExp: REG_EXP, EMPTY_GROUP_VALUE })
    worker.onmessage = (e: MessageEvent<any[]>) => { resolve(e.data) }
    worker.onmessageerror = (e) => { reject(e.data) }
  })
}

export const buildCrossColumn1 = (rowFields: IColumnField[], colFields: IColumnField[], datas: any[]) => {
  const ret: ICrossColumn[] = []
  const level = colFields.length
  // step 1
  for (const rowField of rowFields) {
    const { type, label, name, align, sortable, map, fixed, pristine } = rowField
    ret.push({
      ...pristine,
      type,
      label,
      name,
      field: name,
      groupName: new Array(level - 1).fill(name).join(','),
      groupLabels: [],
      map,
      fixed,
      align,
      sortable,
      isCountField: false
    })
  }
  // step 2
  ret.push({
    label: '指标',
    name: INDEX_NAME,
    field: INDEX_NAME,
    groupName: new Array(level - 1).fill(INDEX_NAME).join(','),
    groupLabels: [],
    isCountField: false,
    type: 'plain',
    align: 'left',
    sortable: false,
  })
  // step 3
  const uniqColArr = uniqArr(colFields, datas)
  for (const item of uniqColArr) {
    const cols: string[] = []
    for (let i = 0; i < colFields.length; i++) {
      const field = colFields[i]
      if (i == 0) {
        cols.push(String(item[field.name] ?? EMPTY_GROUP_VALUE).replace(REG_EXP, ''))
      } else {
        const vals = []
        for (let j = 0; j <= i; j++) {
          const preField = colFields[j]
          vals.push(String(item[preField.name] ?? EMPTY_GROUP_VALUE).replace(REG_EXP, ''))
        }
        cols.push(vals.join(CROSS_SPLIT_CHAR))
      }
    }
    const groupName = level == 1 ? '' : cols.slice(0, level - 1).reverse().join(',')
    const labels = colFields.map(field => {
      const val = getCellValue(item[field.name], field) || EMPTY_GROUP_VALUE
      return field.titleExpr ? field.titleExpr.replaceAll(`$\{${field.name}\}`, val) : val
    })
    ret.push({
      label: labels[level - 1],
      name: cols[level - 1],
      field: '',
      groupName,
      groupLabels: labels.slice(0, level - 1).reverse(),
      isCountField: false,
      type: 'plain',
      align: 'right',
      sortable: false
    })
  }
  return ret
}

export const buildCrossData1 = (rowFields: IColumnField[], colFields: IColumnField[], valueFields: IColumnField[], crossColumns: ICrossColumn[], datas: any[]) => {
  const uniqRowData = uniqArr(rowFields, datas)
  const tempColumns = crossColumns.slice(rowFields.length + 1)
  return flatMap(uniqRowData ?? [], rowData => {
    const ret: any[] = []
    const rowDatas = datas.filter(data => rowFields.every(rowField => data[rowField.name] == rowData[rowField.name])) 
    for (let i = 0; i < valueFields.length; i++) {
      const field = valueFields[i]
      const obj = { ...rowData }
      obj[INDEX_NAME] = field.label
      for (const tempCol of tempColumns) {
        const val: any | undefined = rowDatas.find(data => {
          return colFields.every((colField, index) => data[colField.name] == tempCol.name.split(CROSS_SPLIT_CHAR)[index])
        })
        obj[tempCol.name] = val ? val[field.name] ? getCellValue(val[field.name], field) : null : null
      }
      ret.push(obj)
    }
    return ret
  })
}

export const getChangeRows = (changeDatas: any[], rawDatas: any[], cross: ICross): any[] => {
  if (cross.positionType === 1) return []
  const { rowFields, columnFields } = cross
  return flatMap(changeDatas, changeData => {
    const colFields = Object.keys(changeData).filter(key => key.includes(CROSS_SPLIT_CHAR))
    const rowTarget = rawDatas.filter(data => rowFields.every(field => data[field.name] == changeData[field.name]))
    if (colFields.length > 0) {
      const groupNames = colFields[0].split(CROSS_SPLIT_CHAR)
      const colTarget = rowTarget.find(data => columnFields.every((field, index) => data[field.name] == groupNames[index]))
      if (colTarget) {
        const ret = { ...colTarget }
        colFields.forEach(field => {
          const values = field.split(CROSS_SPLIT_CHAR)
          const valueField = values[values.length - 1]
          ret[valueField] = changeData[field]
        })
        return ret
      }
    }
    return []
  })
}

// export const buildCrossData = (rowFields: IColumnField[], colFields: IColumnField[], crossColumns: ICrossColumn[], datas: any[], countSum: boolean) => {
//   const uniqRowArr = uniqArr(rowFields, datas)
//   return new Promise<any[]>((resolve, reject) => {
//     const ret = uniqRowArr.map(item => {
//       for (let i = rowFields.length; i < crossColumns.length; i++) {
//         const { name, field, isPercentage, calculatedField, isCountField } = crossColumns[i]
//         const isRate = isPercentage === true && (calculatedField?.length ?? 0) > 0
//         let val = 0, zSum = 0, mSum = 0
//         let rVal = 0, rzSum = 0, rmSum = 0
//         const rowDatas = datas.filter(data => rowFields.every(field => data[field.name] == item[field.name]))
//         for (const data of rowDatas) {
//           const conditon = colFields.every((field, index) => {
//             const value = name.split('·')[index]
//             const dValue = data[field.name]
//             return (dValue == null ? dValue : String(dValue).replace(REG_EXP, '')) == (value == EMPTY_GROUP_VALUE ? null : value)
//           })
//           if (conditon) {
//             if (isRate) {
//               zSum += (+(data[field] / 100) * +(data[calculatedField]))
//               mSum += +(data[calculatedField])
//             } else {
//               val += +(data[field])
//             }
//           }
//           if (countSum) {
//             if (isRate) {
//               rzSum += (+(data[field] / 100) * +(data[calculatedField]))
//               rmSum += +(data[calculatedField])
//             } else {
//               rVal += +(data[field])
//             }
//           }
//         }
//         if (isRate) {
//           if (isCountField) {
//             item[name] = (rzSum == 0 || rmSum == 0) ? null : (rzSum / rmSum * 100)
//           } else {
//             item[name] = (zSum == 0 || mSum == 0) ? null : (zSum / mSum * 100)
//           }
//         } else {
//           if (isCountField) {
//             item[name] = rVal == 0 ? null : rVal
//           } else {
//             item[name] = val == 0 ? null : val
//           }
//         }
//       }
//       return item
//     })
//     resolve(ret)
//   })
// }

