<template>
  <UDashboardPanel
    :ui="{ body: 'border-b border-default relative' }"
    :class="ui.root({ class: [props.ui?.root, props.class] })"
  >
    <template #header>
      <UDashboardNavbar :title="title">
        <template #right>
          <slot name="navbar-right" />
        </template>
      </UDashboardNavbar>
      <UDashboardToolbar>
        <template #left>
          <slot name="toolbar-left" />
        </template>
        <template #right>
          <slot name="toolbar-right" />

          <CTableColumnVisibility
            v-model:visibility="columnVisibility"
            v-model:order="columnOrder"
            :table="tableRef?.tableApi"
            :storage-key="storageKey"
          />
          <CTableColumnSorting
            :model-value="sorting"
            :table="tableRef?.tableApi"
            @update:model-value="emit('update:sorting', $event)"
          />
          <CTableFilters
            v-if="props.filtersSource"
            :model-value="props.filters"
            :schema="props.filtersSchema"
            :source="props.filtersSource"
            :fields="props.filtersFields"
            @update:model-value="emit('update:filters', $event)"
            @reset="emit('filtersReset', $event)"
            @submit="emit('filtersSubmit', $event)"
          >
            <template
              v-for="(filterSlot, pageTableSlot) in getFiltersSlots(slots)"
              #[filterSlot]="slotData"
            >
              <slot :name="pageTableSlot" v-bind="slotData" />
            </template>
          </CTableFilters>
        </template>
      </UDashboardToolbar>
    </template>
    <template #body>
      <div v-show="status === 'pending'" :class="ui.loader({ class: props.ui?.loader })">
        <UIcon :name="appConfig.ui.icons.loading" :class="ui.loaderIcon({ class: props.ui?.loaderIcon })" />
      </div>

      <UTable
        ref="table"
        v-model:column-visibility="columnVisibility"
        v-model:column-order="columnOrder"
        :class="ui.table({ class: props.ui?.table })"
        :ui="{
  base: 'table-fixed border-separate border-spacing-0',
  thead: '[&>tr]:bg-elevated/50 [&>tr]:after:content-none',
  tbody: '[&>tr]:last:[&>td]:border-b-0',
  th: 'py-2 first:rounded-l-lg last:rounded-r-lg border-y border-default first:border-l last:border-r',
  td: 'border-b border-default'
}"
        :loading="status === 'pending'"
        :row-selection="rowSelection"
        :row-selection-options="rowSelectionOptions"
        :column-pinning="columnPinningWrap"
        :columns="tableColumns"
        :data="data"
        :sorting-options="{ manualSorting: hasPagination }"
        :sorting="sorting"
        :sticky="sticky"
        :get-row-id="getRowId"
        :grouping="grouping"
        :grouping-options="groupingOptions"
        :column-filters="columnFilters"
        :empty="empty"
        @select="onSelect"
        @update:row-selection="emit('update:row-selection', $event)"
        @update:grouping="emit('update:grouping', $event)"
      >
        <template v-for="(_, name) in slots" #[name]="slotData">
          <slot :name="name" v-bind="slotData" />
        </template>
      </UTable>
    </template>

    <template v-if="hasPagination" #footer>
      <div class="flex justify-between items-center py-3 px-4">
        <div>
          <span class="text-sm text-muted">
            Всего: {{ total ?? 0 }}
          </span>
        </div>
        <div class="flex items-center gap-2">
          <USelect
            :model-value="itemsPerPage"
            :items="itemsPerPageItems"
            :ui="{ base: 'text-muted' }"
            variant="subtle"
            @update:model-value="emit('update:items-per-page', $event)"
          >
            <template #default="{ modelValue }">
              {{ modelValue }} / на страницу
            </template>

            <template #item="{ item }">
              {{ item }}
            </template>
          </USelect>

          <UPagination
            v-bind="paginationProps"
            :ui="isTotalUnknown ? { first: 'hidden', last: 'hidden' } : void 0"
            variant="subtle"
            :sibling-count="isTotalUnknown ? 3 : 1"
            :show-edges="!isTotalUnknown"
            @update:page="emit('update:page', $event)"
          />
        </div>
      </div>
    </template>
  </UDashboardPanel>
</template>

<script>
import theme from "#build/cms/table-panel";
import { UButton, UCheckbox, UDropdownMenu, UTable } from "#components";
import { useAppConfig, useOverlay } from "#imports";
import { getGroupedRowModel } from "@tanstack/vue-table";
import { reactivePick } from "@vueuse/core";
import { defu } from "defu";
import { useForwardProps } from "reka-ui";
import { computed, h, ref, useTemplateRef, watch } from "vue";
import { tv } from "../tv";
import CModalConfirm from "./ModalConfirm.vue";
import CTableColumnSorting from "./TableColumnSorting.vue";
import CTableColumnVisibility from "./TableColumnVisibility.vue";
import CTableFilters from "./TableFilters.vue";
</script>

