<script setup>
import { ref, computed, watch, h, useSlots } from "vue";
import { router } from "@inertiajs/vue3";
import {
  useVueTable,
  getCoreRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  getFilteredRowModel,
} from "@tanstack/vue-table";
import DataTableColumnHeader from "./DataTableColumnHeader.vue";
import DataTablePagination from "./DataTablePagination.vue";
import DataTableViewOptions from "./DataTableViewOptions.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 slots = useSlots();

const props = defineProps({
  // Data
  rows: { type: Array, default: () => [] },
  columns: {
    type: Array,
    required: true,
    validator: (cols) =>
      cols.every(
        (col) =>
          col.key &&
          col.label &&
          typeof col.key === "string" &&
          typeof col.label === "string"
      ),
  },

  // Pagination (for server-side)
  meta: {
    type: Object,
    default: null,
    validator: (meta) =>
      !meta ||
      (typeof meta.current_page === "number" &&
        typeof meta.per_page === "number" &&
        typeof meta.total === "number" &&
        typeof meta.last_page === "number"),
  },

  // Sorting
  sort: {
    type: Object,
    default: () => ({ key: null, direction: null }),
  },

  // Search
  search: { type: String, default: "" },

  // Loading state
  loading: { type: Boolean, default: false },

  // Client-side pagination (when meta is null)
  pageSize: { type: Number, default: 10 },
  pageSizeOptions: { type: Array, default: () => [10, 25, 50, 100] },

  // Routing (for server-side)
  routeName: { type: String, default: null },
  routeParams: { type: Object, default: () => ({}) },
  pageParamName: { type: String, default: "page" },
  onlyProps: { type: Array, default: () => [] },

  // Features
  showPagination: { type: Boolean, default: true },
  showViewOptions: { type: Boolean, default: false },
  rowKey: { type: [String, Function], default: "uuid" },
  selectable: { type: Boolean, default: false },
  striped: { type: Boolean, default: false },
  hoverable: { type: Boolean, default: true },

  // Empty state
  emptyText: { type: String, default: "Ni podatkov" },
  emptyIcon: { type: [String, Object, Array], default: null },
  emptyDescription: { type: String, default: null },

  // Actions
  showActions: { type: Boolean, default: false },
  actionsPosition: {
    type: String,
    default: "right",
    validator: (v) => ["left", "right"].includes(v),
  },

  // Mobile
  mobileCardView: { type: Boolean, default: true },
  mobileBreakpoint: { type: Number, default: 768 },

  // State preservation
  preserveState: { type: Boolean, default: true },
  preserveScroll: { type: Boolean, default: true },
});

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

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

// 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);
}

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

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

  return [
    {
      id: "select",
      enableHiding: false,
      enableSorting: false,
      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",
        });
      },
    },
    ...columnDefinitions.value,
  ];
});

// Add actions column if showActions
const finalColumns = computed(() => {
  if (!props.showActions && !slots.actions) return columnsWithSelection.value;

  return [
    ...columnsWithSelection.value,
    {
      id: "actions",
      enableHiding: false,
      enableSorting: false,
      header: () => h("span", { class: "sr-only" }, "Actions"),
      cell: ({ row }) => {
        // Actions will be rendered via slot
        return null;
      },
    },
  ];
});

// Internal search state
const internalSearch = ref(props.search);
watch(
  () => props.search,
  (newVal) => {
    internalSearch.value = newVal;
  }
);

// Internal sorting state
const sorting = computed(() => {
  if (!props.sort?.key || !props.sort?.direction) return [];
  return [
    {
      id: props.sort.key,
      desc: props.sort.direction === "desc",
    },
  ];
});

// Internal pagination state
const pagination = computed(() => {
  if (isServerSide.value) {
    return {
      pageIndex: (props.meta?.current_page ?? 1) - 1,
      pageSize: props.meta?.per_page ?? props.pageSize,
    };
  }
  return {
    pageIndex: internalPage.value - 1,
    pageSize: internalPageSize.value,
  };
});

const internalPage = ref(1);
const internalPageSize = ref(props.pageSize);

// Row selection
const rowSelection = ref({});

