import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'
import MinusCircleOutlined from '@ant-design/icons/lib/icons/MinusCircleOutlined';
import { Button, Drawer, Input, Select, Space, DatePicker, Switch, message, InputRef, Checkbox, Dropdown } from 'antd';
import { ColumnsFilterType, Condition, FilterValue, getColumnsFilterType, getOptions, getCondtion, dealInputValue } from './types';
import { guid } from '../../../utils/helper';
import locale from 'antd/lib/date-picker/locale/zh_CN';
import TextareaPop from '../../common/TextareaPop';
import { ClassNamesFn } from '../../../theme';
import { TranslateFn } from '../../../locale';
import './index.scss'
import { flatMap } from 'lodash';

interface IDrawerProps {
  visible: boolean
  showColumnsFilter: boolean
  columns: { type: string, label: string, name: string, groupName: string, map: object, format?: string }[]
  crudName?: string
  filterMap: Map<string, RFilterValue[]>
  getContainer: (() => HTMLElement) | undefined
  onCheckedChange: (checked: boolean, filterMap: Map<string, RFilterValue[]>, caseSensitive: boolean) => void
  onClose: () => void
  onConfirm: (filterMap: Map<string, RFilterValue[]>, caseSensitive: boolean) => void
  classPrefix: string
  classnames: ClassNamesFn
  translate: TranslateFn<any>
}

interface SecondFilterRef {
  setFilters: (filters: RFilterValue[]) => void
}

