import { memo, useEffect, useState, ReactNode } from 'react'
import { InputNumber, Select, Table as RawTable, Tag, SelectProps, TableProps } from 'antd'
import { DefaultOptionType, SelectValue } from 'antd/lib/select'
import { Rule } from 'rc-field-form/lib/interface'

import { tryAddKeys } from './EditableTable'
import { makeNumericSorter, makeStringSorter } from './sorters'

export { makeDateSorter, makeNumericSorter, makeStringSorter } from './sorters'
export { EditableTable, makeActionHandler } from './EditableTable'
export { DatePickerWrapper } from './DatePickerWrapper'

export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/

export const defaultPagination = {
  defaultPageSize: 14,
  showSizeChanger: true,
}

export const makeNumericRender = (fixed?: number) => (value: any, _: object): ReactNode => {
  let val = '-'
  if ((value ?? null) !== null && Number.isFinite(+value)) {
    val = (fixed ?? null) !== null
      ? (+value).toFixed(fixed)
      : (+value).toPrecision(5)
  }

  return (
    <div className={'text-align-r-container'}>
      <span>{val}</span>
    </div>
  )
}

export const makeNumericColumnOptions = (fixed?: number, sorterKey?: string) => ({
  editable: true,
  initialValue: 0,
  width: 100,
  sorter: sorterKey ? makeNumericSorter(sorterKey) : null,
  formItemRules: [
    {
      required: true,
      message: 'Введите число',
      pattern: RegExpIsFloat,
    },
  ],
  render: makeNumericRender(fixed),
})

/*
other - объект с дополнительными свойствами колонки
поддерживаются все базовые свойства из описания https://ant.design/components/table/#Column
плю дополнительные для колонок EditableTable: */
interface columnPropsOther {
  // редактируемая колонка
  editable?: boolean
  // react компонента редактора
  input?: ReactNode
  // значение может быть пустым
  isRequired?: boolean
  // css класс для <FormItem/>, если требуется
  formItemClass?: string
  // массив правил валидации значений https://ant.design/components/form/#Rule
  formItemRules?: Rule[]
  // дефолтное значение при добавлении новой строки
  initialValue?: string | number

  render?: (...attributes: any) => any
}

export const makeColumn = (title: string | ReactNode, key: string, other?: columnPropsOther) => ({
  title: title,
  key: key,
  dataIndex: key,
  ...other,
})

export const makeColumnsPlanFact = (
  title: string | ReactNode,
  key: string | string[],
  columsOther?: any | any[],
  gruopOther?: any
) => {
  let keyPlanLocal: string
  let keyFactLocal: string

  if (key instanceof Array) {
    keyPlanLocal = key[0]
    keyFactLocal =  key[1]
  } else {
    keyPlanLocal = key + 'Plan'
    keyFactLocal =  key + 'Fact'
  }

  let columsOtherLocal : any[2]
  if (columsOther instanceof Array)
    columsOtherLocal = [columsOther[0], columsOther[1]]
  else
    columsOtherLocal = [columsOther, columsOther]

  return {
    title: title,
    ...gruopOther,
    children: [
      makeColumn('план', keyPlanLocal, columsOtherLocal[0]),
      makeColumn('факт', keyFactLocal, columsOtherLocal[1]),
    ]
  }
}

export const makeFilterTextMatch = (key: string | number) => (
  (filterValue: string | number, dataItem: any) => dataItem[key] === filterValue
)

export const makeGroupColumn = (title: string, children: object[]) => ({ title, children })

export const makeTextColumn = (
  title: string,
  dataIndex: string,
  filters: object[],
  sorter?: (key: string) => any,
  render?: any,
  other?: any
) => ({
  title: title,
  dataIndex: dataIndex,
  key: dataIndex,
  filters: filters,
  onFilter: filters ? makeFilterTextMatch(dataIndex) : null,
  sorter: sorter ?? makeStringSorter(dataIndex),
  render: render,
  ...other
})

export const makeNumericColumn = (
  title: string,
  dataIndex: string,
  filters: object[],
  filterDelegate: (key: string | number) => any,
  renderDelegate: (_: any, row: object) => any,
  width: string,
  other?: columnPropsOther
) => ({
  title: title,
  dataIndex: dataIndex,
  key: dataIndex,
  filters: filters,
  onFilter: filterDelegate ? filterDelegate(dataIndex) : null,
  sorter: makeNumericSorter(dataIndex),
  width: width,
  input: <InputNumber style={{ width: '100%' }}/>,
  render: renderDelegate ?? makeNumericRender(),
  align: 'right',
  ...other
})

export const makeNumericColumnPlanFact = (
  title: string,
  dataIndex: string,
  filters: object[],
  filterDelegate: (key: string | number) => any,
  renderDelegate: (_: any, row: object) => any,
  width: string
) => makeGroupColumn(title, [
  makeNumericColumn('п', dataIndex + 'Plan', filters, filterDelegate, renderDelegate, width),
  makeNumericColumn('ф', dataIndex + 'Fact', filters, filterDelegate, renderDelegate, width),
])