// Create TanStack Table instance
const table = useVueTable({
  get data() {
    return props.rows;
  },
  get columns() {
    return finalColumns.value;
  },
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: isClientSide.value ? getPaginationRowModel() : undefined,
  getSortedRowModel: isClientSide.value ? getSortedRowModel() : undefined,
  getFilteredRowModel: isClientSide.value ? getFilteredRowModel() : undefined,
  globalFilterFn: "includesString",
  onGlobalFilterChange: (updater) => {
    const newFilter =
      typeof updater === "function" ? updater(internalSearch.value) : updater;
    handleSearchChange(newFilter);
  },
  onSortingChange: (updater) => {
    const newSorting = typeof updater === "function" ? updater(sorting.value) : updater;
    if (newSorting.length > 0) {
      const sort = newSorting[0];
      emit("update:sort", {
        key: sort.id,
        direction: sort.desc ? "desc" : "asc",
      });
      if (isServerSide.value) {
        doServerRequest({
          sort: sort.id,
          direction: sort.desc ? "desc" : "asc",
          page: 1,
        });
      }
    } else {
      emit("update:sort", { key: null, direction: null });
      if (isServerSide.value) {
        doServerRequest({ sort: null, direction: null, page: 1 });
      }
    }
  },
  onPaginationChange: (updater) => {
    const newPagination =
      typeof updater === "function" ? updater(pagination.value) : updater;
    if (isServerSide.value) {
      doServerRequest({ page: newPagination.pageIndex + 1 });
    } else {
      internalPage.value = newPagination.pageIndex + 1;
      emit("update:page", newPagination.pageIndex + 1);
    }
    internalPageSize.value = newPagination.pageSize;
    emit("update:pageSize", newPagination.pageSize);
  },
  onRowSelectionChange: (updater) => {
    const newSelection =
      typeof updater === "function" ? updater(rowSelection.value) : updater;
    rowSelection.value = newSelection;
    const selectedKeys = Object.keys(newSelection).filter((key) => newSelection[key]);
    emit("selection:change", selectedKeys);
  },
  manualSorting: isServerSide.value,
  manualPagination: isServerSide.value,
  manualFiltering: isServerSide.value,
  enableRowSelection: props.selectable,
  state: {
    get sorting() {
      return sorting.value;
    },
    get pagination() {
      return pagination.value;
    },
    get rowSelection() {
      return rowSelection.value;
    },
    get globalFilter() {
      return internalSearch.value;
    },
  },
});

// Server-side request
function doServerRequest(overrides = {}) {
  const existingParams = Object.fromEntries(
    new URLSearchParams(window.location.search).entries()
  );

  const q = {
    ...existingParams,
    per_page: overrides.perPage ?? props.meta?.per_page ?? internalPageSize.value,
    sort: overrides.sort ?? props.sort?.key ?? existingParams.sort ?? null,
    direction:
      overrides.direction ?? props.sort?.direction ?? existingParams.direction ?? null,
    search: overrides.search ?? internalSearch.value ?? existingParams.search ?? "",
  };

  const pageParam = props.pageParamName || "page";
  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 || {});
  router.get(url, q, {
    preserveScroll: props.preserveScroll,
    preserveState: props.preserveState,
    replace: true,
    only: props.onlyProps.length ? props.onlyProps : undefined,
  });
}

function handleSearchChange(value) {
  internalSearch.value = value;
  emit("update:search", value);

  if (isServerSide.value) {
    clearTimeout(searchTimer.value);
    searchTimer.value = setTimeout(() => {
      doServerRequest({ search: value, page: 1 });
    }, 300);
  }
}

function handlePageSizeChange(size) {
  const newSize = Number(size);
  internalPageSize.value = newSize;
  emit("update:pageSize", newSize);

  if (isServerSide.value) {
    doServerRequest({ perPage: newSize, page: 1 });
  } else {
    table.setPageSize(newSize);
  }
}

const searchTimer = ref(null);

// Mobile detection
const isMobile = ref(false);
if (typeof window !== "undefined") {
  const checkMobile = () => {
    isMobile.value = window.innerWidth < props.mobileBreakpoint;
  };
  checkMobile();
  window.addEventListener("resize", checkMobile);
}

