<script lang="ts" setup>
  import { Button, Tabs, Badge, ButtonProps, Tag } from 'ant-design-vue';
  import { get, isFunction, set, uniqBy } from 'lodash-es';
  import { ExtractPropTypes, computed, unref, ref, nextTick, onMounted, watch } from 'vue';
  import { DynamicProps } from '/#/utils';
  import { BasicModal, useModal } from '/@/components/Modal';
  import { BasicTable, BasicTableProps, TableRowSelection, useTable } from '/@/components/Table';
  import { FETCH_SETTING } from '/@/components/Table/src/const';
  import { useDesign } from '/@/hooks/web/useDesign';

  interface Options extends Recordable {
    primaryKey?: string;
    disabled?: boolean;
  }

  enum Active {
    PENDING = 'pending',
    SELECTED = 'selected',
  }

  interface ActionType {
    setSelectedOptions: (options: Recordable[]) => void;
  }

  const emit = defineEmits(['change', 'update:value']);

  const props = withDefaults(
    defineProps<{
      value?: string[];
      labelField?: string;
      valueField?: string;
      primaryKey?: string;
      params?: Recordable;
      buttonName?: string;
      pendingTableProps?: BasicTableProps;
      selectedTableProps?: BasicTableProps;
      maxTagLength?: number;
      modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>;
      buttonProps?: ExtractPropTypes<InstanceType<typeof Button>['$props']>;
      initSelectedOptions?: (actionType: ActionType) => Promise<Recordable[]>;
      transformValue?: (selectedRowKeys: string[], selectedRows: Options[]) => any[];
      onValueChange?: (selectedRowkeys: string[]) => any[];
      onRemoveAfter?: (actionType: ActionType) => Promise<any>;
      onSelectedAfter?: (actionType: ActionType) => Promise<any>;
    }>(),
    {
      buttonName: '选择设备',
      primaryKey: 'id',
      maxTagLength: 2,
      labelField: 'label',
      valueField: 'value',
    }
  );

  const { prefixCls } = useDesign('transfer-table-modal');

  const activeKey = ref<Active>(Active.PENDING);

  const selectedRows = ref<Options[]>([]);

  const selectedRowKeys = ref<string[]>(props.value || []);

  const pendingOptions = ref<Options[]>([]);

  const pendingConfirmQueue = ref<Options[]>([]);

  const selectedTotal = ref(0);

  const pendingTotal = ref(0);

  const getFetchSetting = computed(() => {
    const { pendingTableProps } = props;
    return pendingTableProps?.fetchSetting || FETCH_SETTING;
  });

  const getPendingRowSelection = computed<TableRowSelection>(() => {
    const rowKeys = unref(selectedRowKeys);
    return {
      type: 'checkbox',
      getCheckboxProps: (record: Recordable) => {
        const { primaryKey } = props;
        return {
          ...record,
          disabled: rowKeys.includes(record[primaryKey]),
        };
      },
      onSelect: (_record: Recordable, _selected: boolean, selectedRows: Object[]) => {
        pendingConfirmQueue.value = selectedRows;
      },
      onSelectAll: (_selected: boolean, selectedRows: Recordable[]) => {
        pendingConfirmQueue.value = selectedRows;
      },
    };
  });

  const getPendingTableBindProps = computed<Partial<DynamicProps<BasicTableProps>>>(() => {
    const { pendingTableProps, primaryKey } = props;
    return {
      ...pendingTableProps,
      rowKey: primaryKey,
      api: handlePendingApiIntercept,
      clickToRowSelect: false,
      rowSelection: getPendingRowSelection,
    };
  });

  const getSelectedTableBindProps = computed<Partial<DynamicProps<BasicTableProps>>>(() => {
    const { selectedTableProps, primaryKey } = props;
    return {
      ...selectedTableProps,
      dataSource: selectedRows,
      clickToRowSelect: false,
      rowKey: primaryKey,
      api: selectedTableProps!.api ? handleSelectedApiIntercept : undefined,
      rowSelection: {
        type: 'checkbox',
        onSelect: (_record: Recordable, _selected: boolean, selectedRows: Object[]) => {
          pendingConfirmQueue.value = selectedRows;
        },
        onSelectAll: (_selected: boolean, selectedRows: Recordable[]) => {
          pendingConfirmQueue.value = selectedRows;
        },
      },
    };
  });

  const getModalBindProps = computed(() => {
    const { modalProps = {} } = props;
    return {
      width: '60%',
      title: '穿梭表格',
      wrapClassName: prefixCls,
      ...modalProps,
      showOkBtn: false,
    };
  });

  const getBindButtonProps = computed<ButtonProps>(() => {
    const { buttonProps = {} } = props;
    return {
      type: 'link',
      ...buttonProps,
    };
  });

  const getShowTagOptions = computed(() => {
    const { maxTagLength } = props;
    return unref(selectedRows).slice(0, maxTagLength);
  });

  const getSurplusOptionsLength = computed(() => {
    const { maxTagLength } = props;
    const surplusValue = unref(selectedRows).length - maxTagLength;
    return surplusValue < 0 ? 0 : surplusValue;
  });

  const [registerModal, { openModal }] = useModal();

  const [
    regsterPendingTable,
    {
      getSelectRows: getPendingSelectRows,
      getSelectRowKeys: getPendingSelectRowKeys,
      reload: reloadPending,
      clearSelectedRowKeys: clearPendingSelectedRowKeys,
    },
  ] = useTable(unref(getPendingTableBindProps));

  const [
    registerSelectedTable,
    { getSelectRowKeys, setProps, clearSelectedRowKeys, reload: reloadSelected },
  ] = useTable(unref(getSelectedTableBindProps));

  async function handlePendingApiIntercept(params?: Recordable) {
    try {
      const { api } = props.pendingTableProps || {};
      if (api && isFunction(api)) {
        let options = await api(params);
        pendingOptions.value = options;
        const { totalField, listField } = unref(getFetchSetting);
        const total = get(options, totalField!);
        if (unref(selectedTotal) + unref(pendingTotal) !== total) {
          pendingTotal.value = total;
        }
        let list: Recordable[] = get(options, listField!);
        list = getSelectedRows(list);
        options = set(options, listField!, list);
        return options;
      }
    } catch (error) {
      console.error(error);
      return [];
    }
    return [];
  }

  async function handleSelectedApiIntercept(params?: Recordable) {
    try {
      const { api } = props.selectedTableProps || {};
      if (api && isFunction(api)) {
        let options = await api(params);
        pendingOptions.value = options;
        const { totalField, listField } = unref(getFetchSetting);
        selectedTotal.value = get(options, totalField!);
        let list: Recordable[] = get(options, listField!);
        list = getSelectedRows(list);
        options = set(options, listField!, list);
        return options;
      }
    } catch (error) {
      console.error(error);
      return [];
    }
    return [];
  }

  const handleOpenModal = async () => {
    openModal(true);
    await nextTick();
    if (props.value && !props.value.length) {
      activeKey.value = Active.PENDING;
      reloadPending();
    }
  };

  const handleTriggerEmit = (selectedRowKeys: string[], selectedRows: Options[]) => {
    const { transformValue } = props;
    let value = selectedRowKeys;
    if (transformValue && isFunction(transformValue)) {
      value = transformValue(selectedRowKeys, selectedRows);
    }
    emit('change', unref(selectedRowKeys), unref(selectedRows));
    emit('update:value', unref(value));
  };

  const handleSelected = async () => {
    const { onSelectedAfter } = props;
    const currentPageSelectRows = getPendingSelectRows();
    const currentPageSelectRowKeys = getPendingSelectRowKeys();
    const { primaryKey } = props;
    selectedRows.value = uniqBy([...unref(selectedRows), ...currentPageSelectRows], primaryKey);
    selectedRowKeys.value = [...new Set([...unref(selectedRowKeys), ...currentPageSelectRowKeys])];
    pendingConfirmQueue.value = [];
    // selectedTotal.value = unref(selectedRowKeys).length;
    pendingTotal.value = unref(pendingTotal) - currentPageSelectRows.length;
    selectedTotal.value = unref(selectedTotal) + currentPageSelectRows.length;

    clearPendingSelectedRowKeys();
    handleTriggerEmit(unref(selectedRowKeys), unref(selectedRows));

    if (onSelectedAfter && isFunction(onSelectedAfter)) {
      await onSelectedAfter(actionType);
    }
    reloadPending();
  };

  const handleRemoveSelected = async () => {
    const { onRemoveAfter } = props;
    const removeRowKeys = getSelectRowKeys();
    selectedRowKeys.value = unref(selectedRowKeys).filter((key) => !removeRowKeys.includes(key));
    selectedRows.value = unref(selectedRows).filter((item) => {
      const { primaryKey } = props;
      return unref(selectedRowKeys).includes(item[primaryKey]);
    });
    pendingTotal.value = unref(pendingTotal) + removeRowKeys.length;
    selectedTotal.value = unref(selectedTotal) - removeRowKeys.length;

    clearSelectedRowKeys();
    pendingConfirmQueue.value = [];
    setProps({ dataSource: unref(selectedRows) });
    handleTriggerEmit(unref(selectedRowKeys), unref(selectedRows));

    if (onRemoveAfter && isFunction(onRemoveAfter)) {
      await onRemoveAfter(actionType);
    }
  };

  const actionType = {
    setSelectedOptions,
    setSelectedTotal,
    reloadPending,
    reloadPending,
    reloadSelected,
  };

  const getSelectedRows = (options: Recordable[]) => {
    const { labelField, valueField } = props;
    return options.map((item) => ({ ...item, label: item[labelField], value: item[valueField] }));
  };

  const getSelectedKeys = (options: Recordable[]) => {
    const { primaryKey } = props;
    return options.map((item) => item[primaryKey]);
  };

  function setSelectedOptions(options: Recordable[]) {
    selectedRows.value = getSelectedRows(options);
    selectedRowKeys.value = getSelectedKeys(options);
  }

  function setSelectedTotal(number: number) {
    selectedTotal.value = number;
  }

  const handleCheckoutPanel = async (keys: Active) => {
    await nextTick();
    if (keys === Active.PENDING) {
      reloadPending();
    } else {
      reloadSelected();
      setProps({
        dataSource: unref(selectedRows),
      });
    }
  };

  watch(
    () => props.value,
    () => {
      if (props.value && !props.value.length) {
        selectedRowKeys.value = [];
        selectedRows.value = [];
        pendingTotal.value = 0;
        selectedTotal.value = 0;
      }
    }
  );

  onMounted(async () => {
    const { initSelectedOptions } = props;
    if (initSelectedOptions && isFunction(initSelectedOptions)) {
      const options = await initSelectedOptions(actionType);
      setSelectedOptions(options);
    }
  });
