import React from 'react';
import classnames from 'classnames';
import { CaretDownOutlined } from '@easyv/react-icons';
import { useDrag, useDrop } from 'react-dnd';
import { Input } from 'antd';

import { DraggableTypeEnum } from '@/constants';

export type TreeNodeDataSource<T = any> = T & {
  deep: number;
};

export interface TreeNodeProp {
  dataSource: TreeNodeDataSource;
  keyPropName: string;
  titlePropName: string;
  isGroupPropName: string;
  expandPropName: string;
  visible?: boolean;
  isSelected?: boolean;
  isEditing?: boolean;
  iconRender?: React.ReactNode;
  extendRender?: React.ReactNode;
  dragType: string;
  onClick?: (e: React.MouseEvent<HTMLElement>) => void;
  onRightClick?: (e: React.MouseEvent<HTMLElement>) => void;
  onExpandClick?: (e: React.MouseEvent<HTMLElement>) => void;
  onDragStart?: (e: React.DragEvent<HTMLElement>) => void;
  onDragEnd?: (e: React.DragEvent<HTMLElement>) => void;
  onDragHover?: (sourceKey: string, targetKey: string) => void;
  onDrop?: (sourceKey: string, targetKey: string) => void;
  onNameEditChange?: (name: string) => void;
}

const TreeNode: React.FC<TreeNodeProp> = function ({
  dataSource,
  keyPropName,
  titlePropName,
  isGroupPropName,
  expandPropName,
  visible = true,
  isSelected = false,
  isEditing = false,
  iconRender,
  extendRender,
  dragType,
  onClick,
  onRightClick,
  onExpandClick,
  onDragStart,
  onDragEnd,
  onDragHover,
  onDrop,
  onNameEditChange,
}) {
  const ref = React.useRef<HTMLDivElement>(null);

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const [_, drop] = useDrop(
    () => ({
      accept: DraggableTypeEnum.TREE,
      hover: (item: TreeNodeDataSource) => {
        if (!ref.current || !ref.current.dataset.key) return;
        onDragHover?.(item[keyPropName], ref.current.dataset.key);
      },
      drop: (item: TreeNodeDataSource) => {
        if (!ref.current || !ref.current.dataset.key) return;
        onDrop?.(item[keyPropName], ref.current.dataset.key);
      },
    }),
    [keyPropName],
  );

  const [{ isDragging }, drag] = useDrag(
    () => ({
      type: dragType,
      item: dataSource,
      options: {
        dropEffect: 'copy',
      },
      collect: (monitor) => ({
        isDragging: monitor.isDragging(),
      }),
    }),
    [dragType],
  );

  drag(drop(ref));

  const [editing, setEditing] = React.useState<boolean>(false);
  React.useEffect(() => {
    setEditing(isEditing);
  }, [isEditing]);

  const handelEditChange = (name: string) => {
    onNameEditChange?.(name);
    setEditing(false);
  };

  return (
    <div
      ref={ref}
      data-key={dataSource[keyPropName]}
      className={classnames('pr-2 h-6 mt-[1px] flex gap-x-1 items-center justify-center', {
        'bg-primary': isSelected || isDragging,
        'text-white': isSelected,
        'opacity-50': isDragging,
        hidden: !visible || (isDragging && DraggableTypeEnum.TREE === dragType),
      })}
      style={{
        paddingLeft: 8 + dataSource.deep * 16,
      }}
      onDragStart={onDragStart}
      onDragEnd={onDragEnd}
      onClick={onClick}
      onContextMenu={onRightClick}>
      <span className="w-4 flex items-center justify-center">
        {dataSource[isGroupPropName] ? (
          <span className="cursor-pointer" onClick={onExpandClick}>
            <CaretDownOutlined
              className={classnames(
                'anticon transform transition-all duration-300 text-xs',
                dataSource[expandPropName] ? '' : '-rotate-90',
              )}
            />
          </span>
        ) : null}
      </span>
      <span>{iconRender}</span>
      {editing ? (
        <Input
          autoFocus
          className="!h-full !flex-auto"
          defaultValue={dataSource[titlePropName]}
          onPressEnter={({ currentTarget: { value } }) => handelEditChange(value)}
          onBlur={({ currentTarget: { value } }) => handelEditChange(value)}
        />
      ) : (
        <span className="flex-auto cursor-pointer truncate" onDoubleClick={() => setEditing(true)}>
          {dataSource[titlePropName]}
        </span>
      )}
      <span>{extendRender}</span>
    </div>
  );
};

export default TreeNode;
