<template>
  <div :class="ns.b()">
    <transfer-panel
      ref="leftPanel"
      :data="sourceData"
      :option-render="optionRender"
      :placeholder="panelFilterPlaceholder"
      :title="leftPanelTitle"
      :filterable="filterable"
      :format="format"
      :filter-method="filterMethod"
      :default-checked="leftDefaultChecked"
      :props="props"
      @checked-change="onSourceCheckedChange"
    >
      <slot name="left-footer"></slot>
    </transfer-panel>
    <div :class="ns.e('buttons')">
      <el-button
        type="primary"
        :class="[ns.e('button'), ns.is('with-texts', hasButtonTexts)]"
        :disabled="rightChecked.length === 0"
        @click="addToLeft"
      >
        <el-icon><arrow-left /></el-icon>
        <span v-if="buttonTexts[0] !== undefined">{{ buttonTexts[0] }}</span>
      </el-button>
      <el-button
        type="primary"
        :class="[ns.e('button'), ns.is('with-texts', hasButtonTexts)]"
        :disabled="leftChecked.length === 0"
        @click="addToRight"
      >
        <span v-if="buttonTexts[1] !== undefined">{{ buttonTexts[1] }}</span>
        <el-icon><arrow-right /></el-icon>
      </el-button>
    </div>
    <transfer-panel
      ref="rightPanel"
      :data="targetData"
      :option-render="optionRender"
      :placeholder="panelFilterPlaceholder"
      :filterable="filterable"
      :format="format"
      :filter-method="filterMethod"
      :title="rightPanelTitle"
      :default-checked="rightDefaultChecked"
      :props="props"
      @checked-change="onTargetCheckedChange"
    >
      <slot name="right-footer"></slot>
    </transfer-panel>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, inject, h, reactive, ref, toRefs, watch } from 'vue'
import ElButton from '@element-ultra/components/button'
import ElIcon from '@element-ultra/components/icon'
import { formItemKey } from '@element-ultra/tokens'
import { UPDATE_MODEL_EVENT } from '@element-ultra/shared'
import { useNamespace } from '@element-ultra/hooks'
import { ArrowLeft, ArrowRight } from 'icon-ultra'
import TransferPanel from './transfer-panel.vue'
import { useComputedData } from './useComputedData'
import {
  useCheckedChange,
  LEFT_CHECK_CHANGE_EVENT,
  RIGHT_CHECK_CHANGE_EVENT
} from './useCheckedChange'
import { useMove } from './useMove'
import { CHANGE_EVENT } from './transfer'

import type { PropType, VNode } from 'vue'
import type { FormItemContext } from '@element-ultra/tokens'
import type { DataItem, Format, Key, Props, TargetOrder } from './transfer'

type TransferType = InstanceType<typeof TransferPanel>

export default defineComponent({
  name: 'ElTransfer',

  components: {
    TransferPanel,
    ElButton,
    ElIcon,
    ArrowLeft,
    ArrowRight
  },

  props: {
    data: {
      type: Array as PropType<DataItem[]>,
      default: () => []
    },
    titles: {
      type: Array as PropType<any> as PropType<[string, string]>,
      default: () => []
    },
    buttonTexts: {
      type: Array as PropType<any> as PropType<[string, string]>,
      default: () => []
    },
    filterPlaceholder: {
      type: String,
      default: ''
    },
    filterMethod: Function as PropType<(query: string, item: DataItem) => boolean>,
    leftDefaultChecked: {
      type: Array as PropType<Key[]>,
      default: () => []
    },
    rightDefaultChecked: {
      type: Array as PropType<Key[]>,
      default: () => []
    },
    renderContent: Function as PropType<(h, option) => VNode>,
    modelValue: {
      type: Array as PropType<Key[]>,
      default: () => []
    },
    format: {
      type: Object as PropType<Format>,
      default: () => ({})
    },
    filterable: {
      type: Boolean,
      default: false
    },
    props: {
      type: Object as PropType<Props>,
      default: () => ({
        label: 'label',
        key: 'key',
        disabled: 'disabled'
      })
    },
    targetOrder: {
      type: String as PropType<TargetOrder>,
      default: 'original',
      validator: (val: string) => {
        return ['original', 'push', 'unshift'].includes(val)
      }
    }
  },

  emits: [UPDATE_MODEL_EVENT, CHANGE_EVENT, LEFT_CHECK_CHANGE_EVENT, RIGHT_CHECK_CHANGE_EVENT],

  setup(props, { emit, slots }) {
    const ns = useNamespace('transfer')
    const elFormItem = inject(formItemKey, {} as FormItemContext)

    const checkedState = reactive({
      leftChecked: [],
      rightChecked: []
    })

    const { propsKey, sourceData, targetData } = useComputedData(props)

    const { onSourceCheckedChange, onTargetCheckedChange } = useCheckedChange(checkedState, emit)

    const { addToLeft, addToRight } = useMove(props, checkedState, propsKey, emit)

    const leftPanel = ref<TransferType>()
    const rightPanel = ref<TransferType>()

    const clearQuery = (which: 'left' | 'right') => {
      switch (which) {
        case 'left':
          leftPanel.value!.query = ''
          break
        case 'right':
          rightPanel.value!.query = ''
          break
      }
    }

    const hasButtonTexts = computed(() => props.buttonTexts.length === 2)

    const leftPanelTitle = computed(() => props.titles[0] || '列表1')

    const rightPanelTitle = computed(() => props.titles[1] || '列表2')

    const panelFilterPlaceholder = computed(() => props.filterPlaceholder || '请输入搜索内容')

    watch(
      () => props.modelValue,
      () => {
        elFormItem?.validate()
      }
    )

    const optionRender = computed(() => option => {
      if (props.renderContent) return props.renderContent(h, option)

      if (slots.default) return slots.default({ option })

      return h('span', option[props.props.label] || option[props.props.key])
    })

    return {
      ns,
      sourceData,
      targetData,
      onSourceCheckedChange,
      onTargetCheckedChange,
      addToLeft,
      addToRight,

      ...toRefs(checkedState),

      hasButtonTexts,
      leftPanelTitle,
      rightPanelTitle,
      panelFilterPlaceholder,
      clearQuery,
      leftPanel,
      rightPanel,

      optionRender
    }
  }
})
</script>
