<template>
  <UPopover v-model:open="isOpen">
    <UChip
      :ui="{ base: 'z-1 size-3' }"
      :show="appliedFields.length > 0"
      :text="appliedFields.length"
      size="xl"
    >
      <UButton
        variant="subtle"
        color="neutral"
        label="Фильтры"
        :icon="appConfig.ui.icons.filter"
      />
    </UChip>

    <template #content>
      <UForm
        :id="formId"
        ref="filters-form"
        class="p-4 w-xs max-h-[60vh] overflow-y-auto"
        :state="models"
        :schema="schema"
        @submit="submit($event.data)"
      >
        <UFormField
          v-for="field of fields"
          :key="field.name"
          v-bind="field"
        >
          <slot
            :name="field.name"
            :model="models[field.name]"
            :state="models"
            :update-model="(value) => models[field.name] = value"
          >
            <component
              :is="presets[field.render]?.(models, field)"
              v-if="typeof field.render === 'string'"
            />

            <component
              :is="field.render(models, field)"
              v-else-if="field.render"
            />
          </slot>
        </UFormField>
      </UForm>

      <USeparator class="mt-0" orientation="horizontal" />

      <div class="flex justify-end space-x-2 p-4">
        <UButton
          color="neutral"
          label="Сбросить"
          variant="ghost"
          @click="reset"
        />

        <UButton
          type="submit"
          label="Подтвердить"
          :color="formRef && formRef.errors.length > 0 ? 'error' : 'primary'"
          :form="formId"
        />
      </div>
    </template>
  </UPopover>
</template>

<script>
import { UCheckbox, UForm, UInput, USelect, USwitch } from "#components";
import { useAppConfig } from "#imports";
import { useCloned } from "@vueuse/core";
import { copy } from "fast-copy";
import { deepEqual } from "fast-equals";
import { computed, h, nextTick, ref, toValue, unref, useId, useTemplateRef, watch } from "vue";
import CDatePicker from "./DatePicker.vue";
const presets = {
  Date: (state, field) => h(CDatePicker, {
    "modelValue": state[field.name],
    "withTime": toValue(field.withTime),
    "onUpdate:modelValue": (value) => state[field.name] = value
  }),
  DateRange: (state, field) => h(CDatePicker, {
    "modelValue": state[field.name],
    "range": true,
    "withTime": unref(field.withTime),
    "onUpdate:modelValue": (value) => state[field.name] = value
  }),
  Input: (state, field) => h(UInput, {
    "modelValue": state[field.name],
    "modelModifiers": { trim: true, optional: true },
    "placeholder": unref(field.placeholder) ?? `\u0412\u0432\u0435\u0434\u0438\u0442\u0435 ${field.label?.toLowerCase() ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"}`,
    "onUpdate:modelValue": (value) => state[field.name] = value
  }, {}),
  InputNumber: (state, field) => h(UInput, {
    "modelValue": state[field.name],
    "modelModifiers": { number: true, optional: true },
    "placeholder": unref(field.placeholder) ?? `\u0412\u0432\u0435\u0434\u0438\u0442\u0435 ${field.label?.toLowerCase() ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"}`,
    "onUpdate:modelValue": (value) => state[field.name] = value
  }, {}),
  Select: (state, field) => h(USelect, {
    "modelValue": state[field.name],
    "items": unref(field.items),
    "placeholder": unref(field.placeholder) ?? `\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 ${field.label?.toLowerCase() ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"}`,
    "onUpdate:modelValue": (value) => state[field.name] = value
  }, {}),
  MultiSelect: (state, field) => h(USelect, {
    "modelValue": state[field.name],
    "items": unref(field.items),
    "placeholder": unref(field.placeholder) ?? `\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 ${field.label?.toLowerCase() ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"}`,
    "multiple": true,
    "onUpdate:modelValue": (value) => state[field.name] = value
  }, {}),
  Checkbox: (state, field) => h(UCheckbox, {
    "modelValue": state[field.name],
    "label": unref(field.label),
    "onUpdate:modelValue": (value) => state[field.name] = Boolean(value) || void 0
  }),
  Switch: (state, field) => h(USwitch, {
    "modelValue": state[field.name],
    "label": unref(field.label),
    "onUpdate:modelValue": (value) => state[field.name] = value || void 0
  })
};
</script>

<script setup>
const props = defineProps({
  source: { type: null, required: true },
  fields: { type: null, required: false },
  schema: { type: null, required: false },
  validate: { type: Function, required: false },
  validateOn: { type: Array, required: false },
  disabled: { type: Boolean, required: false },
  validateOnInputDelay: { type: Number, required: false },
  loadingAuto: { type: Boolean, required: false },
  class: { type: null, required: false }
});
const emit = defineEmits(["submit", "reset"]);
defineSlots();
const appConfig = useAppConfig();
const filters = defineModel({ type: null });
const formId = `form-${useId()}`;
const formRef = useTemplateRef("filters-form");
const { cloned: models, sync: resetModels } = useCloned(() => props.source, {
  clone: copy
});
const appliedFields = computed(() => {
  return props.fields?.filter((field) => {
    let value = filters.value?.[field.name];
    let sourceValue = props.source?.[field.name];
    if (value === "" || value === null || value === void 0 || Array.isArray(value) && value.length === 0) {
      value = null;
    }
    if (sourceValue === "" || sourceValue === null || sourceValue === void 0 || Array.isArray(sourceValue) && sourceValue.length === 0) {
      sourceValue = null;
    }
    return value !== sourceValue && !deepEqual(value, sourceValue);
  }) ?? [];
});
let _isInternalUpdate = false;
watch(filters, (value) => {
  if (!value) {
    return;
  }
  if (_isInternalUpdate) {
    return;
  }
  models.value = copy(value);
}, { immediate: true, deep: true });
const isOpen = ref(false);
function submit(data) {
  filters.value = copy(data);
  emit("submit", filters.value);
  isOpen.value = false;
  _isInternalUpdate = true;
  nextTick(() => _isInternalUpdate = false);
}
function reset() {
  resetModels();
  filters.value = copy(models.value);
  emit("reset", filters.value);
  isOpen.value = false;
  _isInternalUpdate = true;
  nextTick(() => _isInternalUpdate = false);
}
function clearField(field) {
  models.value = copy({ ...models.value, [field.name]: props.source[field.name] });
  submit(models.value);
}
defineExpose({
  appliedFields,
  clearField,
  reset,
  submit
});
</script>
