<script setup>
import { VDataTable } from "vuetify/components/VDataTable";
import { VInput } from "vuetify/components/VInput";
import { computed, nextTick, ref, useAttrs, watch, useTemplateRef } from "vue";
import { omit } from "lodash-es";
import { useDialog } from "../../composables/dialog";
import { useLocalStorageModel } from "../../composables/localStorageModel";
import { usePerPagePreference } from "../../composables/perPagePreference";
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" },
  dialogWidth: { type: [String, Number], required: false },
  dialogMaxWidth: { type: [String, Number], required: false },
  dialogHeight: { type: [String, Number], required: false },
  dialogMaxHeight: { type: [String, Number], required: false },
  dialogFullscreen: { type: Boolean, required: false, default: false },
  initialData: { type: Object, required: false },
  toolbarColor: { type: String, required: false, default: "primary" },
  importable: { type: Boolean, required: false, default: true },
  exportable: { type: Boolean, required: false, default: true },
  insertable: { type: Boolean, required: false, default: true },
  searchable: { type: Boolean, required: false, default: true },
  inputPad: { type: Boolean, required: false, default: false },
  inputPadOnly: { type: Boolean, required: false, default: false },
  saveAndStay: { type: Boolean, required: false, default: false },
  stringFields: { type: Array, required: false, default: () => [] },
  perPageStorageKey: { type: String, required: false },
  perPageStorageEnabled: { type: Boolean, required: false, default: true },
  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", "open:dialog", "close:dialog"]);
const attrs = useAttrs();
const plainAttrs = computed(() => {
  return omit(attrs, ["modelValue", "onUpdate:modelValue"]);
});
const inputRef = useTemplateRef("inputRef");
const items = ref([]);
const search = ref();
const currentItem = ref(void 0);
const itemsPerPageInternal = ref();
useLocalStorageModel(items, props);
usePerPagePreference(itemsPerPageInternal, {
  storageKey: props.perPageStorageKey,
  enabled: props.perPageStorageEnabled
});
function setSearch(keyword) {
  search.value = keyword;
}
const isDialogOpen = ref(false);
watch(() => props.modelValue, (newValue) => {
  if (!Array.isArray(newValue) || !newValue.every((item) => typeof item === "object")) {
    items.value = [];
  } 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;
      }
    });
    items.value = newValue;
  }
}, { immediate: true });
watch(items, (newValue) => {
  emit("update:modelValue", newValue);
}, { deep: true });
function createItem(item, callback) {
  if (items.value.length > 0) item[props.modelKey] = Math.max(...items.value.map((i) => i[props.modelKey] || 0)) + 1;
  else item[props.modelKey] = 1;
  items.value.push(item);
  if (callback && callback.setData) callback.setData(item);
  if (callback) callback.done();
}
function importItems(importItems2, callback) {
  importItems2.forEach((item) => {
    createItem(item);
  });
  if (callback) callback.done();
}
function updateItem(newItem, callback) {
  const index = items.value.findIndex((item) => item[props.modelKey] === newItem[props.modelKey]);
  if (index !== -1) {
    items.value[index] = newItem;
  }
  if (callback && callback.setData) callback.setData(newItem);
  if (callback) callback.done();
}
function moveUpItem(currentItem2, callback) {
  const index = items.value.findIndex((item) => item[props.modelKey] === currentItem2[props.modelKey]);
  if (index > 0) {
    const temp = items.value[index - 1];
    items.value[index - 1] = items.value[index];
    items.value[index] = temp;
  }
  if (callback) callback.done();
}
function moveDownItem(currentItem2, callback) {
  const index = items.value.findIndex((item) => item[props.modelKey] === currentItem2[props.modelKey]);
  if (index >= 0 && index < items.value.length - 1) {
    const temp = items.value[index + 1];
    items.value[index + 1] = items.value[index];
    items.value[index] = temp;
  }
  if (callback) callback.done();
}
function moveToItem(currentItem2, callback) {
  const index = items.value.findIndex((item) => item[props.modelKey] === currentItem2[props.modelKey]);
  if (index !== -1) {
    const newPosition = prompt("Enter the new position (0-based index):");
    const parsedPosition = parseInt(newPosition, 10);
    if (isNaN(parsedPosition) || parsedPosition < 0 || parsedPosition >= items.value.length) {
      alert("Invalid position entered. Please enter a number between 0 and " + (items.value.length - 1));
      return;
    }
    const [temp] = items.value.splice(index, 1);
    items.value.splice(parsedPosition, 0, temp);
  }
  if (callback) callback.done();
}
async function deleteItem(deleteItem2, callback) {
  const index = items.value.findIndex((item) => item[props.modelKey] === deleteItem2[props.modelKey]);
  if (index !== -1) {
    let confirm = await useDialog().confirm({ message: "Do you want to delete record?" });
    if (confirm) {
      items.value.splice(index, 1);
      if (callback) callback.done();
    }
  }
}
function openDialog(item) {
  if (props.inputPadOnly) inputPadRef.value?.setOriginalData(item);
  else {
    currentItem.value = item;
    nextTick(() => {
      isDialogOpen.value = true;
      emit("open:dialog", item);
    });
  }
}
const inputPadRef = ref();
const operation = ref({ openDialog, createItem, updateItem, deleteItem, moveUpItem, moveDownItem, moveToItem, setSearch });
const isValid = computed(() => {
  return inputRef.value?.isValid;
});
const errorMessages = computed(() => {
  return inputRef.value?.errorMessages;
});
defineExpose({
  errorMessages,
  isValid,
  reset: () => inputRef.value?.reset(),
  resetValidation: () => inputRef.value?.resetValidation(),
  validate: () => inputRef.value?.validate(),
  operation,
  items
});
</script>