<script setup>
const props = defineProps({
  title: { type: String, required: false },
  status: { type: String, required: false },
  page: { type: Number, required: false },
  itemsPerPage: { type: Number, required: false },
  total: { type: Number, required: false },
  storageKey: { type: String, required: false },
  columnVisibility: { type: Boolean, required: false, default: true },
  defaultHiddenColumns: { type: Array, required: false },
  sorting: { type: Array, required: false },
  columns: { type: Array, required: false },
  data: { type: Array, required: false },
  sticky: { type: [Boolean, String], required: false, default: true },
  columnPinning: { type: Object, required: false },
  getRowId: { type: Function, required: false },
  rowSelection: { type: Object, required: false },
  rowSelectionOptions: { type: Object, required: false },
  resetRowSelection: { type: Boolean, required: false, default: true },
  grouping: { type: Array, required: false },
  groupingOptions: { type: Object, required: false, default: () => ({ getGroupedRowModel: getGroupedRowModel(), groupedColumnMode: false }) },
  hideSelectedFilters: { type: Boolean, required: false },
  filters: { type: null, required: false },
  filtersSchema: { type: null, required: false },
  filtersSource: { type: null, required: false },
  filtersFields: { type: null, required: false },
  empty: { type: String, required: false },
  actions: { type: Function, required: false },
  onSelect: { type: Function, required: false },
  class: { type: null, required: false },
  ui: { type: null, required: false }
});
const emit = defineEmits(["update:page", "update:items-per-page", "update:sorting", "update:row-selection", "update:filters", "update:grouping", "filtersReset", "filtersSubmit"]);
const slots = defineSlots();
const appConfig = useAppConfig();
const paginationProps = useForwardProps(
  reactivePick(props, "page", "itemsPerPage", "total")
);
const tableRef = useTemplateRef("table");
watch(() => props.resetRowSelection && props.data, (value) => {
  value && tableRef.value?.tableApi.resetRowSelection();
});
watch(() => props.data, () => {
  tableRef.value?.tableRef?.parentElement?.scrollTo({ top: 0 });
});
const modalConfirm = useOverlay().create(CModalConfirm);
function createActionCell(action) {
  if (action.visibility && !action.visibility()) {
    return;
  }
  if (action.confirm) {
    return {
      ...action,
      onClick: () => modalConfirm.open(action.confirm)
    };
  }
  if (action.delete) {
    return {
      ...action,
      color: "error",
      onClick: () => modalConfirm.open(defu(action.delete, {
        title: "\u0423\u0434\u0430\u043B\u0435\u043D\u0438\u0435",
        message: "\u0412\u044B \u0434\u0435\u0438\u0306\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u0445\u043E\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043B\u0438\u0442\u044C \u044D\u043B\u0435\u043C\u0435\u043D\u0442?",
        color: "error"
      }))
    };
  }
  return action;
}
const tableColumns = computed(() => {
  const columns = props.columns ? [...props.columns] : Object.keys(props.data?.[0] ?? {}).map((accessorKey) => ({ accessorKey, header: accessorKey }));
  if (props.actions) {
    columns.push({
      id: "actions",
      enableHiding: false,
      meta: {
        class: {
          th: "w-20",
          td: "text-right"
        }
      },
      cell: ({ row }) => {
        const items = props.actions?.(row)?.map(createActionCell).filter((item) => item);
        if (!items?.length) {
          return;
        }
        return h(
          UDropdownMenu,
          {
            items,
            content: { align: "end" }
          },
          () => h(UButton, {
            icon: appConfig.ui.icons.ellipsisVertical,
            color: "neutral",
            variant: "ghost"
          })
        );
      }
    });
  }
  if (props.rowSelection) {
    columns.unshift({
      id: "select",
      header: ({ table }) => h(UCheckbox, {
        "modelValue": table.getIsSomeRowsSelected() ? "indeterminate" : table.getIsAllPageRowsSelected(),
        "onUpdate:modelValue": (value) => table.toggleAllPageRowsSelected(!!value)
      }),
      cell: ({ row }) => h(UCheckbox, {
        "modelValue": row.getIsSelected(),
        "disabled": !row.getCanSelect(),
        "onUpdate:modelValue": (value) => row.toggleSelected(!!value)
      }),
      enableHiding: false
    });
  }
  return columns;
});
const columnVisibility = ref();
watch(() => props.defaultHiddenColumns, (value) => {
  columnVisibility.value = Object.fromEntries(
    value?.map((column) => [column, false]) ?? []
  );
}, { immediate: true });
const columnOrder = ref([]);
const hasPagination = computed(() => props.page !== void 0);
const itemsPerPageItems = [20, 50, 100, 200];
const isTotalUnknown = computed(() => (props.total ?? 0) >= 1e6);
const columnPinningWrap = computed(() => {
  return defu(props.columnPinning, { right: props.actions ? ["actions"] : [] });
});
const columnFilters = computed(() => {
  if (hasPagination.value || !props.filters) {
    return;
  }
  return Object.entries(props.filters).flatMap(([id, value]) => {
    if (value === void 0) {
      return [];
    }
    return [{ id, value }];
  });
});
function getFiltersSlots(slots2) {
  return Object.keys(slots2).reduce((acc, key) => {
    if (key.startsWith("filter-")) {
      acc[key] = key.replace("filter-", "");
    }
    return acc;
  }, {});
}
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.tablePanel || {} })());
</script>
