import { Form, Table, Button, Popconfirm } from 'antd'
import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons'
import { useState, useEffect } from 'react'

import { invokeWebApiWrapperAsync } from '@components/factory'

import { EditableCell } from './EditableCell'

const newRowKeyValue = 'newRow'

const actions = [
  [['insert'], (data) => [data]],
  [['insertRange'], (data) => [[data].flat(1)]],
  [['edit', 'update', 'put'], (data) => data.id && [data.id, data]],
  [['delete'], (data) => data.id && [data.id]],
]

export const makeActionHandler = (action, { idWell, service, setLoader, errorMsg, onComplete }, recordParser) => service && action && (
  (record) => invokeWebApiWrapperAsync(
    async () => {
      const addIdWell = (...params) => idWell ? [idWell, ...params] : params
      if (typeof recordParser === 'function')
        record = recordParser(record)

      const actionId = actions.findIndex((elm) => elm[0].includes(action))
      const params = actions[actionId]?.[1](record)

      if (params) await service[action](...addIdWell(...params))
      await onComplete?.()
    },
    setLoader,
    errorMsg
  )
)

export const tryAddKeys = (items) => {
  if (!items?.length || !items[0])
    return []
  if (items[0].key)
    return items
  return items.map((item, index) => ({...item, key: item.key ?? item.id ?? index }))
}

export const EditableTable = ({
  columns,
  dataSource,
  onChange, // Метод вызывается со всем dataSource с измененными элементами после любого действия
  onRowAdd, // Метод вызывается с новой добавленной записью. Если метод не определен, то кнопка добавления строки не показывается
  onRowEdit, // Метод вызывается с новой отредактированной записью. Если метод не поределен, то кнопка редактирования строки не показывается
  onRowDelete, // Метод вызывается с удаленной записью. Если метод не поределен, то кнопка удаления строки не показывается
  additionalButtons,
  buttonsWidth,
  ...otherTableProps
}) => {

  const [form] = Form.useForm()
  const [data, setData] = useState(tryAddKeys(dataSource))
  const [editingKey, setEditingKey] = useState('')

  useEffect(() => {
    setData(tryAddKeys(dataSource))
  }, [dataSource])

  const isEditing = (record) => record?.key === editingKey

  const edit = (record) => {
    form.setFieldsValue({...record})
    setEditingKey(record.key)
  }

  const cancel = () => {
    if (editingKey === newRowKeyValue) {
      const newData = [...data]
      const index = newData.findIndex((item) => newRowKeyValue === item.key)
      newData.splice(index, 1)
      setData(newData)
    }
    setEditingKey('')
  }

  const addNewRow = async () => {
    let newRow = {
      ...form.initialValues,
      key:newRowKeyValue
    }

    const newData = [newRow, ...data]
    setData(newData)
    edit(newRow)
  }

  const save = async (record) => {
    try {
      const row = await form.validateFields()
      const newData = [...data]
      const index = newData.findIndex((item) => record.key === item.key)
      const item = newData[index]
      const newItem = { ...item, ...row }

      newData.splice(index, 1, newItem)

      if (item.key === newRowKeyValue)
        item.key = newRowKeyValue + newData.length

      const isAdding = editingKey === newRowKeyValue
      setEditingKey('')
      setData(newData)

      if (isAdding)
        try {
          onRowAdd(newItem)
        } catch (err) {
          console.log('callback onRowAdd fault:', err)
        }
      else
        try {
          onRowEdit(newItem)
        } catch (err) {
          console.log('callback onRowEdit fault:', err)
        }

      try {
        if (onChange)
          onChange(newData)
      } catch (err) {
        console.log('callback onChange fault:', err)
      }

    } catch (errInfo) {
      console.log('Validate Failed:', errInfo)
    }
  }

  const deleteRow = (record) =>{
    const newData = [...data]
    const index = newData.findIndex((item) => record.key === item.key)

    newData.splice(index, 1)
    setData(newData)

    onRowDelete(record)

    if (onChange)
      onChange(newData)
  }

  const operationColumn = {
    width: buttonsWidth ?? 82,
    title: !!onRowAdd && (
      <Button
        onClick={addNewRow}
        disabled={editingKey !== ''}
        icon={<PlusOutlined/>}
      />
    ),
    dataIndex: 'operation',
    render: (_, record) => isEditing(record) ? (
      <span>
        <Button onClick={() => save(record)} icon={<SaveOutlined/>}/>
        <Button onClick={cancel} icon={<CloseCircleOutlined/>}/>
        {additionalButtons?.(record, editingKey)}
      </span>
    ) : (
      <span>
        {onRowEdit && (
          <Button
            disabled={editingKey !== ''}
            onClick={() => edit(record)}
            icon={<EditOutlined/>}
          />
        )}
        {onRowDelete && (
          <Popconfirm title={'Удалить?'} onConfirm={() => deleteRow(record)}>
            <Button icon={<DeleteOutlined/>}/>
          </Popconfirm>
        )}
        {additionalButtons?.(record, editingKey)}
      </span>
    ),
  }

  const handleColumn = (col) => {
    if (col.children)
      col.children = col.children.map(handleColumn)

    if (!col.editable)
      return col

    return {
      ...col,
      onCell: (record) => ({
        editing: isEditing(record),
        record,
        dataIndex: col.dataIndex ?? col.key,
        key: col.key ?? col.dataIndex,
        input: col.input,
        isRequired: col.isRequired,
        title: col.title,
        dataType: col.dataType,
        formItemClass: col.formItemClass,
        formItemRules: col.formItemRules,
        initialValue: col.initialValue,
      }),
    }
  }

  const mergedColumns = [...columns.map(handleColumn), operationColumn]

  return (
    <Form form={form}>
      <Table
        components={{
          body: {
            cell: EditableCell,
          },
        }}
        columns={mergedColumns}
        dataSource={data}
        {...otherTableProps}
      />
    </Form>
  )
}
