<script setup>
import { computed, nextTick, ref, useAttrs, useSlots, useTemplateRef, watch } from "vue";
import { omit } from "lodash-es";
import { VDataIterator } from "vuetify/components/VDataIterator";
import { VDataTable } from "vuetify/components/VDataTable";
import { VInput } from "vuetify/components/VInput";
import { useDisplay } from "vuetify";
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" },
  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 },
  loading: { type: Boolean, required: false, default: false },
  viewSwitch: { type: Boolean, required: false, default: false },
  viewSwitchMultiple: { type: Boolean, required: false, default: false },
  cols: { type: [String, Number, Boolean], required: false, default: 12 },
  xxl: { type: [String, Number, Boolean], required: false, default: false },
  xl: { type: [String, Number, Boolean], required: false, default: false },
  lg: { type: [String, Number, Boolean], required: false, default: 2 },
  md: { type: [String, Number, Boolean], required: false, default: 4 },
  sm: { type: [String, Number, Boolean], required: false, default: 6 },
  itemsPerPage: { type: [String, Number], required: false, default: 12 },
  preferTable: { type: [String, Number, Boolean], required: false },
  preferTableXxl: { type: [String, Number, Boolean], required: false },
  preferTableXl: { type: [String, Number, Boolean], required: false },
  preferTableLg: { type: [String, Number, Boolean], required: false },
  preferTableMd: { type: [String, Number, Boolean], required: false },
  preferTableSm: { type: [String, Number, Boolean], required: false },
  perPageStorageKey: { type: String, required: false },
  perPageStorageEnabled: { type: Boolean, required: false, default: true }
});
const emit = defineEmits(["update:modelValue"]);
const attrs = useAttrs();
const plainAttrs = computed(() => {
  return omit(attrs, ["modelValue", "onUpdate:modelValue"]);
});
const inputRef = useTemplateRef("inputRef");
const slots = useSlots();
const tableSlots = computed(() => {
  return omit(slots, ["item"]);
});
const display = useDisplay();
const isProgrammaticSet = ref(false);
const userOverrodeView = ref(false);
const viewType = ref("iterator");
function setViewTypeSafely(val) {
  isProgrammaticSet.value = true;
  if (Array.isArray(viewType.value)) {
    viewType.value = [val];
  } else {
    viewType.value = val;
  }
  nextTick(() => {
    isProgrammaticSet.value = false;
  });
}
watch(viewType, () => {
  if (!isProgrammaticSet.value) userOverrodeView.value = true;
});
function parsePrefer(val) {
  if (val === true) return true;
  const n = Number(val);
  return Number.isFinite(n) && n > 0 ? n : false;
}
const computedPreferTable = computed(() => {
  const bp = display.name?.value;
  if (bp === "xxl" && props.preferTableXxl !== void 0) return parsePrefer(props.preferTableXxl);
  if (bp === "xl" && props.preferTableXl !== void 0) return parsePrefer(props.preferTableXl);
  if (bp === "lg" && props.preferTableLg !== void 0) return parsePrefer(props.preferTableLg);
  if (bp === "md" && props.preferTableMd !== void 0) return parsePrefer(props.preferTableMd);
  if (bp === "sm" && props.preferTableSm !== void 0) return parsePrefer(props.preferTableSm);
  return parsePrefer(props.preferTable);
});
const items = ref([]);
const search = ref();
const currentItem = ref(void 0);
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 });
watch(
  [() => items.value?.length, computedPreferTable, () => display.name?.value],
  ([len, prefer]) => {
    if (userOverrodeView.value) return;
    let target = "iterator";
    if (prefer === true) {
      target = "table";
    } else if (typeof prefer === "number") {
      if (Number(len) >= prefer) target = "table";
    }
    if (!viewType.value?.includes(target)) setViewTypeSafely(target);
  },
  { immediate: true }
);
const itemsPerPageInternal = ref();
watch(() => props.itemsPerPage, (newValue) => {
  if (newValue.toString().toLowerCase() == "all") itemsPerPageInternal.value = "-1";
  else if (newValue) itemsPerPageInternal.value = newValue;
}, { immediate: true });
usePerPagePreference(itemsPerPageInternal, {
  storageKey: props.perPageStorageKey,
  enabled: props.perPageStorageEnabled
});
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.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.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();
}
function deleteItem(deleteItem2, callback) {
  const index = items.value.findIndex((item) => item[props.modelKey] === deleteItem2[props.modelKey]);
  if (index !== -1) {
    items.value.splice(index, 1);
  }
  if (callback) callback.done();
}
function openDialog(item) {
  currentItem.value = item;
  nextTick(() => {
    isDialogOpen.value = true;
  });
}
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
});
</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>
          <v-data-iterator
              v-bind="plainAttrs"
              v-model:items-per-page="itemsPerPageInternal"
              :items="items"
              :item-value="modelKey"
              :search="search"
              :loading="loading"
          >
            <template #default="defaultProps" v-if="viewType.includes('iterator')">
              <slot
                  v-bind="defaultProps"
                  :operation="operation"
              >
                <v-container fluid>
                  <v-row>
                    <v-col
                        v-for="(item, index) in defaultProps.items"
                        :key="index"
                        :cols="cols"
                        :sm="sm"
                        :md="md"
                        :lg="lg"
                        :xl="xl"
                        :xxl="xxl"
                    >
                      <slot
                          name="item"
                          :item="item"
                          :operation="operation"
                      />
                    </v-col>
                  </v-row>
                </v-container>
              </slot>
            </template>
            <template #loader="loaderProps">
              <slot
                  name="loader"
                  v-bind="loaderProps"
              >
                <v-container fluid>
                  <v-row>
                    <v-col
                        v-for="key in itemsPerPage"
                        :key="key"
                        :cols="cols"
                        :sm="sm"
                        :md="md"
                        :lg="lg"
                        :xl="xl"
                        :xxl="xxl"
                    >
                      <slot name="loaderItem">
                        <v-skeleton-loader
                            type="paragraph"
                        />
                      </slot>
                    </v-col>
                  </v-row>
                </v-container>
              </slot>
            </template>
            <template #header="headerProps">
              <slot
                  name="header"
                  v-bind="headerProps"
                  :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"
                    />
                    <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"
                    />
                    <VBtn
                        v-if="props.insertable && !(isReadonly.value || isDisabled.value)"
                        :color="toolbarColor"
                        prepend-icon="mdi:mdi-plus"
                        variant="flat"
                        @click="openDialog()"
                    >
                      add
                    </VBtn>
                    <v-row align="center" class="mr-2 ml-2" v-if="viewSwitch">
                      <v-btn-toggle v-model="viewType" :multiple="viewSwitchMultiple" mandatory>
                        <v-btn icon="mdi mdi-view-grid-outline" value="iterator"></v-btn>
                        <v-btn icon="mdi mdi-table-large" value="table"></v-btn>
                      </v-btn-toggle>
                    </v-row>
                  </VToolbarItems>
                </VToolbar>
              </slot>
              <v-data-table
                  v-bind="plainAttrs"
                  color="primary"
                  :items="items"
                  :search="search"
                  :loading="loading"
                  v-if="viewType.includes('table')"
              >
                <!-- @ts-ignore -->
                <template
                    v-for="(_, name, index) in tableSlots"
                    :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>
            </template>
            <template #footer="footerProps" v-if="viewType.includes('iterator')">
              <v-container fluid>
                <v-row
                    align-content="center"
                    justify="end"
                    density="compact"
                >
                  <v-spacer />
                  <v-select
                      v-model="itemsPerPageInternal"
                      density="compact"
                      variant="outlined"
                      :items="[6, 12, 18, 24, 30, { title: 'All', value: '-1' }]"
                      min-width="220"
                      max-width="220"
                      hide-details
                  >
                    <template #prepend>
                      Items per page:
                    </template>
                  </v-select>
                  <v-pagination
                      density="compact"
                      :length="footerProps.pageCount"
                      total-visible="6"
                      show-first-last-page
                      @first="footerProps.setPage(1)"
                      @last="footerProps.setPage(footerProps.pageCount)"
                      @update:model-value="footerProps.setPage"
                  />
                </v-row>
              </v-container>
            </template >
          </v-data-iterator>
          <FormDialog
              v-model="isDialogOpen"
              :title="title"
              :fullscreen="dialogFullscreen"
              :initial-data="initialData"
              :form-data="currentItem"
              @create="createItem"
              @update="updateItem"
          >
            <template #default="slotData">
              <slot
                  name="form"
                  v-bind="slotData"
              />
            </template>
          </FormDialog>
        </v-card>
      </v-container>
    </template>
  </v-input>
</template>
