<script setup>
import { ref, computed, watch, h } from "vue";
import { router } from "@inertiajs/vue3";
import {
  FlexRender,
  getCoreRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  getFilteredRowModel,
  useVueTable,
} from "@tanstack/vue-table";
import { valueUpdater } from "@/lib/utils";
import DataTableColumnHeader from "./DataTableColumnHeader.vue";
import DataTablePaginationClient from "./DataTablePaginationClient.vue";
import DataTableViewOptions from "./DataTableViewOptions.vue";
import DataTableToolbar from "./DataTableToolbar.vue";
import SkeletonTable from "../Skeleton/SkeletonTable.vue";
import EmptyState from "../EmptyState.vue";
import Pagination from "../Pagination.vue";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/Components/ui/table";
import Checkbox from "@/Components/ui/checkbox/Checkbox.vue";
import { cn } from "@/lib/utils";

const props = defineProps({
  // Column definitions using TanStack Table format or simple format
  columns: {
    type: Array,
    required: true,
  },
  // Data rows
  data: {
    type: Array,
    default: () => [],
  },
  // Server-side pagination meta (Laravel pagination)
  meta: {
    type: Object,
    default: null,
  },
  // Current sort state
  sort: {
    type: Object,
    default: () => ({ key: null, direction: null }),
  },
  // Search/filter value
  search: {
    type: String,
    default: "",
  },
  // Loading state
  loading: {
    type: Boolean,
    default: false,
  },
  // Page size for client-side pagination
  pageSize: {
    type: Number,
    default: 10,
  },
  pageSizeOptions: {
    type: Array,
    default: () => [10, 25, 50, 100],
  },
  // Server-side routing
  routeName: {
    type: String,
    default: null,
  },
  routeParams: {
    type: Object,
    default: () => ({}),
  },
  pageParamName: {
    type: String,
    default: "page",
  },
  perPageParamName: {
    type: String,
    default: "per_page",
  },
  onlyProps: {
    type: Array,
    default: () => [],
  },
  preserveState: {
    type: Boolean,
    default: true,
  },
  preserveScroll: {
    type: Boolean,
    default: true,
  },
  // Features
  showPagination: {
    type: Boolean,
    default: true,
  },
  showToolbar: {
    type: Boolean,
    default: true,
  },
  filterColumn: {
    type: String,
    default: null,
  },
  filterPlaceholder: {
    type: String,
    default: "Filter...",
  },
  rowKey: {
    type: [String, Function],
    default: "id",
  },
  enableRowSelection: {
    type: Boolean,
    default: false,
  },
  striped: {
    type: Boolean,
    default: false,
  },
  hoverable: {
    type: Boolean,
    default: true,
  },
  // Empty state
  emptyText: {
    type: String,
    default: "No results.",
  },
  emptyIcon: {
    type: [String, Object, Array],
    default: null,
  },
  emptyDescription: {
    type: String,
    default: null,
  },
});

const emit = defineEmits([
  "update:search",
  "update:sort",
  "update:page",
  "update:pageSize",
  "row:click",
  "row:select",
  "selection:change",
]);

// Determine if this is server-side mode
const isServerSide = computed(() => !!(props.meta && props.routeName));

// Convert simple column format to TanStack ColumnDef if needed
const columnDefinitions = computed(() => {
  return props.columns.map((col) => {
    // If already a full ColumnDef, return as is
    if (col.accessorKey || col.accessorFn) {
      return col;
    }

    // Convert simple format to ColumnDef
    return {
      accessorKey: col.key,
      id: col.key,
      header: ({ column }) => {
        return h(DataTableColumnHeader, {
          column,
          title: col.label,
          class: col.class,
        });
      },
      cell: ({ row }) => {
        const value = row.getValue(col.key);
        return h("div", { class: col.class }, value);
      },
      enableSorting: col.sortable !== false,
      enableHiding: col.hideable !== false,
      meta: {
        align: col.align || "left",
        class: col.class,
      },
    };
  });
});

