<script setup>
import { VDataTable } from "vuetify/components/VDataTable";
import { VInput } from "vuetify/components/VInput";
import { computed, ref, useAttrs, watch, useTemplateRef } from "vue";
import { cloneDeep, isEqual, omit, isArray, isString } from "lodash-es";
import { templateItemToString } from "../../composables/document/template";
import { templateToHeader } from "../../composables/document/templateFormTable";
import { useLocalStorageModel } from "../../composables/localStorageModel";
defineOptions({
  inheritAttrs: false
});
const props = defineProps({
  title: { type: String, required: true },
  noDataText: { type: String, required: false, default: "\u0E44\u0E21\u0E48\u0E1E\u0E1A\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25" },
  modelValue: { type: Array, required: false },
  modelKey: { type: String, required: false, default: "id" },
  toolbarColor: { type: String, required: false, default: "primary" },
  headers: { type: Array, required: true },
  itemsInitial: { type: Array, required: true },
  dataTemplate: { type: [String, Object], required: false },
  disableApplyToAll: { type: [Boolean, String, Array], required: false, default: false },
  persist: { type: Boolean, required: false },
  persistKey: { type: String, required: false },
  persistPrefix: { type: String, required: false },
  persistDebounce: { type: Number, required: false },
  persistClearOnEmpty: { type: Boolean, required: false },
  persistTtl: { type: Number, required: false },
  persistEncrypt: { type: Boolean, required: false },
  persistSecret: { type: String, required: false },
  persistAlwaysHydrate: { type: Boolean, required: false }
});
const emit = defineEmits(["update:modelValue"]);
const attrs = useAttrs();
const plainAttrs = computed(() => {
  return omit(attrs, ["modelValue", "items", "onUpdate:modelValue", "itemsInitial", "dataTemplate"]);
});
const inputRef = useTemplateRef("inputRef");
const itemsInternal = ref([]);
const itemsApplyAll = ref({});
useLocalStorageModel(itemsInternal, props);
const computedDisableApplyToAll = computed(() => {
  if (isString(props.disableApplyToAll)) {
    return props.disableApplyToAll.split(",").map((i) => i.trim());
  }
  return props.disableApplyToAll;
});
const canApplyAll = (variableName) => {
  if (isArray(computedDisableApplyToAll.value)) return !computedDisableApplyToAll.value.includes(variableName);
  return !computedDisableApplyToAll.value;
};
watch(() => props.modelValue, (newValue) => {
  if (!Array.isArray(newValue) || !newValue.every((item) => typeof item === "object")) {
    itemsInternal.value = cloneDeep(props.itemsInitial);
  } else {
    let maxKey = 0;
    newValue.forEach((item) => {
      if (!item.hasOwnProperty(props.modelKey)) {
        maxKey = Math.max(maxKey, ...newValue.map((i) => i[props.modelKey] || 0));
        item[props.modelKey] = maxKey + 1;
      }
    });
    itemsInternal.value = newValue;
  }
}, { immediate: true });
watch(() => props.itemsInitial, (newValue, oldValue) => {
  if (!isEqual(newValue, oldValue)) itemsInternal.value = cloneDeep(props.itemsInitial);
}, { immediate: true, deep: true });
watch(itemsInternal, () => {
  emit("update:modelValue", itemsInternal.value);
}, { deep: true });
watch(itemsApplyAll, () => {
  itemsInternal.value = itemsInternal.value?.map((item) => {
    return Object.assign(item, itemsApplyAll.value);
  });
}, { deep: true });
const computedHeaders = computed(() => {
  let dataHeaders = templateToHeader(props.dataTemplate);
  let combinedHeaders = [...props.headers, ...dataHeaders];
  return combinedHeaders.map((header) => {
    return {
      ...header,
      headerProps: {
        style: "font-weight: 800"
      }
    };
  });
});
const computedDataTemplate = computed(() => {
  let template = cloneDeep(props.dataTemplate);
  if (isString(props.dataTemplate)) {
    try {
      template = JSON.parse(props.dataTemplate);
    } catch (e) {
      void e;
    }
  }
  if (isArray(template)) {
    return template.map((t) => {
      const out = { ...t };
      let s = out.inputAttributes?.trim() || "";
      if (!/(^|\s)hide-details(\s|$)/.test(s) && !/(^|\s)hideDetails(\s|$)/.test(s)) {
        s = `${s} hide-details`.trim();
      }
      out.inputAttributes = s;
      return out;
    });
  }
  return [];
});
const reset = () => {
  inputRef.value?.reset();
  itemsApplyAll.value = {};
};
const isValid = computed(() => {
  return inputRef.value?.isValid;
});
const errorMessages = computed(() => {
  return inputRef.value?.errorMessages;
});
const operation = ref({ reset });
defineExpose({
  errorMessages,
  isValid,
  reset,
  resetValidation: () => inputRef.value?.resetValidation(),
  validate: () => inputRef.value?.validate(),
  operation
});
</script>

<template>
  <v-input v-model="itemsInternal" v-bind="plainAttrs" ref="inputRef">
    <template #default="{ isReadonly, isDisabled }">
      <v-container fluid class="ma-0 pa-0">
        <v-card>
          <slot
              name="header"
              :items="itemsInternal"
              :operation="operation"
          >
            <VToolbar :color="toolbarColor">
              <v-row
                  justify="end"
                  class="ma-1"
                  density="compact"
                  no-gutters
                  align="center"
              >
                <v-col>
                  <VToolbarTitle class="pl-3">
                    <slot name="title">
                      {{ title }}
                    </slot>
                  </VToolbarTitle>
                </v-col>
              </v-row>

              <VToolbarItems>
                <slot name="toolbarItems" :items="itemsInternal" :operation="operation"/>
                <VBtn
                    :color="toolbarColor"
                    icon="mdi:mdi-restore"
                    variant="flat"
                    @click="reset()"
                >
                </VBtn>
              </VToolbarItems>
            </VToolbar>
          </slot>
          <v-data-table
              v-bind="plainAttrs"
              color="primary"
              :items="itemsInternal"
              :headers="computedHeaders"
              disable-sort
              hide-default-footer
          >
            <!-- @ts-ignore -->
            <template
                v-for="(_, name, index) in $slots"
                :key="index"
                #[name]="slotData"
            >
              <slot
                  :name="name"
                  v-bind="slotData || {}"
                  :operation="operation"
                  :isReadonly="isReadonly"
                  :isDisabled="isDisabled"
              />
            </template>

            <template v-for="template in computedDataTemplate" :key="template.variableName" #[`header.${template.variableName}`]="props">
              <form-pad v-model="itemsApplyAll" :template="templateItemToString(template, [])" v-if="canApplyAll(template.variableName)"></form-pad>
              <template v-else>{{props.column.title}}</template>
            </template>

            <template v-for="template in computedDataTemplate" :key="template.variableName" #[`item.${template.variableName}`]="{ index }">
              <form-pad v-model="itemsInternal[index]" :template="templateItemToString(template, [])"></form-pad>
            </template>
          </v-data-table>
        </v-card>
      </v-container>
    </template>
  </v-input>
</template>