</script>

<template>
  <section>
    <BasicModal @register="registerModal" v-bind="getModalBindProps"   :maskClosable="false">
      <section class="bg-gray-100">
        <Tabs v-model:active-key="activeKey" type="card" @change="handleCheckoutPanel">
          <Tabs.TabPane :key="Active.PENDING">
            <template #tab>
              <div class="flex items-center justify-center">
                <span>待选设备</span>
                <Badge show-zero :count="pendingTotal" />
              </div>
            </template>
            <BasicTable @register="regsterPendingTable">
              <template #toolbar>
                <section class="flex w-full justify-end items-center">
                  <!-- <Button type="primary">全选</Button> -->
                  <div class="text-blue-400">
                    <span class="mr-2">选择设备:</span>
                    <span>{{ pendingConfirmQueue.length }}</span>
                  </div>
                </section>
              </template>
            </BasicTable>
            <section class="flex justify-end px-4 pb-4">
              <Button
                type="primary"
                @click="handleSelected"
                :disabled="!pendingConfirmQueue.length"
              >
                <span>确定已选</span>
              </Button>
            </section>
          </Tabs.TabPane>
          <Tabs.TabPane :key="Active.SELECTED">
            <template #tab>
              <div class="flex items-center justify-center">
                <span>已选设备</span>
                <Badge show-zero :count="selectedTotal" />
              </div>
            </template>
            <BasicTable @register="registerSelectedTable">
              <template #toolbar>
                <section class="flex w-full justify-end items-center">
                  <!-- <Button type="primary">全选</Button> -->
                  <div class="text-blue-400">
                    <span class="mr-2">选择设备:</span>
                    <span>{{ pendingConfirmQueue.length }}</span>
                  </div>
                </section>
              </template>
            </BasicTable>
            <section class="flex justify-end px-4 pb-4">
              <Button
                type="primary"
                :disabled="!pendingConfirmQueue.length"
                @click="handleRemoveSelected"
              >
                <span>移除已选</span>
              </Button>
            </section>
          </Tabs.TabPane>
        </Tabs>
      </section>
    </BasicModal>
    <Button @click="handleOpenModal" v-bind="getBindButtonProps">
      <span v-if="!selectedRowKeys.length">选择设备</span>
      <div v-if="selectedRowKeys.length">
        <Tag
          class="!px-2 !py-1 !bg-gray-50 !border-gray-100"
          v-for="item in getShowTagOptions"
          :key="item.value"
        >
          <span>
            {{ item.label }}
          </span>
        </Tag>
        <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength">
          <span> +{{ getSurplusOptionsLength }}... </span>
        </Tag>
      </div>
    </Button>
  </section>
</template>

<style lang="less">
  @prefix-cls: ~'@{namespace}-transfer-table-modal';

  .@{prefix-cls} {
    .vben-basic-table {
      padding-top: 0;
    }

    .vben-basic-form > .ant-row {
      width: 100%;
    }

    .ant-tabs-top-bar {
      background-color: #fff;
    }
  }
</style>