export const SecondFilterDrawer: React.FC<IDrawerProps> = (props) => {

  const { visible, showColumnsFilter, columns, filterMap, crudName = '', onClose, onConfirm, onCheckedChange, getContainer, classPrefix, classnames, translate } = props

  const filterColumns = flatMap(columns ?? [], column => {
    if (column.name && column.type != '__checkme' && column.type != 'operation' && column.type != '__pseudoColumn') {
      return { ...column, label: (column.groupName ? `${column.groupName} ·` : '') + column.label }
    }
    return []
  })

  const [caseSensitive, setCaseSensitive] = useState(true)

  const filtersRef = useRef<RFilterValue[]>([])

  const ref = useRef<SecondFilterRef>(null)

  useEffect(() => {
    if (visible && showColumnsFilter) {
      ref.current?.setFilters(flatMap(Array.from(filterMap.values()) ?? [], item => item))
    }
  }, [visible])

  const changeToMap = (filters: RFilterValue[]) => {
    const rValue = flatMap(filters ?? [], filter => {
      const { condition, value1, value2, relation, key, fieldName, ...rest } = filter
      const format = columns.find(column => column.name == fieldName)?.format ?? 'YYYY-MM-DD HH:mm:ss'
      if (condition != undefined) {
        if (condition == Condition.Null || condition == Condition.NotNull) {
          return { ...rest, fieldName, key, condition, value1: Array.isArray(value1) ? value1.join(',') : typeof value1 == 'object' ? value1.format(format) : value1, value2: typeof value2 == 'object' ? value2.format(format) : value2, relation }
        } else if (condition == Condition.Between) {
          if (value1 != undefined && value2 != undefined) {
            return { ...rest, fieldName, key, condition, value1: Array.isArray(value1) ? value1.join(',') : typeof value1 == 'object' ? value1.format(format) : value1, value2: typeof value2 == 'object' ? value2.format(format) : value2, relation }
          }
        } else {
          if (value1 != undefined) {
            return {
              ...rest,
              fieldName,
              key,
              condition,
              value1: Array.isArray(value1) ? value1.join(',') : typeof value1 == 'object' ? value1.format(format) : value1,
              value2: typeof value2 == 'object' ? value2.format(format) : value2,
              value3: dealInputValue(value1, filterColumns),
              relation
            }
          }
        }
      }
      return []
    })

    const filedNames = rValue.reduce<string[]>((pre, cur) => {
      if (!pre.includes(cur.fieldName) && cur.fieldName != '') {
        pre.push(cur.fieldName)
      }
      return pre
    }, [])
    const map = new Map<string, RFilterValue[]>()
    for (const fieldName of filedNames) {
      const values = rValue.filter(filter => filter.fieldName == fieldName)
      map.set(fieldName, values)
    }
    return map
  }

  const handleSave = () => {
    const filter = changeToMap(filtersRef.current)
    const value = JSON.stringify(Object.fromEntries(filter))
    if (crudName) {
      localStorage.setItem(crudName, value)
      message.success('保存成功')
    } else {
      message.error('保存失败')
    }
  }

  const handleReset = () => {
    const filterMap = new Map<string, any[]>()
    if (crudName) {
      const value = localStorage.getItem(crudName)
      if (value) {
        const obj = JSON.parse(value)
        Object.keys(obj).forEach(key => filterMap.set(key, obj[key]))
      }
      ref.current?.setFilters(flatMap(Array.from(filterMap.values()) ?? [], item => item))
    }
  }

  return (
    <Drawer
      visible={visible}
      onClose={onClose}
      keyboard={true}
      className={`second-fliter-drawer-${crudName} second-filter-drawer`}
      width={800}
      title='数据过滤'
      destroyOnClose={true}
      style={{ left: 'auto' }}
      footerStyle={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}
      getContainer={getContainer}
      footer={(
        <>
          <Checkbox checked={caseSensitive} onChange={e => setCaseSensitive(e.target.checked)}>区分大小写</Checkbox>
          <Space>
            <Button style={{ borderRadius: 4 }} onClick={() => handleSave()}>保存默认</Button>
            <Button disabled={!showColumnsFilter} style={{ borderRadius: 4 }} onClick={() => handleReset()}>重置</Button>
            <Button style={{ borderRadius: 4 }} type='primary' onClick={() => onConfirm(changeToMap(filtersRef.current), caseSensitive)}>过滤</Button>
          </Space>
        </>
      )}
    >
      <SecondFilterWithRef
        showColumnsFilter={showColumnsFilter}
        filterColumns={filterColumns}
        crudName={crudName}
        filterMap={filterMap}
        onEnter={() => onConfirm(changeToMap(filtersRef.current), caseSensitive)}
        onChange={checked => onCheckedChange(checked, changeToMap(filtersRef.current), caseSensitive)}
        onFilterChange={filters => filtersRef.current = filters}
        ref={ref}
        classPrefix={classPrefix}
        classnames={classnames}
        translate={translate}
      />
    </Drawer>
  )
}


type RFilterValue = FilterValue & { label: string, fieldName: string, filterType: ColumnsFilterType, value3?: string }

interface IProps {
  showColumnsFilter: boolean
  filterColumns: { type: string, label: string, name: string, map: object, format?: string }[]
  filterMap: Map<string, RFilterValue[]>
  crudName: string
  onEnter: () => void
  onChange: (checked: boolean) => void
  onFilterChange: (filter: RFilterValue[]) => void
  classPrefix: string
  classnames: ClassNamesFn
  translate: TranslateFn<any>
}