<template>
  <v-input v-model="items" v-bind="plainAttrs" ref="inputRef">
    <template #default="{ isReadonly, isDisabled }">
      <v-container fluid class="ma-0 pa-0">
        <v-card>
          <slot
              name="header"
              :items="items"
              :operation="operation"
          >
            <VToolbar :color="toolbarColor">
              <v-row
                  justify="end"
                  class="ma-1"
                  density="compact"
                  no-gutters
                  align="center"
              >
                <v-col cols="7">
                  <VToolbarTitle class="pl-3">
                    <slot name="title">
                      {{ title }}
                    </slot>
                  </VToolbarTitle>
                </v-col>
                <v-col cols="5">
                  <slot name="search" :items="items" :operation="operation" v-if="props.searchable">
                    <VTextField
                        v-model="search"
                        class="justify-end w-100"
                        density="compact"
                        hide-details
                        placeholder="ค้นหา"
                        clearable
                        variant="solo"
                    />
                  </slot>
                </v-col>
              </v-row>

              <VToolbarItems>
                <slot name="toolbarItems" :items="items" :operation="operation"/>
                <ImportCSV
                    v-if="props.importable && !(isReadonly.value || isDisabled.value)"
                    icon="mdi:mdi-file-upload"
                    variant="flat"
                    @import="importItems"
                    :color="toolbarColor"
                    :stringFields="props.stringFields"
                />
                <ExportCSV
                    v-if="props.exportable && items.length && !(isReadonly.value || isDisabled.value)"
                    icon="mdi:mdi-file-download"
                    variant="flat"
                    :file-name="title"
                    :model-value="items"
                    :color="toolbarColor"
                    :stringFields="props.stringFields"
                />
                <VBtn
                    v-if="props.insertable && !props.inputPadOnly && !(isReadonly.value || isDisabled.value)"
                    :color="toolbarColor"
                    prepend-icon="mdi:mdi-plus"
                    variant="flat"
                    @click="openDialog()"
                >
                  add
                </VBtn>
              </VToolbarItems>
            </VToolbar>
          </slot>
          <v-data-table
              v-bind="plainAttrs"
              v-model:items-per-page="itemsPerPageInternal"
              color="primary"
              :items="items"
              :search="search"
          >
            <!-- @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-if="!$slots['item.operation'] && !(isReadonly.value || isDisabled.value)"
                #item.operation="props"
            >
              <v-icon density="compact" :disabled="props.index == 0" @click="moveUpItem(props.item)">mdi:mdi-arrow-up-thick</v-icon>
              <v-icon density="compact" @click="moveToItem(props.item)">fa:fas fa-arrow-right-to-bracket</v-icon>
              <v-icon density="compact" :disabled="props.index == items.length - 1" @click="moveDownItem(props.item)">mdi:mdi-arrow-down-thick</v-icon>
            </template>
            <template
                v-if="!$slots['item.action'] && !(isReadonly.value || isDisabled.value)"
                #item.action="{ item }"
            >
              <v-btn
                  variant="flat"
                  density="compact"
                  icon="mdi:mdi-note-edit"
                  @click="openDialog(item)"
              />
              <v-btn
                  variant="flat"
                  density="compact"
                  icon="mdi:mdi-delete"
                  @click="deleteItem(item)"
              />
            </template>
          </v-data-table>
          <FormDialog
              v-model="isDialogOpen"
              :title="title"
              :fullscreen="dialogFullscreen"
              :initial-data="initialData"
              :form-data="currentItem"
              @create="createItem"
              @update="updateItem"
              @afterLeave="emit('close:dialog')"
              :saveAndStay="saveAndStay"
              :width="dialogWidth"
              :height="dialogHeight"
              :max-width="dialogMaxWidth"
              :max-height="dialogMaxHeight"
              v-if="!props.inputPadOnly"
          >
            <template #default="slotData">
              <slot
                  name="form"
                  v-bind="slotData"
              />
            </template>
            <template #title="slotData">
              <slot
                  name="formTitle"
                  v-bind="slotData"
              />
            </template>
            <template #action="slotData">
              <slot
                  name="formAction"
                  v-bind="slotData"
              />
            </template>
          </FormDialog>
        </v-card>
        <slot name="inputPad" :operation="operation" :isReadonly="isReadonly" :isDisabled="isDisabled">
          <v-sheet border rounded class="mt-1" v-if="inputPad && !(isReadonly.value || isDisabled.value)">
            <form-action-pad ref="inputPadRef" :title="props.title" @create="createItem" @update="updateItem" :initial-data="initialData">
              <template #default="slotData">
                <slot
                    name="form"
                    v-bind="slotData"
                />
              </template>
            </form-action-pad>
          </v-sheet>
        </slot>
      </v-container>
    </template>
  </v-input>
</template>