// Add selection column if enabled
const columnsWithSelection = computed(() => {
  if (!props.enableRowSelection) return columnDefinitions.value;

  return [
    {
      id: "select",
      header: ({ table }) => {
        return h(Checkbox, {
          modelValue: table.getIsAllPageRowsSelected(),
          indeterminate: table.getIsSomePageRowsSelected(),
          "onUpdate:modelValue": (value) => table.toggleAllPageRowsSelected(!!value),
          "aria-label": "Select all",
        });
      },
      cell: ({ row }) => {
        return h(Checkbox, {
          modelValue: row.getIsSelected(),
          "onUpdate:modelValue": (value) => row.toggleSelected(!!value),
          "aria-label": "Select row",
        });
      },
      enableSorting: false,
      enableHiding: false,
    },
    ...columnDefinitions.value,
  ];
});

// Internal state
const sorting = ref([]);
const columnFilters = ref([]);
const columnVisibility = ref({});
const rowSelection = ref({});

// Client-side pagination state
const clientPagination = ref({
  pageIndex: 0,
  pageSize: props.pageSize,
});

// Initialize sorting from props
watch(
  () => props.sort,
  (newSort) => {
    if (newSort?.key && newSort?.direction) {
      sorting.value = [
        {
          id: newSort.key,
          desc: newSort.direction === "desc",
        },
      ];
    } else {
      sorting.value = [];
    }
  },
  { immediate: true }
);

// Initialize filter from props
watch(
  () => props.search,
  (newSearch) => {
    if (props.filterColumn && newSearch) {
      columnFilters.value = [
        {
          id: props.filterColumn,
          value: newSearch,
        },
      ];
    } else if (!newSearch) {
      columnFilters.value = [];
    }
  },
  { immediate: true }
);

// Pagination state
const pagination = computed(() => {
  if (isServerSide.value) {
    // Check URL for custom per-page parameter
    const urlParams = new URLSearchParams(window.location.search);
    const perPageParam = props.perPageParamName || "per_page";
    const urlPerPage = urlParams.get(perPageParam);
    const pageSize = urlPerPage
      ? Number(urlPerPage)
      : props.meta?.per_page ?? props.pageSize;

    return {
      pageIndex: (props.meta?.current_page ?? 1) - 1,
      pageSize: pageSize,
    };
  }
  return clientPagination.value;
});

// Watch for prop changes to update client pagination
watch(
  () => props.pageSize,
  (newSize) => {
    if (!isServerSide.value) {
      clientPagination.value.pageSize = newSize;
    }
  }
);

// Create TanStack Table
const table = useVueTable({
  get data() {
    return props.data;
  },
  get columns() {
    return columnsWithSelection.value;
  },
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: !isServerSide.value ? getPaginationRowModel() : undefined,
  getSortedRowModel: !isServerSide.value ? getSortedRowModel() : undefined,
  getFilteredRowModel: !isServerSide.value ? getFilteredRowModel() : undefined,
  onSortingChange: (updater) => {
    valueUpdater(updater, sorting);
    const newSort = sorting.value[0];
    if (newSort) {
      emit("update:sort", {
        key: newSort.id,
        direction: newSort.desc ? "desc" : "asc",
      });
      if (isServerSide.value) {
        doServerRequest({
          sort: newSort.id,
          direction: newSort.desc ? "desc" : "asc",
          page: 1,
        });
      }
    } else {
      emit("update:sort", { key: null, direction: null });
      if (isServerSide.value) {
        doServerRequest({ sort: null, direction: null, page: 1 });
      }
    }
  },
  onColumnFiltersChange: (updater) => {
    valueUpdater(updater, columnFilters);
    const filter = columnFilters.value.find((f) => f.id === props.filterColumn);
    const searchValue = filter?.value ?? "";
    emit("update:search", searchValue);
    if (isServerSide.value) {
      clearTimeout(searchTimer.value);
      searchTimer.value = setTimeout(() => {
        doServerRequest({ search: searchValue, page: 1 });
      }, 300);
    }
  },
  onColumnVisibilityChange: (updater) => valueUpdater(updater, columnVisibility),
  onRowSelectionChange: (updater) => {
    valueUpdater(updater, rowSelection);
    const selectedKeys = Object.keys(rowSelection.value).filter(
      (key) => rowSelection.value[key]
    );
    emit("selection:change", selectedKeys);
  },
  onPaginationChange: (updater) => {
    const currentPagination = pagination.value;
    const newPagination =
      typeof updater === "function" ? updater(currentPagination) : updater;

    // Check if page size changed
    const pageSizeChanged = newPagination.pageSize !== currentPagination.pageSize;

    if (isServerSide.value) {
      // If page size changed, go back to page 1
      const targetPage = pageSizeChanged ? 1 : newPagination.pageIndex + 1;
      doServerRequest({
        page: targetPage,
        perPage: newPagination.pageSize,
      });
    } else {
      // Update client-side pagination state
      clientPagination.value = {
        pageIndex: newPagination.pageIndex,
        pageSize: newPagination.pageSize,
      };
    }

    if (pageSizeChanged) {
      emit("update:pageSize", newPagination.pageSize);
    }
    if (newPagination.pageIndex !== currentPagination.pageIndex) {
      emit("update:page", newPagination.pageIndex + 1);
    }
  },
  manualSorting: isServerSide.value,
  manualPagination: isServerSide.value,
  manualFiltering: isServerSide.value,
  enableRowSelection: props.enableRowSelection,
  state: {
    get sorting() {
      return sorting.value;
    },
    get columnFilters() {
      return columnFilters.value;
    },
    get columnVisibility() {
      return columnVisibility.value;
    },
    get rowSelection() {
      return rowSelection.value;
    },
    get pagination() {
      return pagination.value;
    },
  },
});

