<script setup>
import { computed, nextTick, ref, markRaw, useAttrs, useSlots, watch } from "vue";
import { useModelAutoRefresh } from "../../composables/modelAutoRefresh";
import { VDataIterator } from "vuetify/components/VDataIterator";
import { VDataTable } from "vuetify/components/VDataTable";
import { omit } from "lodash-es";
import { useGraphqlModel } from "../../composables/graphqlModel";
import { useApiModel } from "../../composables/apiModel";
import { usePerPagePreference } from "../../composables/perPagePreference";
import { useDisplay } from "vuetify";
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" },
  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: false },
  exportable: { type: Boolean, required: false, default: false },
  insertable: { type: Boolean, required: false, default: true },
  searchable: { type: Boolean, required: false, default: true },
  search: { type: String, required: 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 },
  api: { type: Boolean, required: false, default: false },
  perPageStorageKey: { type: String, required: false },
  perPageStorageEnabled: { type: Boolean, required: false, default: true },
  autoRefresh: { type: [Number, Boolean], required: false, default: false },
  autoRefreshDefault: { type: Number, required: false, default: 60 },
  autoRefreshControl: { type: Boolean, required: false, default: true },
  modelName: { type: String, required: true },
  modelKey: { type: String, required: false, default: "id" },
  modelBy: { type: Object, required: false, default: void 0 },
  operationCreate: { type: [Object, String], required: false },
  operationUpdate: { type: [Object, String], required: false },
  operationDelete: { type: [Object, String], required: false },
  operationRead: { type: [Object, String], required: false },
  operationReadPageable: { type: [Object, String], required: false },
  operationSearch: { type: [Object, String], required: false },
  fields: { type: Array, required: false, default: () => [] },
  headers: { type: Array, required: false }
});
const attrs = useAttrs();
const plainAttrs = computed(() => {
  const returnAttrs = omit(attrs, ["modelValue", "onUpdate:modelValue"]);
  if (props.headers) returnAttrs["headers"] = props.headers;
  return returnAttrs;
});
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 currentItem = ref(void 0);
const isDialogOpen = ref(false);
const {
  items,
  itemsLength,
  search,
  setSearch,
  currentOptions,
  canServerPageable,
  canServerSearch,
  canCreate,
  canUpdate,
  canDelete,
  createItem,
  importItems,
  updateItem,
  deleteItem,
  loadItems,
  reload,
  isLoading
} = props.api ? useApiModel(props) : useGraphqlModel(props);
const { autoRefresh, manualReload } = useModelAutoRefresh(props, {
  reload,
  isLoading,
  isDialogOpen
});
function openDialog(item) {
  currentItem.value = item;
  nextTick(() => {
    isDialogOpen.value = 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 ?? props.modelName,
  enabled: props.perPageStorageEnabled
});
const sortBy = ref();
const pageCount = computed(() => {
  if (!itemsPerPageInternal.value || itemsPerPageInternal.value == "All" || itemsPerPageInternal.value == "-1" || Number(itemsPerPageInternal.value) <= 0) return 1;
  else return Math.ceil(itemsLength.value / Number(itemsPerPageInternal.value));
});
const currentPage = ref(1);
watch([currentPage, itemsPerPageInternal, sortBy], () => {
  if (canServerPageable.value) {
    loadItems({
      page: currentPage.value,
      itemsPerPage: !itemsPerPageInternal.value || itemsPerPageInternal.value == "All" || Number(itemsPerPageInternal.value) <= 0 ? "-1" : itemsPerPageInternal.value,
      sortBy: sortBy.value
    });
  }
}, { immediate: true });
watch(
  [itemsLength, 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 operation = ref({ openDialog, createItem, importItems, updateItem, deleteItem, reload, setSearch, canServerPageable, canServerSearch, canCreate, canUpdate, canDelete, autoRefresh: markRaw(autoRefresh) });
const computedInitialData = computed(() => {
  return Object.assign({}, props.initialData, props.modelBy);
});
const computedSkeletonPerPage = computed(() => {
  if (!itemsPerPageInternal.value || itemsPerPageInternal.value == "All" || Number(itemsPerPageInternal.value) <= 0) return 1;
  else return Number(itemsPerPageInternal.value);
});
watch(() => props.search, () => {
  search.value = props.search;
}, { immediate: true });
defineExpose({ reload, operation, autoRefresh });
</script>

<template>
  <v-card>
    <v-data-iterator
      v-bind="plainAttrs"
      v-model:items-per-page="itemsPerPageInternal"
      v-model:sort-by="sortBy"
      :items="items"
      :item-value="modelKey"
      :search="search"
      :loading="isLoading"
    >
      <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" v-if="viewType.includes('iterator')">
        <slot
          name="loader"
          v-bind="loaderProps"
        >
          <v-container fluid>
            <v-row>
              <v-col
                v-for="key in computedSkeletonPerPage"
                :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"
                    :reload="manualReload"
                  >
                    {{ title }}
                    <v-icon
                      size="small"
                      @click="manualReload"
                    >
                      mdi mdi-refresh
                    </v-icon>
                  </slot>
                  <slot
                    name="autoRefreshControl"
                    v-bind="autoRefresh"
                  >
                    <ModelAutoRefreshChip
                      v-if="props.autoRefreshControl"
                      :control="autoRefresh"
                      :color="toolbarColor"
                    />
                  </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 && canCreate && props.insertable"
                  icon="mdi mdi-file-upload"
                  variant="flat"
                  :color="toolbarColor"
                  @import="importItems"
              />
              <ExportCSV
                  v-if="props.exportable && items.length"
                  icon="mdi mdi-file-download"
                  variant="flat"
                  :file-name="title"
                  :model-value="items"
                  :color="toolbarColor"
              />
              <VBtn
                v-if="canCreate && props.insertable"
                :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">
                  <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>
        <template v-if="viewType.includes('table')">
          <v-data-table-server
              v-if="canServerPageable"
              v-bind="plainAttrs"
              color="primary"
              :items="items"
              :items-length="itemsLength"
              :item-value="props.modelKey"
              :search="search"
              :loading="isLoading"
              @update:options="loadItems"
          >
            <!-- @ts-ignore -->
            <template
                v-for="(_, name, index) in tableSlots"
                :key="index"
                #[name]="slotData"
            >
              <slot
                  :name="name"
                  v-bind="slotData || {}"
                  :operation="operation"
              />
            </template>
            <template
                v-if="!$slots['item.action']"
                #item.action="{ item }"
            >
              <v-btn
                  v-if="canUpdate"
                  variant="flat"
                  density="compact"
                  icon="mdi mdi-note-edit"
                  @click="openDialog(item)"
              />
              <v-btn
                  v-if="canDelete"
                  variant="flat"
                  density="compact"
                  icon="mdi mdi-delete"
                  @click="deleteItem(item)"
              />
            </template>
          </v-data-table-server>
          <v-data-table
              v-else
              v-bind="plainAttrs"
              color="primary"
              :items="items"
              :item-value="props.modelKey"
              :search="search"
              :loading="isLoading"
          >
            <!-- @ts-ignore -->
            <template
                v-for="(_, name, index) in tableSlots"
                :key="index"
                #[name]="slotData"
            >
              <slot
                  :name="name"
                  v-bind="slotData || {}"
                  :operation="operation"
              />
            </template>
            <template
                v-if="!$slots['item.action']"
                #item.action="{ item }"
            >
              <v-btn
                  v-if="canUpdate"
                  variant="flat"
                  density="compact"
                  icon="mdi mdi-note-edit"
                  @click="openDialog(item)"
              />
              <v-btn
                  v-if="canDelete"
                  variant="flat"
                  density="compact"
                  icon="mdi mdi-delete"
                  @click="deleteItem(item)"
              />
            </template>
          </v-data-table>
        </template>
      </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
              v-if="!canServerPageable"
              v-model="currentPage"
              density="compact"
              :length="footerProps.pageCount"
              total-visible="6"
              show-first-last-page
              @first="footerProps.setPage(1)"
              @last="footerProps.setPage(footerProps.pageCount)"
              @next="footerProps.nextPage"
              @prev="footerProps.prevPage"
              @update:model-value="footerProps.setPage"
            />
            <v-pagination
              v-else
              v-model="currentPage"
              density="compact"
              :length="pageCount"
              total-visible="6"
              show-first-last-page
            />
          </v-row>
        </v-container>
      </template>
    </v-data-iterator>
    <FormDialog
      v-model="isDialogOpen"
      :title="title"
      :fullscreen="dialogFullscreen"
      :initial-data="computedInitialData"
      :form-data="currentItem"
      @create="createItem"
      @update="updateItem"
    >
      <template #default="slotData">
        <slot
          name="form"
          v-bind="slotData"
        />
      </template>
    </FormDialog>
  </v-card>
</template>