export const makeNumericStartEnd = (
  title: string,
  dataIndex: string,
  fixed: number,
  filters: object[],
  filterDelegate: (key: string | number) => any,
  renderDelegate: (_: any, row: object) => any,
  width: string,
) => makeGroupColumn(title, [
    makeNumericColumn('старт', dataIndex + 'Start', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Start')),
    makeNumericColumn('конец', dataIndex + 'End', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'End'))
])

export const makeNumericMinMax = (
  title: string,
  dataIndex: string,
  fixed: number,
  filters: object[],
  filterDelegate: (key: string | number) => any,
  renderDelegate: (_: any, row: object) => any,
  width: string,
) => makeGroupColumn(title, [
  makeNumericColumn('мин', dataIndex + 'Min', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Min')),
  makeNumericColumn('макс', dataIndex + 'Max', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Max')),
])

export const makeNumericAvgRange = (
  title: string,
  dataIndex: string,
  fixed: number,
  filters: object[],
  filterDelegate: (key: string | number) => any,
  renderDelegate: (_: any, row: object) => any,
  width: string
) => makeGroupColumn(title, [
  makeNumericColumn('мин', dataIndex + 'Min', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Min')),
  makeNumericColumn('сред', dataIndex + 'Avg', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Avg')),
  makeNumericColumn('макс', dataIndex + 'Max', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Max'))
])

export const makeSelectColumn = <T extends unknown = string>(
  title: string,
  dataIndex: string,
  options: DefaultOptionType[],
  defaultValue?: T,
  other?: columnPropsOther,
  selectOther?: SelectProps<SelectValue>
) => makeColumn(title, dataIndex, {
  ...other,
  input: <Select options={options} {...selectOther}/>,
  render: (value) => {
    const item = options?.find(option => option?.value === value)
    return other?.render?.(item?.value) ?? item?.label ?? defaultValue ?? value ?? '--'
  }
})

const makeTagInput = <T extends Record<string, any>>(value_key: string, label_key: string) => memo<{
  options: T[],
  value?: T[],
  onChange?: (values: T[]) => void,
  other?: SelectProps<SelectValue>,
}>(({ options, value, onChange, other }) => {
  const [selectOptions, setSelectOptions] = useState<DefaultOptionType[]>([])
  const [selectedValue, setSelectedValue] = useState<SelectValue>([])

  useEffect(() => {
    setSelectOptions(options.map((elm) => ({
      value: String(elm[value_key]),
      label: elm[label_key],
    })))
  }, [options])

  useEffect(() => {
    setSelectedValue(value?.map((elm) => String(elm[value_key])) ?? [])
  }, [value])

  const onSelectChange = (rawValues?: SelectValue) => {
    let values: any[] = []
    if (typeof rawValues === 'string')
      values = rawValues.split(',')
    else if (Array.isArray(rawValues))
      values = rawValues

    const objectValues: T[] = values.reduce((out: T[], value: string) => {
      const res = options.find((option) => String(option[value_key]) === String(value))
      if (res) out.push(res)
      return out
    }, [])

    onChange?.(objectValues)
  }

  return (
    <Select
      {...other}
      mode={'tags'}
      options={selectOptions}
      value={selectedValue}
      onChange={onSelectChange}
    />
  )
})

export const makeTagColumn = <T extends Record<string, any>>(
  title: string,
  dataIndex: string,
  options: T[],
  value_key: string,
  label_key: string,
  other?: columnPropsOther,
  tagOther?: SelectProps<SelectValue>
) => {
  const InputComponent = makeTagInput<T>(value_key, label_key)

  return makeColumn(title, dataIndex, {
    ...other,
    render: (item?: T[]) => item?.map((elm: T) => <Tag key={elm[label_key]} color={'blue'}>{other?.render?.(elm) ?? elm[label_key]}</Tag>) ?? '-',
    input: <InputComponent options={options} other={tagOther} />,
  })
}

type PaginationContainer = {
  skip?: number
  take?: number
  count?: number
  items?: any[] | null
}

export const makePaginationObject = (сontainer: PaginationContainer, ...other: any) => ({
  ...other,
  pageSize: сontainer.take,
  total: сontainer.count ?? сontainer.items?.length ?? 0,
  current: 1 + Math.floor((сontainer.skip ?? 0) / (сontainer.take ?? 1))
})

export type TableContainer = TableProps<any> & {
  dataSource: any[]
  children?: ReactNode
}

export const Table = memo<TableContainer>(({dataSource, children, ...other}) => (
  <RawTable dataSource={tryAddKeys(dataSource)} {...other}>{children}</RawTable>
))
