import { defineComponent, inject, watch, createVNode, ref, createTextVNode } from 'vue';
import { isNil } from 'lodash-es';
import Button from '../button';
import { SearchOutlined, RightOutlined, LeftOutlined } from '../icon';
import Empty from '../empty';
import Checkbox from '../checkbox';
import Input from '../input';
import VirtualList from '../virtual-list';
import { COMPONENT_NAME, TRANSFER_INJECT_KEY, COMPONENT_CLASS, COMPONENT_TWO_WAY_CLASS } from './const';
import { cls } from './utils';
import { TransferCheckbox, calcCheckStatus } from './checkbox';
import { useCheckValueWithCheckbox } from './useCheckValueWithCheckbox';
import { useOptionsFilter } from './useOptionsFilter';

const usePanelData = _ref => {
  let {
    rootProps,
    type,
    filter,
    handleChange
  } = _ref;
  const checkValue = ref([]);
  const options = ref([]);
  const {
    checkboxStatus,
    handleCheckboxChange,
    handleCheck
  } = useCheckValueWithCheckbox({
    checkValue,
    options,
    onCheckboxChange: () => {
      if (type === 'source') {
        return;
      }
      handleChange({
        nextValue: checkValue.value
      });
    }
  });
  const handleListOptionChange = (optionValue, nextValue) => {
    const nextCheckValue = [...checkValue.value];
    if (nextValue) {
      // add
      nextCheckValue.push(optionValue);
    } else {
      // remove
      const index = nextCheckValue.findIndex(v => v === optionValue);
      if (index === -1) {
        return;
      }
      nextCheckValue.splice(index, 1);
    }
    checkValue.value = nextCheckValue;
    handleCheck({
      checkedKeys: nextCheckValue
    });
  };
  const updateCheckboxStatus = () => {
    checkboxStatus.value = calcCheckStatus(checkValue.value.length, options.value.length);
  };
  const {
    filterText,
    displayOptions
  } = useOptionsFilter({
    options,
    filter,
    rootProps
  });
  return {
    type,
    checkValue,
    options,
    checkboxStatus,
    handleCheckboxChange,
    handleListOptionChange,
    filterText,
    displayOptions,
    updateCheckboxStatus
  };
};
const TwoWayTransfer = defineComponent({
  name: COMPONENT_NAME,
  setup: () => {
    const {
      modelValue,
      rootProps,
      rootStyle,
      filter,
      renderLabel,
      handleChange,
      scrollContentHeight
    } = inject(TRANSFER_INJECT_KEY);
    const sourcePanel = usePanelData({
      rootProps,
      handleChange,
      type: 'source',
      filter: filter.value
    });
    const targetPanel = usePanelData({
      rootProps,
      handleChange,
      type: 'target',
      filter: filter.value
    });

    // options 变化时，重置所有数据
    watch(() => rootProps.options, nextOptions => {
      sourcePanel.checkValue.value = [];
      targetPanel.checkValue.value = [];
      sourcePanel.options.value = nextOptions;
      targetPanel.options.value = [];
    });
    const renderOption = (option, panelData) => createVNode("div", {
      "class": cls('option')
    }, [createVNode(Checkbox, {
      "modelValue": panelData.checkValue.value.includes(option.value),
      "onChange": nextValue => {
        panelData.handleListOptionChange(option.value, nextValue);
      }
    }, null), createVNode("span", {
      "class": cls('option-label')
    }, [renderLabel(option)])]);
    const renderPanel = function (panelData) {
      let appendClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
      const isEmpty = panelData.options.value.length === 0;
      let content;
      if (isEmpty) {
        content = createVNode(Empty, {
          "class": cls('empty')
        }, null);
      } else if (!isNil(scrollContentHeight.value)) {
        content = createVNode(VirtualList, {
          "class": cls('panel-list'),
          "style": {
            height: `${scrollContentHeight.value}px`
          },
          "dataSources": panelData.displayOptions.value,
          "dataKey": option => option.value
        }, {
          default: _ref2 => {
            let {
              source: option
            } = _ref2;
            return renderOption(option, panelData);
          }
        });
      } else {
        content = createVNode("div", {
          "class": cls('panel-list')
        }, [panelData.displayOptions.value.map(option => renderOption(option, panelData))]);
      }
      return createVNode("div", {
        "class": [cls('panel'), isEmpty && cls('empty-panel'), ...appendClass]
      }, [createVNode("div", {
        "class": [cls('panel-header')]
      }, [createVNode(TransferCheckbox, {
        "modelValue": panelData.checkboxStatus.value,
        "onUpdate:modelValue": $event => panelData.checkboxStatus.value = $event,
        "label": '全选',
        "disabled": panelData.options.value.length === 0,
        "onChange": panelData.handleCheckboxChange
      }, null), createVNode("span", {
        "class": cls('panel-count')
      }, [panelData.checkValue.value.length, createTextVNode("\xA0/\xA0"), panelData.options.value.length, createTextVNode("\xA0\u9879")])]), rootProps.filterable && createVNode(Input, {
        "modelValue": panelData.filterText.value,
        "onUpdate:modelValue": $event => panelData.filterText.value = $event,
        "class": cls('panel-filter'),
        "placeholder": '请输入'
      }, {
        suffix: () => createVNode(SearchOutlined, null, null)
      }), content]);
    };

    // 仅更新 modelValue，panel 的数据更新由 watch 完成
    const handleTransfer = from => {
      let nextValue;
      if (from === 'source') {
        nextValue = [...targetPanel.options.value.map(_ref3 => {
          let {
            value
          } = _ref3;
          return value;
        }), ...sourcePanel.checkValue.value];
      } else {
        nextValue = targetPanel.options.value.map(_ref4 => {
          let {
            value
          } = _ref4;
          return value;
        }).filter(value => !targetPanel.checkValue.value.includes(value));
      }
      modelValue.value = nextValue;
      handleChange({
        nextValue
      });
    };

    // modelValue 变化时，更新 panel 的 options 和 checkValue
    watch(modelValue, nextValue => {
      sourcePanel.options.value = rootProps.options.filter(_ref5 => {
        let {
          value
        } = _ref5;
        return !nextValue.includes(value);
      });
      targetPanel.options.value = rootProps.options.filter(_ref6 => {
        let {
          value
        } = _ref6;
        return nextValue.includes(value);
      });
      sourcePanel.checkValue.value = sourcePanel.checkValue.value.filter(value => !nextValue.includes(value));
      targetPanel.checkValue.value = targetPanel.checkValue.value.filter(value => nextValue.includes(value));
      sourcePanel.updateCheckboxStatus();
      targetPanel.updateCheckboxStatus();
    }, {
      immediate: true,
      deep: true
    });
    const renderActions = () => {
      return createVNode("div", {
        "class": cls('actions')
      }, [createVNode(Button, {
        "class": cls('action-button'),
        "type": "primary",
        "disabled": sourcePanel.checkValue.value.length === 0,
        "onClick": () => handleTransfer('source')
      }, {
        icon: () => createVNode(RightOutlined, {
          "class": cls('action-button-icon')
        }, null)
      }), createVNode(Button, {
        "class": cls('action-button'),
        "type": "primary",
        "disabled": targetPanel.checkValue.value.length === 0,
        "onClick": () => handleTransfer('target')
      }, {
        icon: () => createVNode(LeftOutlined, {
          "class": cls('action-button-icon')
        }, null)
      })]);
    };
    return () => createVNode("div", {
      "class": [COMPONENT_CLASS, COMPONENT_TWO_WAY_CLASS],
      "style": rootStyle.value
    }, [renderPanel(sourcePanel, [cls('source-panel')]), renderActions(), renderPanel(targetPanel, [cls('target-panel')])]);
  }
});

export { TwoWayTransfer as default };
