"use client";

import {
  type ColumnDef,
  flexRender,
  getCoreRowModel,
  useReactTable,
  getPaginationRowModel,
  getSortedRowModel,
  getFilteredRowModel,
  getFacetedUniqueValues,
  type RowData,
} from "@tanstack/react-table";

import { cn } from "~/lib/utils";
import { DataTablePagination } from "./DataTablePagination";
import { DataTableFilter } from "./filters/DataTableFilter";
import { DebouncedInput } from "../inputs/DebouncedInput";
import { DataTableRowCount } from "./DataTableRowCount";
import { DataTableColumnHeader } from "./DataTableColumnHeader";
import { useQueryStringState } from "./hooks/useQueryStringState";
import { type ReactNode, useState } from "react";
import { Button } from "../ui/button";
import {
  DownloadIcon,
  FolderOpenIcon,
  MaximizeIcon,
  MinimizeIcon,
} from "lucide-react";
import { useCellSelection } from "./hooks/useCellSelection";
import { useCsvExport } from "./hooks/useCsvExport";
import { useRowVirtualizer } from "./hooks/useRowVirtualizer";
import { LoadingIcon } from "yet-another-react-lightbox";

declare module "@tanstack/react-table" {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  interface ColumnMeta<TData extends RowData, TValue> {
    flex?: boolean;
  }
}

export type DataTableProps<TData, TValue> = {
  className?: string;
  columns: ColumnDef<TData, TValue>[];
  data: TData[];
  pagination: boolean;
  enableTopBar?: boolean;
  enableHeaderFilter?: boolean;
  isLoading?: boolean;
  maxCellLines?: number;
  children?: ReactNode;
};

