<script setup>
import { useAppConfig } from "#app";
import UForm from "@nuxt/ui/components/Form.vue";
import UFormField from "@nuxt/ui/components/FormField.vue";
import defu from "defu";
import { splitByCase, upperFirst } from "scule";
import { computed, useSlots, useTemplateRef } from "vue";
import { COMPONENTS_MAP, mapZodTypeToComponent } from "../components_map";
import { useMetaProcessor } from "../utils/useMetaProcessor";
const props = defineProps({
  schema: { type: null, required: true },
  state: { type: Object, required: true },
  config: { type: Object, required: false },
  id: { type: [String, Number], required: false },
  validate: { type: Function, required: false },
  validateOn: { type: Array, required: false },
  disabled: { type: Boolean, required: false },
  validateOnInputDelay: { type: Number, required: false },
  transform: { type: Boolean, required: false },
  loadingAuto: { type: Boolean, required: false }
});
const emits = defineEmits(["submit", "error"]);
const slots = useSlots();
const formRef = useTemplateRef("form");
const shape = props.schema.shape;
const defaults = {
  components: COMPONENTS_MAP,
  theme: {
    wFull: true
  }
};
const appConfig = computed(() => {
  return defu(props.config, useAppConfig().autoForm, defaults);
});
const { processFieldMeta } = useMetaProcessor(appConfig);
const fields = computed(() => {
  const resolvedFields = [];
  for (const key of Object.keys(shape)) {
    const zodType = shape[key];
    const result = mapZodTypeToComponent(key, zodType, appConfig.value, props.state);
    if (!result)
      continue;
    const rawMeta = typeof zodType.meta === "function" ? zodType.meta() || {} : {};
    const meta = processFieldMeta(rawMeta, key);
    const defaultProps = {
      class: appConfig.value?.theme?.wFull ? "w-full" : ""
    };
    resolvedFields.push({
      key,
      formField: {
        name: key,
        slots: findSlots(key),
        ...parseMeta(meta, key, appConfig.value)
      },
      component: meta?.input?.component ?? result.component,
      props: defu(defaultProps, meta?.input?.props, result.componentProps ?? {})
    });
  }
  return resolvedFields;
});
function findSlots(key) {
  return Object.keys(slots).filter((name) => name.startsWith(`${key}-`)).map((name) => name.slice(key.length + 1));
}
function parseMeta(meta, key, config) {
  const enableDefaultTitles = config?.theme?.enableDefaultTitles !== false;
  const label = typeof meta.title === "string" ? meta.title : meta.title === void 0 && enableDefaultTitles ? upperFirst(splitByCase(key).join(" ").toLowerCase()) : void 0;
  return {
    label,
    required: meta.required,
    description: meta.description,
    hint: meta.hint,
    help: meta.help,
    class: meta.theme?.floatRight ? "flex items-center justify-between text-left" : ""
  };
}
function getFieldValue(key) {
  return props.state[key];
}
function updateFieldValue(key, value) {
  ;
  props.state[key] = value;
}
const formProps = computed(() => ({
  schema: props.schema,
  state: props.state,
  id: props.id,
  validate: props.validate,
  validateOn: props.validateOn,
  disabled: props.disabled,
  validateOnInputDelay: props.validateOnInputDelay,
  transform: props.transform,
  loadingAuto: props.loadingAuto
}));
defineExpose({
  form: formRef,
  submit: () => formRef.value?.submit(),
  validate: (opts) => formRef.value?.validate(opts),
  clear: (path) => formRef.value?.clear(path),
  getErrors: (path) => formRef.value?.getErrors(path),
  setErrors: (errors, name) => formRef.value?.setErrors(errors, name),
  errors: computed(() => formRef.value?.errors),
  disabled: computed(() => formRef.value?.disabled),
  dirty: computed(() => formRef.value?.dirty),
  dirtyFields: computed(() => formRef.value?.dirtyFields),
  touchedFields: computed(() => formRef.value?.touchedFields),
  blurredFields: computed(() => formRef.value?.blurredFields)
});
</script>

<template>
  <UForm
    :id="formProps.id"
    ref="form"
    :schema="formProps.schema"
    :state="formProps.state"
    :validate="formProps.validate"
    :validate-on="formProps.validateOn"
    :disabled="formProps.disabled"
    :validate-on-input-delay="formProps.validateOnInputDelay"
    :transform="formProps.transform"
    :loading-auto="formProps.loadingAuto"
    class="space-y-4"
    @submit="emits('submit', $event)"
    @error="emits('error', $event)"
  >
    <slot name="before-fields" />

    <UFormField
      v-for="field in fields"
      :key="field.key"
      :ui="{ description: 'text-left' }"
      v-bind="field.formField"
    >
      <template v-for="slot in field.formField.slots" #[slot]>
        <slot :name="`${field.key}-${slot}`" />
      </template>

      <slot
        :name="field.key"
        :field="field.key"
        :state="props.state"
      >
        <component
          :is="field.component"
          v-bind="field.props"
          :model-value="getFieldValue(field.key)"
          @update:model-value="(value) => updateFieldValue(field.key, value)"
        >
          <slot :name="`${field.key}-content`" />
        </component>
      </slot>
    </UFormField>

    <slot name="after-fields" />

    <slot name="submit" />
  </UForm>
</template>
