import React, { useState, useCallback, useRef, useEffect, useContext, useMemo } from 'react';
import { Input, Form, Typography, Button, Select, Row, Col, Popover, Checkbox, Tooltip, Table, Space } from 'antd';
import { DeleteOutlined, CloseOutlined, CheckOutlined, PlusOutlined, QuestionCircleOutlined, DownOutlined, UpOutlined } 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';
import { EditModelContext } from './ContextManage';
import { ValidateTip } from './ImportActionHeader';

const { Option } = Select;

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

const InputDebounce = DebounceInput(300)(Input);

const TypesItem = ({ value, types, onChange }) => {

  useEffect(() => {
    if ((value?.name||'')==='' && (types||[]).length > 0) {
      const filterTypes = (types||[]).filter(type => type.default===true);
      if ((filterTypes||[]).length > 0) {
        onChange && onChange(filterTypes[0]);
      }
    }
    //eslint-disable-next-line react-hooks/exhaustive-deps
  }, [value])

  const onTypeChange = (val) => {
    const filterTypes = (types||[]).filter(type => type.name===val);
    if ((filterTypes||[]).length > 0) {
      onChange && onChange(filterTypes[0]);
    }
  }

  return (
    <span onClick={e => e.stopPropagation()}>
      <Select 
        onChange={(val) => { onTypeChange && onTypeChange(val) }} 
        value={value?.name} 
        placeholder='请选择排序方式'
      >
      {
        (types||[]).map((type, index) => {
          return (
            <Option key={index} value={type.name}>{type.displayName}</Option>
          );
        })
      }
      </Select>
    </span>
  );
}

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>
  )
}

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

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

      let inputNode = <Input />
      if (inputType === 'check') {
        inputNode = <Checkbox />
      } else if (inputType === 'textarea') {
        inputNode = <Input.TextArea rows={3} />
      }

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

      editingComponent = (
        <Form.Item
          name={dataIndex}
          style={{
            margin: 0,
          }}
          valuePropName={'value'}
          rules={[
            {
              required: true,
              message: `请输入${colTitle}!`,
            },
          ]}
        >
          <TypesItem types={types} />
        </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>
  );
};

export 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, types } = 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 [isCollapse, setCollapse] = React.useState(true)

  const { indexIsEditingFunction } = useContext(EditModelContext);

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

  const tableRef = useRef(null);
  const termsRef = useRef(null);

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

  useClickAway(() => {
    save();
  }, tableRef);

  useEffect(() => {
    indexIsEditingFunction && indexIsEditingFunction(editingKey!==null);
    //eslint-disable-next-line react-hooks/exhaustive-deps    
  }, [ editingKey ])
  
  //规则改变的时候 数据表为可编辑状态
  useEffect(() => {
    setEditingKey(null);
  }, [constraint, template, modelerData])

  useEffect(() => {
    termsRef.current = terms;

    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, terms])

  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 menuData = useMemo(() => {
    return [
      { title: '在上方插入行', key: 'up' },
      { title: '在下方插入行', key: 'down' },
      { title: '删除', key: 'delete' },
    ]
  }, [])
  
  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 onInsertToFrontClick = (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 onInsertToBackClick = (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.resetFields();
    form.setFieldsValue({
      name: '',
      attributesWithOrders: {
        indexedEasyDataModelAttributes: [{}],
        indexedAttributeOrders: [''],
      },
      indextype: {},
      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 onRemoveClick = (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,
            indextype: row.indextype,
            indexedEasyDataModelAttributes: _indexedEasyDataModelAttributes,
            indexedAttributeOrders: _indexedAttributeOrders,
          });

        } else {
          newData.splice(index, 1, {...{
            name: row.name,
            unique: row.unique,
            indextype: row.indextype,
            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,
      require: true,
      render: (text, record, index) => {
        return (
          <React.Fragment>
            <ValidateTip type='Index' propertyName='name' validateReports={validateReports} iid={record.name} />
            <Tooltip title={text||''}>
            <span>{highlightSearchContentByTerms(text, termsRef.current)}</span>
            </Tooltip>
          </React.Fragment>
        )
      }
    },
    {
      title: '索引类型',
      width: 200,
      dataIndex: 'indextype',
      editable: true,
      ellipsis: true,
      render: (_, record, __) => {
        return (
          <Tooltip title={record.indextype?.displayName||''}>
          <span >{highlightSearchContentByTerms(record.indextype?.displayName||'', termsRef.current)}</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||'', termsRef.current) }
                  <span>{` 排序:  ${order||''}`}</span>
                </Row>
              )
            })
          }
          </div>
        );
      }
    },
  ];

  const mergedColumns = () => {
    if (editable) {
      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),
            require: col.require,
            attributes,
            types,
          }),
        };
      });
    }

    return 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, onChange],
  );

  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') {
      onInsertToFrontClick(currentItem);
    } else if (key === 'down') {
      onInsertToBackClick(currentItem);
    } else if (key === 'delete') {
      onRemoveClick(currentItem);
    }
  }

  return (
    <div className='model-import-action-index' id='model-import-action-index'>
      <div className='d-flex mb-3' style={{ justifyContent: 'space-between' }}>
        <Space>
          <h3 style={{ marginBottom: 0 }}>数据表索引</h3>
          { 
            editable && <Popover content='点击行进行编辑,表格可以通过拖拽来排序'>
              <QuestionCircleOutlined className='pointer' /> 
            </Popover> 
          }
          {
            isCollapse ? <Button type='primary' size='small' onClick={() => {
              setCollapse(!isCollapse)
            }}>展开<DownOutlined /></Button> : <Button type='primary' size='small' onClick={() => {
              setCollapse(!isCollapse)
            }}>收起<UpOutlined /></Button>
          }
        </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>
      {
        !isCollapse && <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>
        </div>
      }
      <RcMenu id={MENU_ID} >
      {
        (menuData??[]).map(item => (
          <RcItem key={item.key} id={item.key} onClick={handleItemClick}>
            {item.title}
          </RcItem>
        ))
      }
      </RcMenu>
    </div>
  );
};

export default ImportActionIndex;