const searchTimer = ref(null);

// Server-side request handler
function doServerRequest(overrides = {}) {
  if (!props.routeName) return;

  const existingParams = Object.fromEntries(
    new URLSearchParams(window.location.search).entries()
  );

  const perPageParam = props.perPageParamName || "per_page";
  const pageParam = props.pageParamName || "page";

  const q = {
    ...existingParams,
    sort: overrides.sort ?? props.sort?.key ?? null,
    direction: overrides.direction ?? props.sort?.direction ?? null,
    search: overrides.search ?? props.search ?? "",
  };

  // Use custom per_page parameter name
  q[perPageParam] = overrides.perPage ?? props.meta?.per_page ?? props.pageSize;
  if (perPageParam !== "per_page") {
    delete q.per_page;
  }

  // Use custom page parameter name
  q[pageParam] = overrides.page ?? props.meta?.current_page ?? 1;
  if (pageParam !== "page") {
    delete q.page;
  }

  Object.keys(q).forEach((k) => {
    if (q[k] === null || q[k] === undefined || q[k] === "") delete q[k];
  });

  const url = route(props.routeName, props.routeParams || {});
  const onlyProps = Array.isArray(props.onlyProps) ? props.onlyProps : [];
  const inertiaOptions = {
    preserveScroll: props.preserveScroll,
    preserveState: props.preserveState,
    replace: true,
  };

  if (onlyProps.length > 0) {
    inertiaOptions.only = onlyProps;
  }

  router.get(url, q, inertiaOptions);
}

// Row key helper
function keyOf(row) {
  if (typeof props.rowKey === "function") return props.rowKey(row);
  if (typeof props.rowKey === "string" && row && row[props.rowKey] != null)
    return row[props.rowKey];
  return row?.uuid ?? row?.id ?? Math.random().toString(36).slice(2);
}

// Expose methods for parent component
defineExpose({
  clearSelection: () => {
    table.resetRowSelection();
    rowSelection.value = {};
  },
  getSelectedRows: () => {
    return Object.keys(rowSelection.value).filter((key) => rowSelection.value[key]);
  },
});
</script>