const SecondFilter: React.ForwardRefRenderFunction<SecondFilterRef, IProps> = (props, ref) => {

  const { showColumnsFilter, filterMap, crudName, filterColumns, onChange, onEnter, onFilterChange, classPrefix, classnames, translate } = props

  const [filters, setFilters] = useState<RFilterValue[]>(flatMap(Array.from(filterMap.values()) ?? [], item => item))

  const inputRef = useRef<InputRef>(null)

  useImperativeHandle(ref, () => ({ setFilters }))

  useEffect(() => {
    document.getElementsByClassName(`second-fliter-drawer-${crudName}`)[0]?.addEventListener('keydown', handleKeyDown)
    return () => {
      document.getElementsByClassName(`second-fliter-drawer-${crudName}`)[0]?.removeEventListener('keydown', handleKeyDown)
    }
  }, [])

  useEffect(() => {
    onFilterChange(filters)
  }, [filters])

  const handleKeyDown = (e: KeyboardEvent) => {
    if (e.key == 'Enter' && showColumnsFilter) {
      onEnter()
    }
  }

  const handleAdd = () => {
    const type = filterColumns[0]?.type
    const filterType = getColumnsFilterType(type ?? '') ?? 'text'

    const filter: RFilterValue = {
      key: guid(),
      condition: getCondtion(filterType),
      filterType: filterType,
      fieldName: filterColumns[0]?.name ?? '',
      label: filterColumns[0]?.label ?? '',
      relation: 'and'
    }
    setFilters(filters => filters.concat(filter))
  }

  const handleChange = (value: Partial<RFilterValue>) => {
    setFilters(filters => filters.map(filter => {
      if (filter.key === value.key) {
        // 修改的是字段名,重置字段类型和值
        if (value.hasOwnProperty('fieldName')) {
          const type = filterColumns.find(column => column.name == value.fieldName)?.type
          const _filterType = getColumnsFilterType(type ?? '') ?? 'text'
          const condition = getCondtion(_filterType)
          return { ...filter, ...value, filterType: _filterType, condition, value1: undefined, value2: undefined }
        }
        return { ...filter, ...value }
      }
      return filter
    }))
  }

  return (
    <div onContextMenu={e => e.preventDefault()} style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
      <span style={{ fontWeight: 'bold' }}>过滤状态</span>
      <Switch style={{ marginTop: 4 }} checkedChildren='开' unCheckedChildren='关' defaultChecked={showColumnsFilter} onChange={onChange}></Switch>
      <span style={{ marginTop: 24, fontWeight: 'bold' }}>过滤条件规则</span>
      <div>
        {filters.map(filter => {
          const { key, fieldName, filterType, condition, value1, value2, relation } = filter
          const map = filterColumns.find(column => column.name == fieldName)?.map ?? {}
          const format = filterColumns.find(column => column.name == fieldName)?.format ?? 'YYYY-MM-DD HH:mm:ss'
          const options = getOptions(filterType)
          return (
            <Space key={key} size={12} style={{ marginTop: 8 }}>
              <MinusCircleOutlined disabled={!showColumnsFilter} style={{ fontSize: 16, color: '#d9d9d9' }} onClick={() => setFilters(filters => filters.filter(filter => filter.key != key))} />
              <Button
                disabled={!showColumnsFilter}
                type='primary'
                size='small'
                style={{ border: 'none', borderRadius: 4, fontSize: 14, height: 25, backgroundColor: relation == 'or' ? '#5EA7F8' : '#FDA71E' }}
                onClick={() => handleChange({ key, relation: relation == 'and' ? 'or' : 'and' })}>
                {relation == 'or' ? '或' : '且'}
              </Button>
              <Select disabled={!showColumnsFilter} value={fieldName} style={{ width: 200 }} onChange={(value, opt: any) => handleChange({ key, fieldName: value, label: opt.children })}>
                {filterColumns.map(column => <Select.Option key={column.name} value={column.name}>{column.label}</Select.Option>)}
              </Select>
              <Select disabled={!showColumnsFilter} value={condition} style={{ width: 90 }} onChange={value => handleChange({ key, condition: value })} >
                {options.map(option => <Select.Option key={option.value} value={option.value}>{option.label}</Select.Option>)}
              </Select>
              {filterType == 'select' ?
                <Select
                  disabled={!showColumnsFilter}
                  style={{ width: 180, display: condition == Condition.Null || condition == Condition.NotNull ? 'none' : undefined }}
                  placeholder={translate('Select.placeholder')}
                  value={typeof value1 == 'string' ? value1.split(',').filter(v => v != '') : value1}
                  onChange={value1 => handleChange({ key, value1 })}
                  mode='multiple'
                  maxTagCount='responsive'
                >
                  {Object.keys(map).map(key => (
                    <Select.Option key={key} value={key}>{map[key]}</Select.Option>
                  ))}
                </Select>
                :
                <Dropdown
                  trigger={['contextMenu']}
                  overlayStyle={{ display: condition == Condition.Between ? 'none' : undefined }}
                  menu={{
                    items: filterColumns.map(column => ({
                      key: column.name,
                      label: column.label,
                      onClick: () => handleChange({ key, value1: `${value1 ?? ''}[${column.label}]` })
                    }))
                  }}
                  dropdownRender={menu => (
                    <div style={{
                      backgroundColor: '#fff',
                      boxShadow: 'rgba(0, 0, 0, 0.08) 0px 6px 16px 0px, rgba(0, 0, 0, 0.12) 0px 3px 6px -4px, rgba(0, 0, 0, 0.05) 0px 9px 28px 8px',
                      maxHeight: 300, overflow: 'auto',
                    }}>
                      {menu}
                    </div>
                  )}
                >
                  <Input
                    ref={inputRef}
                    autoFocus={true}
                    disabled={!showColumnsFilter}
                    className="filter-options-input"
                    type={'text'}
                    style={{ width: 180, display: condition == Condition.Null || condition == Condition.NotNull ? 'none' : undefined }}
                    onDoubleClick={() => inputRef.current?.select()}
                    onChange={e => handleChange({ key, value1: e.target.value })}
                    value={typeof value1 == 'object' ? Array.isArray(value1) ? value1.join(',') : value1.format(format) : value1}
                    addonAfter={filterType == 'date' ? (
                      //@ts-ignore
                      <DatePicker
                        className='filter-options-date'
                        disabled={!showColumnsFilter}
                        locale={locale}
                        format={format}
                        showTime={true}
                        dropdownAlign={{ offset: [-155, 2] }}
                        allowClear={false}
                        onChange={date => date && handleChange({ key, value1: date })}
                        style={{ height: 30 }}
                      />
                    ) : (
                      <TextareaPop
                        visible={false}
                        values={value1 as string | undefined}
                        classPrefix={classPrefix}
                        classnames={classnames}
                        translate={translate}
                        onReset={() => handleChange({ key, value1: undefined })}
                        handleText={(val: string) => handleChange({ key, value1: val })}
                        icon='tablet'
                        placement='right'
                      />
                    )}
                    onPaste={e => {
                      e.preventDefault()
                      const input = e.target as HTMLInputElement
                      const start = input.selectionStart ?? 0
                      const end = input.selectionEnd ?? 0
                      const copy = e.clipboardData.getData('text/plain').trim().replaceAll('\r', '').replaceAll('\n', ',')
                      const prefix = input.value.slice(0, start)
                      const suffix = input.value.slice(end - start > 0 ? end : start)
                      const value = prefix + copy + suffix
                      handleChange({ key, value1: value })
                    }}
                  />
                </Dropdown>
              }
              {condition == Condition.Between && (
                <Input
                  disabled={!showColumnsFilter}
                  className='filter-options-input'
                  type={'text'}
                  style={{ width: 180 }}
                  addonAfter={filterType == 'date' && (
                    //@ts-ignore
                    <DatePicker
                      className='filter-options-date'
                      disabled={!showColumnsFilter}
                      locale={locale}
                      format={format}
                      showTime={true}
                      allowClear={false}
                      onChange={date => date && handleChange({ key, value2: date })}
                      style={{ height: 30 }}
                    />
                  )}
                  onChange={e => handleChange({ key, value2: e.target.value })}
                  value={typeof value2 == 'object' ? Array.isArray(value2) ? value2.join(',') : value2.format(format) : value2}
                />
              )}
            </Space>
          )
        })}
      </div>
      <Button type='link' disabled={!showColumnsFilter} onClick={() => handleAdd()} style={{ marginTop: 10, borderRadius: 4 }}>{translate('Condition.add_cond')}</Button>
    </div>
  )
}

const SecondFilterWithRef = forwardRef(SecondFilter)