export function DataTable<TData, TValue>({
  className,
  columns,
  data,
  children,
  pagination,
  enableTopBar = true,
  enableHeaderFilter = true,
  isLoading,
  maxCellLines = 3,
}: DataTableProps<TData, TValue>) {
  const [isFullscreen, setIsFullscreen] = useState(false);

  const {
    sorting,
    setSorting,
    columnFilters,
    setColumnFilters,
    globalFilter,
    setGlobalFilter,
  } = useQueryStringState();

  const table = useReactTable({
    data,
    columns,
    defaultColumn: {
      sortUndefined: "last",
      //size: "auto" as unknown as number,
      minSize: 10,
      maxSize: 500,
    },
    getCoreRowModel: getCoreRowModel(),
    ...(pagination && { getPaginationRowModel: getPaginationRowModel() }),
    onSortingChange: setSorting,
    getSortedRowModel: getSortedRowModel(),
    state: {
      sorting,
      columnFilters,
      globalFilter,
    },
    onColumnFiltersChange: setColumnFilters,
    onGlobalFilterChange: setGlobalFilter,
    globalFilterFn: "includesString",
    getColumnCanGlobalFilter: (column) => column.getCanFilter(),
    getFilteredRowModel: getFilteredRowModel(),
    getFacetedUniqueValues: getFacetedUniqueValues(),
  });

  const { rows } = table.getRowModel();
  const {
    tableContainerRef,
    tableRowRef,
    virtualRows,
    virtualHeight,
    scrollToRow,
  } = useRowVirtualizer({
    table,
  });

  const { isCellSelected, isRowSelected, isCellCopied, ...cellSelection } =
    useCellSelection({
      table,
      scrollToRow,
    });

  const { exportData } = useCsvExport({
    table,
  });

  const hasRows = virtualRows?.length > 0;

  return (
    <div
      className={cn(
        "flex min-h-48 flex-1 flex-col gap-2 overflow-hidden bg-background p-1",
        className,
        isFullscreen &&
          "height-[100dvh] width-[100dvw] fixed bottom-0 left-0 right-0 top-0 z-30 !m-0 max-h-[100dvh] p-2",
      )}
    >
      {enableTopBar && (
        <div className="flex items-center gap-2">
          {children}
          <DebouncedInput
            value={globalFilter ?? ""}
            onChange={(value) => setGlobalFilter(String(value))}
            className="font-lg border-blockmb-0 flex-1 border p-2 shadow"
            placeholder="Search all columns..."
          />
          <div className="ml-auto">
            <Button variant="ghost" size="icon" onClick={() => exportData()}>
              <DownloadIcon />
            </Button>
            <Button
              variant="ghost"
              size="icon"
              onClick={() => setIsFullscreen((fullscreen) => !fullscreen)}
            >
              {isFullscreen ? <MinimizeIcon /> : <MaximizeIcon />}
            </Button>
          </div>
        </div>
      )}
      <div className="flex flex-1 flex-col overflow-hidden rounded-md border">
        <div
          ref={tableContainerRef}
          className="flex flex-1 caption-bottom flex-col overflow-auto text-sm "
        >
          <div className="sticky top-0 z-10 flex">
            {table.getHeaderGroups().map((headerGroup) => (
              <div key={headerGroup.id} className="flex w-full">
                {headerGroup.headers.map((header) => {
                  return (
                    <div
                      key={header.id}
                      className="flex flex-col border-b bg-background p-1"
                      style={{
                        width: header.getSize(),
                        minWidth: header.getSize(),
                        flex: header.column.columnDef.meta?.flex
                          ? 1
                          : undefined,
                      }}
                    >
                      {header.isPlaceholder ? null : (
                        <>
                          <DataTableColumnHeader column={header.column}>
                            {flexRender(
                              header.column.columnDef.header,
                              header.getContext(),
                            )}
                          </DataTableColumnHeader>
                          {(enableHeaderFilter || isFullscreen) &&
                          header.column.getCanFilter() ? (
                            <DataTableFilter
                              column={header.column}
                              table={table}
                            />
                          ) : null}
                        </>
                      )}
                    </div>
                  );
                })}
              </div>
            ))}
          </div>
          <div className="relative flex flex-1 overflow-visible">
            <div
              className="relative flex flex-1"
              style={{
                height: virtualHeight,
              }}
              tabIndex={-1}
              onKeyDown={cellSelection.handleCellsKeyDown}
            >
              {hasRows &&
                virtualRows.map((virtualRow) => {
                  const row = rows[virtualRow.index]!;

                  return (
                    <div
                      key={row.id}
                      ref={tableRowRef}
                      data-state={row.getIsSelected() && "selected"}
                      data-index={virtualRow.index}
                      className="w-100 absolute left-0 right-0 flex border-b border-b-background transition-colors hover:bg-muted data-[state=selected]:bg-muted"
                      style={{
                        transform: `translateY(${virtualRow.start}px)`,
                      }}
                    >
                      {row.getVisibleCells().map((cell) => (
                        <div
                          onMouseDown={(e) =>
                            cellSelection.handleCellMouseDown(e, cell)
                          }
                          onMouseUp={(e) =>
                            cellSelection.handleCellMouseUp(e, cell)
                          }
                          onMouseOver={(e) =>
                            cellSelection.handleCellMouseOver(e, cell)
                          }
                          key={cell.id}
                          className={cn(
                            "relative flex select-none overflow-hidden border-x border-b border-x-transparent bg-background p-2 align-middle transition-colors before:transition-colors focus:outline-none [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
                            isCellSelected(cell) &&
                              "bg-primary/10 before:pointer-events-none before:absolute before:bottom-0 before:left-0 before:right-0 before:top-0 before:border before:border-primary hover:before:bg-primary/10",
                            isCellCopied(cell) &&
                              "bg-copy duration-300 before:border-copy-border before:duration-300",
                          )}
                          style={{
                            width: cell.column.getSize(),
                            minWidth: cell.column.getSize(),
                            flex: cell.column.columnDef.meta?.flex
                              ? 1
                              : undefined,
                          }}
                        >
                          <div
                            className="overflow-hidden"
                            style={{
                              display: "-webkit-box",
                              WebkitLineClamp: !isRowSelected(cell.row.id)
                                ? maxCellLines
                                : undefined,
                              WebkitBoxOrient: "vertical",
                              textOverflow: "clip",
                              lineHeight: "1.4rem",
                            }}
                          >
                            {flexRender(
                              cell.column.columnDef.cell,
                              cell.getContext(),
                            )}
                          </div>
                        </div>
                      ))}
                    </div>
                  );
                })}
              {!hasRows && !isLoading && (
                <div className="flex h-24 flex-1 items-center justify-center gap-2">
                  <FolderOpenIcon className="text-primary/50" />
                  No results.
                </div>
              )}
              {!hasRows && isLoading && (
                <div className="flex h-24 flex-1 items-center justify-center gap-2">
                  <LoadingIcon className="animate-spin text-primary/50" />
                  Loading...
                </div>
              )}
            </div>
          </div>
        </div>
        <div className="flex justify-between border-t p-2 pl-4">
          <DataTableRowCount table={table} />
          {pagination && <DataTablePagination table={table} />}
          <Button
            variant="ghost"
            size={null}
            onClick={() => setIsFullscreen((fullscreen) => !fullscreen)}
          >
            {isFullscreen ? (
              <MinimizeIcon size={15} />
            ) : (
              <MaximizeIcon size={15} />
            )}
          </Button>
        </div>
      </div>
    </div>
  );
}