<template>
  <div class="w-full">
    <!-- Toolbar -->
    <DataTableToolbar
      v-if="showToolbar"
      :table="table"
      :filter-column="filterColumn"
      :filter-placeholder="filterPlaceholder"
      :show-per-page-selector="isServerSide"
      :per-page="pagination.pageSize"
      :page-size-options="pageSizeOptions"
      @update:per-page="(value) => table.setPageSize(value)"
      class="p-2 border-t"
    >
      <template #filters="slotProps">
        <slot name="toolbar-filters" v-bind="slotProps" />
      </template>
      <template #actions="slotProps">
        <slot name="toolbar-actions" v-bind="slotProps" />
      </template>
    </DataTableToolbar>

    <!-- Custom toolbar slot for full control -->
    <slot name="toolbar" :table="table" />

    <!-- Table -->
    <div class="border-t">
      <Table>
        <TableHeader>
          <TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
            <TableHead
              v-for="header in headerGroup.headers"
              :key="header.id"
              :class="[
                'p-3',
                header.column.columnDef.meta?.class,
                header.column.columnDef.meta?.align === 'right'
                  ? 'text-right'
                  : header.column.columnDef.meta?.align === 'center'
                  ? 'text-center'
                  : 'text-left',
                'bg-muted/50',
              ]"
            >
              <FlexRender
                v-if="!header.isPlaceholder"
                :render="header.column.columnDef.header"
                :props="header.getContext()"
              />
            </TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          <!-- Loading State -->
          <template v-if="loading">
            <TableRow>
              <TableCell :colspan="table.getAllColumns().length" class="h-24 text-center">
                <SkeletonTable :rows="5" :cols="columns.length" />
              </TableCell>
            </TableRow>
          </template>

          <!-- Empty State -->
          <template v-else-if="table.getRowModel().rows.length === 0">
            <TableRow>
              <TableCell :colspan="table.getAllColumns().length" class="h-24 text-center">
                <EmptyState
                  :icon="emptyIcon"
                  :title="emptyText"
                  :description="emptyDescription"
                  size="sm"
                />
              </TableCell>
            </TableRow>
          </template>

          <!-- Data Rows -->
          <template v-else>
            <TableRow
              v-for="row in table.getRowModel().rows"
              :key="keyOf(row.original)"
              :data-state="row.getIsSelected() && 'selected'"
              :class="
                cn(
                  hoverable && 'cursor-pointer hover:bg-muted/50',
                  striped && row.index % 2 === 1 && 'bg-muted/50'
                )
              "
              @click="$emit('row:click', row.original, row.index)"
            >
              <TableCell
                v-for="cell in row.getVisibleCells()"
                :key="cell.id"
                :class="[
                  cell.column.columnDef.meta?.class,
                  cell.column.columnDef.meta?.align === 'right'
                    ? 'text-right'
                    : cell.column.columnDef.meta?.align === 'center'
                    ? 'text-center'
                    : 'text-left',
                  'p-3',
                ]"
              >
                <!-- Use slot if provided -->
                <slot
                  :name="`cell-${cell.column.id}`"
                  :row="row.original"
                  :column="cell.column"
                  :value="cell.getValue()"
                  :index="row.index"
                >
                  <!-- Otherwise use FlexRender -->
                  <FlexRender
                    :render="cell.column.columnDef.cell"
                    :props="cell.getContext()"
                  />
                </slot>
              </TableCell>
            </TableRow>
          </template>
        </TableBody>
      </Table>
    </div>

    <!-- Pagination -->
    <div v-if="showPagination" class="border-t border-gray-200 p-4">
      <!-- Server-side pagination -->
      <template v-if="isServerSide && meta?.links">
        <Pagination
          :links="meta.links"
          :from="meta.from"
          :to="meta.to"
          :total="meta.total"
          :current-page="meta.current_page"
          :last-page="meta.last_page"
          :per-page="meta.per_page"
          :page-param="pageParamName"
          :per-page-param="perPageParamName"
        />
      </template>

      <!-- Client-side pagination -->
      <template v-else>
        <DataTablePaginationClient
          :current-page="table.getState().pagination.pageIndex"
          :last-page="table.getPageCount()"
          :total="table.getFilteredRowModel().rows.length"
          :showing-from="table.getFilteredSelectedRowModel().rows.length"
          :showing-to="table.getFilteredRowModel().rows.length"
          :table="table"
        />
      </template>
    </div>
  </div>
</template>