// Display rows
const displayRows = computed(() => {
  if (isServerSide.value) return props.rows;
  return table.getRowModel().rows.map((row) => row.original);
});

const total = computed(() => {
  if (isServerSide.value) return props.meta?.total ?? 0;
  return table.getFilteredRowModel().rows.length;
});

const from = computed(() => {
  if (isServerSide.value) return props.meta?.from ?? 0;
  const pageIndex = table.getState().pagination.pageIndex;
  const pageSize = table.getState().pagination.pageSize;
  return total.value === 0 ? 0 : pageIndex * pageSize + 1;
});

const to = computed(() => {
  if (isServerSide.value) return props.meta?.to ?? 0;
  const pageIndex = table.getState().pagination.pageIndex;
  const pageSize = table.getState().pagination.pageSize;
  return Math.min((pageIndex + 1) * pageSize, total.value);
});

// Export functionality
function handleExport(format) {
  const data = displayRows.value.map((row) => {
    const exported = {};
    props.columns.forEach((col) => {
      exported[col.label] = row?.[col.key] ?? "";
    });
    return exported;
  });

  if (format === "csv") {
    exportToCSV(data);
  } else if (format === "xlsx") {
    exportToXLSX(data);
  }
}

function exportToCSV(data) {
  if (data.length === 0) return;

  const headers = Object.keys(data[0]);
  const csvContent = [
    headers.join(","),
    ...data.map((row) =>
      headers
        .map((header) => {
          const value = row[header];
          if (value == null) return "";
          const stringValue = String(value).replace(/"/g, '""');
          return `"${stringValue}"`;
        })
        .join(",")
    ),
  ].join("\n");

  const blob = new Blob(["\uFEFF" + csvContent], { type: "text/csv;charset=utf-8;" });
  const link = document.createElement("a");
  const url = URL.createObjectURL(blob);
  link.setAttribute("href", url);
  link.setAttribute("download", `export_${new Date().toISOString().split("T")[0]}.csv`);
  link.style.visibility = "hidden";
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}

function exportToXLSX(data) {
  exportToCSV(data);
}

// Expose table instance and utilities for toolbar usage
defineExpose({
  table,
  internalSearch,
  internalPageSize,
  rowSelection,
  handleSearchChange,
  handlePageSizeChange,
  handleExport,
  exportToCSV,
  exportToXLSX,
});
</script>

<template>
  <div class="w-full space-y-4">
    <!-- Toolbar Slot - Users can build their own toolbar using the table instance -->
    <slot
      name="toolbar"
      :table="table"
      :search="internalSearch"
      :page-size="internalPageSize"
      :row-selection="rowSelection"
    />

    <!-- View Options -->
    <div v-if="showViewOptions" class="flex items-center space-x-2">
      <DataTableViewOptions :table="table" />
    </div>

    <!-- Table Container -->
    <div data-table-container class="relative overflow-hidden">
      <!-- Desktop Table View -->
      <div v-if="!isMobile || !mobileCardView" class="overflow-x-auto">
        <Table>
          <TableHeader class="p-4">
            <TableRow
              v-for="headerGroup in table.getHeaderGroups()"
              :key="headerGroup.id"
            >
              <TableHead
                v-for="header in headerGroup.headers"
                :key="header.id"
                :class="[
                  header.column.columnDef.meta?.class,
                  header.column.columnDef.meta?.align === 'right'
                    ? 'text-right'
                    : header.column.columnDef.meta?.align === 'center'
                    ? 'text-center'
                    : 'text-left',
                ]"
              >
                <div v-if="!header.isPlaceholder">
                  <component :is="header.column.columnDef.header(header.getContext())" />
                </div>
              </TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            <!-- Loading State -->
            <template v-if="loading">
              <TableRow>
                <TableCell
                  :colspan="
                    columns.length +
                    (selectable ? 1 : 0) +
                    (showActions || slots.actions ? 1 : 0)
                  "
                  class="h-24 text-center"
                >
                  <SkeletonTable :rows="5" :cols="columns.length" />
                </TableCell>
              </TableRow>
            </template>

            <!-- Empty State -->
            <template v-else-if="!loading && table.getRowModel().rows.length === 0">
              <TableRow>
                <TableCell
                  :colspan="
                    columns.length +
                    (selectable ? 1 : 0) +
                    (showActions || slots.actions ? 1 : 0)
                  "
                  class="h-24 text-center"
                >
                  <EmptyState
                    :icon="emptyIcon"
                    :title="emptyText"
                    :description="emptyDescription"
                    size="sm"
                  />
                </TableCell>
              </TableRow>
            </template>

            <!-- Rows -->
            <template v-else>
              <TableRow
                v-for="row in table.getRowModel().rows"
                :key="row.id"
                :data-state="row.getIsSelected() && 'selected'"
                :class="
                  cn(
                    hoverable && 'cursor-pointer',
                    striped && row.index % 2 === 1 && 'bg-muted/50'
                  )
                "
                @click="
                  (e) => {
                    const interactive = e.target.closest(
                      'button, a, [role=button], [data-dropdown], .relative, input[type=checkbox]'
                    );
                    if (interactive) return;
                    $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',
                  ]"
                >
                  <template v-if="cell.column.id === 'actions'">
                    <slot name="actions" :row="row.original" :index="row.index">
                      <slot name="row-actions" :row="row.original" :index="row.index" />
                    </slot>
                  </template>
                  <template v-else>
                    <slot
                      :name="`cell-${cell.column.id}`"
                      :row="row.original"
                      :column="cell.column.columnDef"
                      :value="cell.getValue()"
                      :index="row.index"
                    >
                      <slot
                        name="cell"
                        :row="row.original"
                        :column="cell.column.columnDef"
                        :value="cell.getValue()"
                        :index="row.index"
                      >
                        <component :is="cell.column.columnDef.cell(cell.getContext())" />
                      </slot>
                    </slot>
                  </template>
                </TableCell>
              </TableRow>
            </template>
          </TableBody>
        </Table>
      </div>

      <!-- Mobile Card View -->
      <div v-else-if="isMobile && mobileCardView" class="divide-y divide-gray-200">
        <template v-if="loading">
          <div class="p-4">
            <SkeletonTable :rows="3" :cols="1" />
          </div>
        </template>

        <template v-else-if="!loading && table.getRowModel().rows.length === 0">
          <div class="p-6">
            <EmptyState
              :icon="emptyIcon"
              :title="emptyText"
              :description="emptyDescription"
              size="sm"
            />
          </div>
        </template>

        <template v-else>
          <div
            v-for="row in table.getRowModel().rows"
            :key="row.id"
            @click="$emit('row:click', row.original, row.index)"
            class="p-4 space-y-2 hover:bg-gray-50 transition-colors"
            :class="{ 'cursor-pointer': !!$attrs.onRowClick }"
          >
            <slot name="mobile-card" :row="row.original" :index="row.index">
              <!-- Default mobile card layout -->
              <div
                v-for="col in columns.slice(0, 3)"
                :key="col.key"
                class="flex justify-between items-start"
              >
                <span class="text-xs font-medium text-gray-500 uppercase tracking-wide">
                  {{ col.label }}
                </span>
                <span class="text-sm text-gray-900 text-right">
                  <slot
                    :name="`cell-${col.key}`"
                    :row="row.original"
                    :column="col"
                    :value="row.original?.[col.key]"
                    :index="row.index"
                  >
                    {{ row.original?.[col.key] ?? "—" }}
                  </slot>
                </span>
              </div>
              <div
                v-if="showActions || $slots.actions"
                class="pt-2 border-t border-gray-100"
              >
                <slot name="actions" :row="row.original" :index="row.index">
                  <slot name="row-actions" :row="row.original" :index="row.index" />
                </slot>
              </div>
            </slot>
          </div>
        </template>
      </div>
    </div>

    <!-- Pagination -->
    <div v-if="showPagination">
      <!-- Use existing Pagination component for server-side -->
      <template v-if="isServerSide && meta?.links">
        <Pagination :links="meta.links" :from="from" :to="to" :total="total" />
      </template>

      <!-- TanStack Table Pagination for client-side -->
      <template v-else>
        <DataTablePagination :table="table" />
      </template>
    </div>
  </div>
</template>
