<template>
  <component
    v-model="activeTabName"
    v-bind="
      tabsComponent?.props({
        type: config.tabType,
        editable: config.editable || false,
        tabPosition: config.tabPosition || 'top',
      }) || {}
    "
    :is="tabsComponent?.component || 'el-tabs'"
    :class="`tmagic-design-tabs ${config.dynamic ? 'magic-form-dynamic-tab' : 'magic-form-tab'}`"
    @tab-click="tabClickHandler"
    @tab-add="onTabAdd"
    @tab-remove="onTabRemove"
  >
    <component
      v-for="(tab, tabIndex) in tabs"
      :is="tabPaneComponent?.component || 'el-tab-pane'"
      :key="tab[mForm?.keyProp || '__key'] ?? tabIndex"
      v-bind="
        tabPaneComponent?.props({
          name: filter(tab.status) || tabIndex.toString(),
          lazy: isCompare ? false : tab.lazy || false,
        }) || {}
      "
    >
      <template #label>
        <span>
          {{ filter(tab.title)
          }}<TMagicBadge
            :hidden="!diffCount[Number(tabIndex)]"
            :value="diffCount[Number(tabIndex)]"
            class="diff-count-badge"
          ></TMagicBadge>
        </span>
      </template>
      <Container
        v-for="item in tabItems(tab)"
        :key="(item as Record<string, any>)[mForm?.keyProp || '__key']"
        :config="item"
        :disabled="disabled"
        :model="
          config.dynamic
            ? (name ? model[name] : model)[tabIndex]
            : tab.name
              ? (name ? model[name] : model)[tab.name]
              : name
                ? model[name]
                : model
        "
        :last-values="
          isEmpty(lastValues)
            ? {}
            : config.dynamic
              ? (name ? lastValues[name] : lastValues)[tabIndex]
              : tab.name
                ? (name ? lastValues[name] : lastValues)[tab.name]
                : name
                  ? lastValues[name]
                  : lastValues
        "
        :is-compare="isCompare"
        :prop="
          config.dynamic
            ? `${prop}${prop ? '.' : ''}${String(tabIndex)}`
            : tab.name
              ? `${prop}${prop ? '.' : ''}${tab.name}`
              : prop
        "
        :size="size"
        :label-width="tab.labelWidth || labelWidth"
        :expand-more="expandMore"
        @change="changeHandler"
        @addDiffCount="onAddDiffCount(Number(tabIndex))"
      ></Container>
    </component>
  </component>
</template>

<script setup lang="ts">
import { computed, inject, ref, watch, watchEffect } from 'vue';
import { isEmpty } from 'lodash-es';

import { getDesignConfig, TMagicBadge } from '@tmagic/design';

import type { ContainerChangeEventData, FormState, TabConfig, TabPaneConfig } from '../schema';
import { display as displayFunc, filterFunction, initValue } from '../utils/form';

import Container from './Container.vue';

defineOptions({
  name: 'MFormTabs',
});

type DiffCount = {
  [tabIndex: number]: number;
};

const props = withDefaults(
  defineProps<{
    model: any;
    lastValues?: any;
    isCompare?: boolean;
    config: TabConfig;
    name: string;
    size?: string;
    labelWidth?: string;
    prop?: string;
    expandMore?: boolean;
    disabled?: boolean;
  }>(),
  {
    lastValues: () => ({}),
    isCompare: false,
    prop: '',
  },
);

const tabPaneComponent = getDesignConfig('components')?.tabPane;
const tabsComponent = getDesignConfig('components')?.tabs;

const getActive = (mForm: FormState | undefined, props: any, activeTabName: string) => {
  const { config, model, prop } = props;
  const { active } = config;

  if (typeof active === 'function') return active(mForm, { model, formValue: mForm?.values, prop });
  if (+activeTabName >= props.config.items.length) return '0';
  if (typeof active !== 'undefined') return active;

  return '0';
};

