<script setup lang="ts">
import type { ExtendedFormApi, VbenFormProps, VbenFormSlots } from './types';

import { nextTick, onMounted, readonly, watch } from 'vue';

import { useForwardPriorityValues } from '@vben-core/composables';
import { get, isEqual } from '@vben-core/shared/utils';

import { useDebounceFn } from '@vueuse/core';

import FormActions from './components/form-actions.vue';
import {
  COMPONENT_BIND_EVENT_MAP,
  COMPONENT_MAP,
  DEFAULT_FORM_COMMON_CONFIG,
} from './config';
import { Form } from './form-render';
import {
  provideComponentRefMap,
  provideFormProps,
  useFormInitial,
} from './use-form-context';

// 通过 extends 会导致热更新卡死，所以重复写了一遍
interface Props extends VbenFormProps {
  formApi?: ExtendedFormApi<any, any, any, any>;
}

const props = defineProps<Props>();
defineSlots<
  Record<string, ((props: Record<string, any>) => any) | undefined> &
    VbenFormSlots<any, any, any>
>();

const formApi = props.formApi;
if (!formApi) {
  throw new Error('Form api is required in <VbenUseForm />');
}

const state = formApi.useStore();

const forward = useForwardPriorityValues(props, state);

const componentRefMap = new Map<string, unknown>();

const { delegatedSlots, form } = useFormInitial(forward);
const values = form.useValues();

provideFormProps([forward, form]);
provideComponentRefMap(componentRefMap);

formApi.mount(form, componentRefMap);

function handleUpdateCollapsed(value: boolean) {
  props.formApi?.setState({ collapsed: value });
  // 触发收起展开状态变化回调
  forward.value.handleCollapsedChange?.(value);
}

function handleKeyDownEnter(event: KeyboardEvent) {
  if (!state?.value.submitOnEnter || !forward.value.formApi?.isMounted) {
    return;
  }
  // 输入法组合态（打中文选词）的回车是「确认候选词」，不是提交意图
  if (event.isComposing) {
    return;
  }
  // 如果是 textarea 不阻止默认行为，否则会导致无法换行。
  // 跳过 textarea 的回车提交处理
  if (event.target instanceof HTMLTextAreaElement) {
    return;
  }
  const target = event.target instanceof Element ? event.target : null;
  // 焦点在按钮上（如重置/展开）时交给原生行为：回车本就会触发该按钮 click，
  // 若在这里拦截成提交，会出现「聚焦重置按钮按回车却执行了查询」的错乱
  if (target?.closest('button')) {
    return;
  }
  // 下拉面板打开时回车是「选中选项」（keydown 冒泡是同步的，open class 尚未被
  // Vue 异步渲染移除，此刻仍可命中），跳过提交；面板关闭后的下一次回车才提交，
  // 即「下拉需要第二次回车才搜索」
  if (target?.closest('.ant-select-open')) {
    return;
  }
  // 日期/时间选择器：与 Select 一致的两段式语义——
  // 面板打开时回车是「确认日期选择」（keydown 同步阶段面板 DOM 尚未被异步移除，仍可命中），跳过提交；
  // 面板关闭后的下一次回车才提交，即「选完日期第二次回车即搜索」
  if (target?.closest('.ant-picker')) {
    const openPickerPanel = document.querySelector(
      '.ant-picker-dropdown:not(.ant-picker-dropdown-hidden)',
    );
    if (openPickerPanel) {
      return;
    }
    // 面板关闭态：rc-picker 的回车会重新打开面板，提交的同时主动失焦收面板，
    // 避免搜索结果被日期面板挡住
    event.preventDefault();
    if (target instanceof HTMLElement) {
      target.blur();
    }
    forward.value.formApi?.validateAndSubmit();
    return;
  }
  event.preventDefault();
  // 关闭态 Select 的回车会同步重新打开面板（rc-select 行为），提交的同时主动
  // 失焦收起面板，避免搜索结果被下拉挡住
  if (target instanceof HTMLElement && target.closest('.ant-select')) {
    target.blur();
  }
  forward.value.formApi?.validateAndSubmit();
}

const handleValuesChangeDebounced = useDebounceFn(async () => {
  state?.value.submitOnChange && forward.value.formApi?.validateAndSubmit();
}, state?.value?.changeDebouncedTime ?? 300);

let valuesChangeReady = false;

onMounted(async () => {
  // 只在挂载后开始监听，form.values会有一个初始化的过程
  await nextTick();
  valuesChangeReady = true;
});

watch(values, (currentValues, previousValues) => {
  if (!valuesChangeReady) {
    return;
  }
  const handleValuesChange = forward.value.handleValuesChange;
  const submitOnChange = state?.value.submitOnChange;
  if (!handleValuesChange && !submitOnChange) {
    return;
  }
  const fields = state?.value.schema?.map((item) => item.fieldName) ?? [];
  if (handleValuesChange && fields.length > 0) {
    const changedFields = fields.filter((field) => {
      return !isEqual(
        get(currentValues, field),
        get(previousValues ?? {}, field),
      );
    });
    if (changedFields.length > 0) {
      handleValuesChange(readonly(currentValues), changedFields, () =>
        formApi.formatValues(currentValues),
      );
    }
  }
  if (submitOnChange) {
    handleValuesChangeDebounced();
  }
});
</script>

<template>
  <Form
    @keydown.enter="handleKeyDownEnter"
    v-bind="forward"
    :collapsed="state?.collapsed"
    :component-bind-event-map="COMPONENT_BIND_EVENT_MAP"
    :component-map="COMPONENT_MAP"
    :form="form"
    :global-common-config="DEFAULT_FORM_COMMON_CONFIG"
  >
    <template
      v-for="slotName in delegatedSlots"
      :key="slotName"
      #[slotName]="slotProps"
    >
      <slot
        :name="slotName"
        v-bind="slotProps"
        :form-api="formApi"
        :values="form.values"
      ></slot>
    </template>
    <template #default="slotProps">
      <slot
        v-if="$slots.default"
        v-bind="slotProps"
        :form-api="formApi"
        :values="form.values"
      ></slot>
      <FormActions
        v-else-if="forward.showDefaultActions"
        :model-value="state?.collapsed"
        @update:model-value="handleUpdateCollapsed"
      >
        <template #reset-before="resetSlotProps">
          <slot
            name="reset-before"
            v-bind="resetSlotProps"
            :form-api="formApi"
            :values="form.values"
          ></slot>
        </template>
        <template #submit-before="submitSlotProps">
          <slot
            name="submit-before"
            v-bind="submitSlotProps"
            :form-api="formApi"
            :values="form.values"
          ></slot>
        </template>
        <template #expand-before="expandBeforeSlotProps">
          <slot
            name="expand-before"
            v-bind="expandBeforeSlotProps"
            :form-api="formApi"
            :values="form.values"
          ></slot>
        </template>
        <template #expand-after="expandAfterSlotProps">
          <slot
            name="expand-after"
            v-bind="expandAfterSlotProps"
            :form-api="formApi"
            :values="form.values"
          ></slot>
        </template>
      </FormActions>
    </template>
  </Form>
</template>
