import React, { useState, useCallback, useRef, useEffect } from 'react';
import { Input, Form, Typography, Button, Select, Row, Col, Popover, Checkbox, Tooltip, Table, Space } from 'antd';
import { DeleteOutlined, CloseOutlined, CheckOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper';
import { useClickAway } from 'ahooks';
import { useContextMenu, Menu as RcMenu, Item as RcItem } from "react-contexify";

import DebounceInput from './DebounceInput';
import { addEventListenerForSidebar, removeEventListenerForSidebar } from './Help';
import { showMessage, highlightSearchContentByTerms, inputWidth } from '../../../../util';

const { Option } = Select;

const type = 'DragableIndexBodyRow';
const MENU_ID = 'model-index-menu';

const InputDebounce = DebounceInput(300)(Input);

const AttributesInputItem = ({ indexedAttribute = null, indexedAttributeOrder = null, attributes, onAttributeChange, onOrderChange, onDelete , className }) => {

  return (
    <Row align='middle' className={className} >
      <Col span={4}>
        <span>字段名称:</span>
      </Col>
      <Col span={6}>
        <span onClick={e => e.stopPropagation()}>
          <Select 
            onChange={(value) => { onAttributeChange && onAttributeChange(value) }} 
            value={indexedAttribute ? (indexedAttribute.name||'') : ''} 
            placeholder='请选择字段名称'
          >
          {
            (attributes||[]).map((attribute, index) => {
              return (
                ((attribute.name||'')==='') ? null : <Option key={index} value={attribute.iid||''}>{attribute.name||''}</Option>
              );
            })
          }
          </Select>
        </span>
      </Col>
      <Col span={1}></Col>
      <Col span={2}>
        <span>排序:</span>
      </Col>
      <Col span={6}>
        <span onClick={e => e.stopPropagation()}>
          <Select 
            onChange={(value) => { onOrderChange && onOrderChange(value) }} 
            value={indexedAttributeOrder||''} 
            placeholder='请选择排序方式'
          >
            <Option value='DESC'>DESC</Option>
            <Option value='ASC'>ASC</Option>
          </Select>
        </span>
      </Col>
      <Col span={1}></Col>
      <Col span={1}>
        <Tooltip title="删除">
          <Button type="text" icon={<DeleteOutlined />} onClick={onDelete} />
        </Tooltip>
      </Col>
    </Row> 
  );
}

const AttributesInput = ({ value = {}, attributes, onChange }) => {
  
  const { indexedEasyDataModelAttributes, indexedAttributeOrders } = value;

  const onAttributeChange = (value, index) => {

    if (indexedEasyDataModelAttributes.findIndex(item => item.iid === value) !== -1) {
      showMessage('warn', '字段不能重复选择');
      return;
    }

    const newIndexedEasyDataModelAttributes = [...indexedEasyDataModelAttributes];

    const _index = attributes.findIndex(item => item.iid === value);
    newIndexedEasyDataModelAttributes.splice(index, 1, {...attributes[_index]});
    triggerChange({
      indexedEasyDataModelAttributes: newIndexedEasyDataModelAttributes
    });
  }

  const onOrderChange = (value, index) => {
    const newIndexedAttributeOrders = [...indexedAttributeOrders];
    newIndexedAttributeOrders.splice(index, 1, value);
    triggerChange({
      indexedAttributeOrders: newIndexedAttributeOrders
    })
  }

  const onItemDelete = (index) => {
    const newIndexedEasyDataModelAttributes = [...indexedEasyDataModelAttributes];
    const newIndexedAttributeOrders = [...indexedAttributeOrders];

    newIndexedEasyDataModelAttributes.splice(index, 1);
    newIndexedAttributeOrders.splice(index, 1);

    if (newIndexedEasyDataModelAttributes.length === 0) {
      newIndexedEasyDataModelAttributes.push({});
    }
    if (newIndexedAttributeOrders.length === 0) {
      newIndexedAttributeOrders.push('');
    }

    triggerChange({
      indexedEasyDataModelAttributes: newIndexedEasyDataModelAttributes,
      indexedAttributeOrders: newIndexedAttributeOrders,
    });
  }

  const addAttribute = () => {
    triggerChange({
      indexedEasyDataModelAttributes: [...indexedEasyDataModelAttributes, {}],
      indexedAttributeOrders: [...indexedAttributeOrders, ''],
    })
  }

  const triggerChange = (changedValue) => {
    onChange && onChange({
      ...value,
      ...changedValue,
    });
  };

  return (
    <React.Fragment>
      {
        (indexedEasyDataModelAttributes||[]).map((indexedAttribute, index) => {
          return (
            <AttributesInputItem 
              key={index}
              className='mb-2'
              indexedAttribute={indexedAttribute}
              indexedAttributeOrder={indexedAttributeOrders[index]||''}
              attributes={attributes}
              onAttributeChange={(value) => { onAttributeChange(value, index) } }
              onOrderChange={(value) => { onOrderChange(value, index) }}
              onDelete={(event) => { 
                event.stopPropagation();
                onItemDelete(index) 
              }}
            />
          );
        })
      }
      <Button onClick={addAttribute}>新增字段</Button>
    </React.Fragment>
  )
}

const EditableCell = ({
  editing,
  dataIndex,
  colTitle,
  inputType,
  record,
  index,
  attributes,
  children,
  ...restProps
}) => {

  let editingComponent = null;
  if (editing) {
    if (dataIndex !== 'attributesWithOrders') {

      const inputNode = inputType === 'check' ? <Checkbox /> : <Input />

      editingComponent = (
        <Form.Item
          name={dataIndex}
          style={{
            margin: 0,
          }}
          valuePropName={(inputType==='check')? 'checked': 'value'}
          rules={[
            {
              required: (inputType === 'text'),
              message: `请输入${colTitle}!`,
            },
          ]}
        >
        { inputNode }
        </Form.Item>
      );
    } else {
      editingComponent = (
        <Form.Item
          name={dataIndex}
          style={{
            margin: 0,
          }}
          valuePropName={'value'}
          rules={[
            {
              required: true,
              message: `请输入${colTitle}!`,
            },
          ]}
          >
          <AttributesInput attributes={attributes} />
        </Form.Item>
      )
    }
  }


  return (
    <td {...restProps}>
      {editing ? (
        editingComponent
      ) : (
        children
      )}
    </td>
  );
};

const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) => {

  const ref = useRef();
  const [{ isOver, dropClassName }, drop] = useDrop(
    () => ({
      accept: type,
      collect: monitor => {

        const { index: dragIndex } = monitor.getItem() || {};
        if (dragIndex === index) {
          return {};
        }
        return {
          isOver: monitor.isOver(),
          dropClassName: dragIndex < index ? ' drop-over-downward' : ' drop-over-upward',
        };
      },
      drop: item => {
        if (moveRow) {
          moveRow(item.index, index);
        }
      },
    }),
    [index],
  );
  const [, drag] = useDrag(
    () => ({
      type,
      item: () => {
        addEventListenerForSidebar("containerId");
        return { index }; 
      },
      end: (_, __) => {
        removeEventListenerForSidebar();
      }, 
      collect: monitor => ({
        isDragging: monitor.isDragging(),
      }),
    }),
    [index],
  );
  drop(drag(ref));

  return (
    <tr
      ref={ref}
      className={`${className}${isOver ? dropClassName : ''}`}
      style={{ cursor: 'move', ...style }}
      {...restProps}
    />
  );
};