const tabClick = (mForm: FormState | undefined, tab: any, props: any) => {
  const { config, model, prop } = props;

  // 兼容vue2的element-ui
  tab.name = tab.paneName;

  if (typeof config.onTabClick === 'function') {
    config.onTabClick(mForm, tab, { model, formValue: mForm?.values, prop, config });
  }

  const tabConfig = config.items.find((item: TabPaneConfig) => tab.name === item.status);
  if (tabConfig && typeof tabConfig.onTabClick === 'function') {
    tabConfig.onTabClick(mForm, tab, { model, formValue: mForm?.values, prop, config });
  }
};

const emit = defineEmits<{
  change: [v: any, eventData?: ContainerChangeEventData];
  addDiffCount: [];
}>();

const mForm = inject<FormState | undefined>('mForm');
const activeTabName = ref(getActive(mForm, props, ''));
const diffCount = ref<DiffCount>({});

const tabs = computed(() => {
  if (props.config.dynamic) {
    if (!props.config.name) throw new Error('dynamic tab 必须配置name');
    return props.model[props.config.name] || [];
  }
  return props.config.items.filter((item) => displayFunc(mForm, item.display, props));
});

const filter = (config: any) => filterFunction(mForm, config, props);

watchEffect(() => {
  if (typeof props.config.activeChange === 'function') {
    props.config.activeChange(mForm, activeTabName.value, {
      model: props.model,
      prop: props.prop,
    });
  }
});

// model 或 lastValues 变化时，重置差异数
watch([() => props.model, () => props.lastValues], () => {
  diffCount.value = {};
});

const tabItems = (tab: TabPaneConfig) => (props.config.dynamic ? props.config.items : tab.items);

const tabClickHandler = (tab: any) => {
  if (typeof tab === 'object') {
    tabClick(mForm, tab, props);
  } else {
    let item = tabs.value.find((tab: any) => tab.status === tab);
    if (!item) {
      item = tabs.value[tab];
    }
    tabClick(mForm, item, props);
  }
};

const onTabAdd = async () => {
  if (!props.name) throw new Error('dynamic tab 必须配置name');

  if (typeof props.config.onTabAdd === 'function') {
    props.config.onTabAdd(mForm, {
      model: props.model,
      prop: props.prop,
      config: props.config,
    });
    emit('change', props.model[props.name]);
  } else {
    const newObj = await initValue(mForm, {
      config: props.config.items,
      initValues: {},
    });

    newObj.title = `标签${tabs.value.length + 1}`;

    props.model[props.name].push(newObj);

    emit('change', props.model[props.name], {
      changeRecords: [
        {
          propPath: `${props.prop}.${props.model[props.name].length - 1}`,
          value: newObj,
        },
      ],
    });
  }

  mForm?.$emit('field-change', props.prop, props.model[props.name]);
};

const onTabRemove = (tabName: string) => {
  if (!props.name) throw new Error('dynamic tab 必须配置name');

  if (typeof props.config.onTabRemove === 'function') {
    props.config.onTabRemove(mForm, tabName, {
      model: props.model,
      prop: props.prop,
      config: props.config,
    });
  } else {
    props.model[props.name].splice(+tabName, 1);

    // 防止删除后没有选中的问题
    if (tabName < activeTabName.value || activeTabName.value >= props.model[props.name].length) {
      activeTabName.value = (+activeTabName.value - 1).toString();
      tabClick(mForm, { name: activeTabName.value }, props);
    }
  }
  emit('change', props.model[props.name]);
  mForm?.$emit('field-change', props.prop, props.model[props.name]);
};

const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
  emit('change', props.name ? props.model[props.name] : props.model, eventData);
};

// 在tabs组件中收集事件触发次数，即该tab下的差异数
const onAddDiffCount = (tabIndex: number) => {
  if (!diffCount.value[tabIndex]) {
    diffCount.value[tabIndex] = 1;
  } else {
    diffCount.value[tabIndex] += 1;
  }
  // 继续抛出给更高层级的组件
  emit('addDiffCount');
};
</script>