const ImportActionIndex = (props) => {
  const { modelerData, onChange, editable, constraint, template, validateReports, terms } = props;

  const [ attributes, setAttributes ] = useState([]);
  const [ data, setData ] = useState([]);

  const [ form ] = Form.useForm();
  const [ editingKey, setEditingKey ] = useState(null);
  const [ keywordCondition, setKeywordCondition ] = useState({ keyword: '', needFilter: true });
  const { keyword, needFilter } = keywordCondition;

  const [ filterData, setFilterData ] = useState([]);
  const [ insertIndex, setInsertIndex ] = useState(0);
  const [ currentItem, setCurrentItem ] = useState(null);

  const dataRef = useRef();
  dataRef.current = data;

  const tableRef = useRef(null);

  const { show } = useContextMenu({
    id: MENU_ID,
  });

  useClickAway(() => {
    save();
  }, tableRef);
  
  //规则改变的时候 数据表为可编辑状态
  useEffect(() => {
    setEditingKey(null);
  }, [constraint, template, modelerData])

  useEffect(() => {

    setAttributes(modelerData.easyDataModelerDataModelAttributes||[]);
    setData(modelerData.easyDataModelerIndices||[]);
    dataRef.current = (modelerData.easyDataModelerIndices||[]);

    const _filterData = (modelerData.easyDataModelerIndices||[]).filter(item => (item.name||'').indexOf(keyword)!==-1);

    const __filterData = [];
    (_filterData||[]).forEach(item => {

      __filterData.push({...item, ...{ attributesWithOrders: {
        indexedEasyDataModelAttributes: item.indexedEasyDataModelAttributes||[],
        indexedAttributeOrders: item.indexedAttributeOrders||[],
      } }});
    })

    setFilterData(__filterData);
    //eslint-disable-next-line react-hooks/exhaustive-deps
  }, [modelerData])

  useEffect(() => {
    if (needFilter) {
      const _filterData = (modelerData.easyDataModelerIndices||[]).filter(item => (item.name||'').indexOf(keyword)!==-1);

      const __filterData = [];
      (_filterData||[]).forEach(item => {
  
        __filterData.push({...item, ...{ attributesWithOrders: {
          indexedEasyDataModelAttributes: item.indexedEasyDataModelAttributes||[],
          indexedAttributeOrders: item.indexedAttributeOrders||[],
        } }});
      })
  
      setFilterData(__filterData);
    }
    //eslint-disable-next-line react-hooks/exhaustive-deps
  }, [keywordCondition])
  
  const isEditing = (record) => record.name === editingKey;

  const onAddClick = (event) => {
    event.stopPropagation(); 

    save().then(result => {
      if (result) {

        setKeywordCondition({ keyword: '', needFilter: false });

        const newData = [...dataRef.current, {name: ''}];

        setFilterData(newData);    
        setInsertIndex(newData.length-1);
        edit(newData[newData.length-1], false);   
      }
    })
  }

  const insertToFront = (record) => {
    save().then(result => {

      if (result) {
        setKeywordCondition({ keyword: '', needFilter: false });

        let newData = [...dataRef.current];

        const index = newData.findIndex((item) => record.name === item.name);

        if (index === -1) {

          if (insertIndex === 0) {
            newData = [{name: ''}, ...newData];
  
            edit(newData[0], false);
          } else {
            newData.splice(insertIndex, 0, {name: ''});
            edit(newData[insertIndex], false);
          }

        } else if (index === 0) {
          newData = [{name: ''}, ...newData];

          setInsertIndex(0);
          edit(newData[0], false);
        } else {
          newData.splice(index, 0, {name: ''});
          setInsertIndex(index);
          edit(newData[index], false);
        }

        setFilterData(newData);
      }
    })
  }

  const insertToBack = (record) => {
    save().then(result => {
      if (result) {
        setKeywordCondition({ keyword: '', needFilter: false });

        const newData = [...dataRef.current];
        const index = newData.findIndex((item) => record.name === item.name);

        if (index === -1) {
          newData.splice(insertIndex+1, 0, {name: ''});
          setFilterData(newData);
          
          setInsertIndex(insertIndex+1);
          edit(newData[insertIndex+1], false);
        } else {
          newData.splice(index+1, 0, {name: ''});
          setFilterData(newData);
          
          setInsertIndex(index+1);
          edit(newData[index+1], false);
        }
      }
    })
  }

  const editLogic = (record) => {
    form.setFieldsValue({
      name: '',
      attributesWithOrders: {
        indexedEasyDataModelAttributes: [{}],
        indexedAttributeOrders: [''],
      },
      unique: false,
      ...record,
    });

    setEditingKey(record?.name);
  }

  const edit = (record, needSave = true) => {

    if (needSave) {
      save().then(result => {
        if (result) {
          editLogic(record);
        }
      })
    } else {
      editLogic(record);
    }
  };

  const removeLogic = (record) => {
    const newData = [...dataRef.current];
    const index = newData.findIndex((item) => record.name === item.name);
  
    if (index !== -1) {
      newData.splice(index, 1);
    }

    onChange && onChange(newData);
  }

  const remove = (record) => {

    if (record.name === '') {
      const newData = [...dataRef.current];
      onChange && onChange(newData);
    } else {
      if (record.name !== editingKey) {
        save().then(result => {
          if (result) {
            removeLogic(record);
          }
        })
      } else {
        removeLogic(record);
      }
    }

    save().then(result => {
      if (result) {
        const newData = [...dataRef.current];
        const index = newData.findIndex((item) => record.name === item.name);
    
        if (index !== -1) {
          newData.splice(index, 1);
        }
    
        onChange && onChange(newData);
      }
    })
  }

  const save = async() => {
    try {
      if (editingKey!==null) {
        const row = await form.validateFields();

        // console.log('row', row);  
        
        const newData = [...data];
        const index = newData.findIndex((item) => editingKey === item.name);
    
        //判断索引名称是否唯一
        let _index;
        if (index === -1) {
          _index = (data||[]).findIndex(item => item.name === row.name);
        } else {
          const newDataExcludeSelf = [...data];
          newDataExcludeSelf.splice(index, 1);  
          _index = (newDataExcludeSelf||[]).findIndex(item => item.name === row.name);
        }

        if (_index !== -1) {
          form.setFields([{ name: 'name', errors: ['索引名称不能重复'] }]);
          return;
        }

        const _indexedEasyDataModelAttributes = [], _indexedAttributeOrders = [];
        row.attributesWithOrders.indexedEasyDataModelAttributes.forEach((item, index) => {
          if ((item.iid||'')!=='') {
            _indexedEasyDataModelAttributes.push(item);
            _indexedAttributeOrders.push(row.attributesWithOrders.indexedAttributeOrders[index]);
          }
        })

        if (_indexedEasyDataModelAttributes.length === 0) {
          form.setFields([{ name: 'attributesWithOrders', errors: ['必须选择字段'] }]);
          return;
        }

        if (index === -1) {

          newData.splice(insertIndex, 0 , {
            name: row.name,
            unique: row.unique,
            indexedEasyDataModelAttributes: _indexedEasyDataModelAttributes,
            indexedAttributeOrders: _indexedAttributeOrders,
          });

        } else {
          newData.splice(index, 1, {...{
            name: row.name,
            unique: row.unique,
            indexedEasyDataModelAttributes: _indexedEasyDataModelAttributes,
            indexedAttributeOrders: _indexedAttributeOrders,
          }});
        }

        dataRef.current = newData;
    
        onChange && onChange(newData, true);

        setEditingKey(null);
      }

      return true;
      
    } catch (errInfo) {
      console.log('Validate Failed:', errInfo);
      return false;
    }
  };

  const onValuesChange = (changedValues, allValues) => {
    // console.log('changed values', changedValues);
    // console.log('all values', allValues);
  };

  const columns = [
    {
      title: '序号',
      dataIndex: 'key',
      editable: false,
      width: 60,
      render: (_, __, index) => {
        return (index+1).toString();
      }
    },
    {
      title: '索引名称',
      width: 200,
      dataIndex: 'name',
      editable: true,
      ellipsis: true,
      render: (text, record, index) => {
        return (
          <Tooltip title={text||''}>
          <span style={{ fontWeight: 'bold' }} >{highlightSearchContentByTerms(text, terms)}</span>
          </Tooltip>
        )
      }
    },
    {
      title: '是否唯一索引',
      width: 200,
      dataIndex: 'unique',
      editable: true,
      render: (unique, _, __) => {
        if (unique === false) {
          return ( 
            <CloseOutlined />
          );
        } else if (unique === true) {
          return (
            <CheckOutlined />
          )
        }

        return '';
      }
    },
    {
      title: '索引字段列表',
      dataIndex: 'attributesWithOrders',
      editable: true,
      ellipsis: true,
      render: (_, record, index) => {

        return (
          <div>
          {
            (record.indexedEasyDataModelAttributes||[]).map((item, index) => {

              const order = record.indexedAttributeOrders[index]||'';

              return (
                <Row key={index}>
                  <span>字段: </span>
                  { highlightSearchContentByTerms(item.name||'', terms) }
                  <span>{` 排序:  ${order||''}`}</span>
                </Row>
              )
            })
          }
          </div>
        );
      }
    },
  ];

  const editableColumn = [
    ...columns,
    {
      title: '操作',
      dataIndex: 'action',
      width: 180,
      render: (_, record) => {
        if (!editable) return null;

        return (
          <React.Fragment>
            <Button 
              className='mr-3' 
              size='small'
              type='text' 
              icon={<PlusOutlined />} 
              onClick={(event) => { 
                event.stopPropagation();
                insertToFront(record); 
              }}
            />

            <Button 
              className='mr-3' 
              size='small'
              type='text' 
              icon={<DeleteOutlined style={{ color: 'red' }} />} 
              onClick={(event) => { 
                event.stopPropagation();
                remove(record); 
              }}
            />
          </React.Fragment>
        )
      },
    },
  ]

  const validateColumn = {
    title: '规范',
    dataIndex: 'validate',
    width: 180,
    ellipsis: true,
    render: (text, record, index) => {
      let shortCauses = []; 
      let causes = [];
      (validateReports||[]).forEach((report) => {
        if (report.type==='Index' && report.iid === record.name) {
          (report.reportItems||[]).forEach((item) => {
            shortCauses.push(item.shortCause||'');
            causes.push(item.cause||'');
          })
        }
      });

      return (
        <div className='d-flex' key={index} style={{ alignItems: 'center', justifyContent: 'space-between' }}>
          <div>
          {
            shortCauses && shortCauses.map((shortCause, index) => {
              return (
                <Tooltip key={index} title={shortCause}>
                  <div className='textOverflow' style={{ width: 130, color: '#f5222d' }} title={shortCause} key={index}>
                    <span>{shortCause||''}</span>
                  </div>
                </Tooltip>
              );
            })
          }
          </div>
          {
            (causes||[]).length>0 && <Popover
              content={
                <div style={{ maxWidth: 200, maxHeight: 200, wordWrap: 'break-word', overflow: 'auto'  }}>
                {
                  causes && causes.map((cause, index) => {
                    return (
                      <Typography.Paragraph key={index} >
                      {cause||''}
                      </Typography.Paragraph>
                    )
                  })
                }
                </div>
              }
              title="规则详情"
            >     
              <a href="#">详情</a>     
            </Popover>
          }
        </div>
      )
    }
  };

  const includeValidateColumn = [
    ...columns,
    validateColumn
  ]

  const includeValidateEditableColumn = [
    ...editableColumn,
    validateColumn
  ]

  const mergedColumns = () => {
    const hasValidateReports = (validateReports||[]).filter(report => report.type==='Index').length>0;

    if (editable) {

      let _columns = hasValidateReports ? includeValidateEditableColumn: editableColumn;

      return _columns.map((col) => {
        if (!col.editable) {
          return col;
        }
    
        return {
          ...col,
          onCell: (record) => ({
            record,
            dataIndex: col.dataIndex,
            inputType:  (col.dataIndex==='unique') ? 'check' : 'text',
            colTitle: col.title,
            editing: isEditing(record),
            attributes,
          }),
        };
      });
    }

    return hasValidateReports ? includeValidateColumn : columns;
  }

  const moveRow = useCallback(
    (dragIndex, hoverIndex) => {

      const dragRow = dataRef.current[dragIndex];
      const newData = update(dataRef.current, {
        $splice: [
          [dragIndex, 1],
          [hoverIndex, 0, dragRow],
        ],
      })
      onChange && onChange(newData);
    },
    //eslint-disable-next-line react-hooks/exhaustive-deps
    [dataRef.current],
  );

  const onSearchInputChange = (value) => {
    setEditingKey(null);
    setKeywordCondition({ keyword: value||'', needFilter: true });
  }

  const displayMenu = (e) => {
    show(e);
  }

  const handleItemClick = ({ event, props, data, triggerEvent }) => {
    const key = event.currentTarget.id;

    if (key === 'up') {
      insertToFront(currentItem);
    } else if (key === 'down') {
      insertToBack(currentItem);
    }
  }

  return (
    <div className='model-import-action-index'>
      <div className='d-flex mb-3' style={{ justifyContent: 'space-between' }}>
        <Space>
          <h2 style={{ marginBottom: 0 }}>数据表索引</h2>
          { 
            editable && <Popover content='点击行进行编辑,表格可以通过拖拽来排序'>
              <QuestionCircleOutlined className='pointer' /> 
            </Popover> 
          }
        </Space>
        <Space>
          { 
            editable && <Tooltip>
              <Button onClick={onAddClick} >新建</Button>
            </Tooltip>
          }
          <div className='d-flex' style={{ alignItems: 'center' }}>
            <InputDebounce 
              placeholder="请输入索引名称" 
              allowClear 
              value={keyword}
              onChange={onSearchInputChange} 
              style={{ width: inputWidth }}
            />
          </div>
        </Space>
      </div>
      <div className='mb-3' id="containerId" ref={tableRef}>
        <DndProvider backend={HTML5Backend} >
          <Form form={form} component={false} onValuesChange={onValuesChange}>
            <Table
              components={{
                body: {
                  cell: EditableCell,
                  //编辑或者搜索状态下不允许拖动
                  row: (editable&&editingKey===null&&keyword==='')?DragableBodyRow:null,
                },
              }}
              onRow={(record, index) => {
                let rowParams = {
                  index,
                };

                if (editable) {
                  rowParams = {...rowParams, onContextMenu: event => {
                      setCurrentItem(record);
                      displayMenu(event);
                    }
                  };

                  if (!isEditing(record)) {
                    rowParams = {...rowParams, onClick: (event) => {
                        event.stopPropagation();
                        edit(record);
                      }
                    }

                    if (keyword.length===0) {
                      rowParams = {...rowParams, moveRow};
                    }
                  }
                }

                return rowParams;
              }}
              dataSource={filterData||[]}
              columns={mergedColumns()}
              size='small'
              rowKey='name'
              rowClassName="editable-row"
              pagination={false}
              sticky
              scroll={{
                x: 1200
              }}
            />
          </Form>
        </DndProvider>

        <RcMenu id={MENU_ID} >
          <RcItem id="up" onClick={handleItemClick}>
            在上方插入行
          </RcItem>
          <RcItem id="down" onClick={handleItemClick}>
            在下方插入行
          </RcItem>
        </RcMenu>
      </div>
    </div>
  );
};

export default ImportActionIndex;