import * as React from "react";

import {
  Column,
  ColumnDef,
  ColumnFiltersState,
  ExpandedState,
  HeaderContext,
  PaginationState,
  SortingState,
  flexRender,
  getCoreRowModel,
  getExpandedRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  useReactTable,
  Table as ReactTable
} from "@tanstack/react-table";

import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow
} from "./Table";
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectTrigger,
  SelectValue
} from "./Select";
import { Input } from "./Input";
import { Button } from "./Button";
import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuItem
} from "./DropdownMenu";

import {
  FolderOpenDotIcon,
  ArrowUpIcon,
  ArrowDownIcon,
  ArrowUpDown,
  EyeIcon,
  X,
  ChevronsLeftIcon,
  ChevronLeftIcon,
  ChevronsRightIcon,
  ChevronRightIcon,
  ChevronDownIcon
} from "lucide-react";

interface RowData<T> {
  subRows?: T[];
}

interface DataTableProps<TData extends RowData<TData>, TValue> {
  columns: ColumnDef<TData, TValue>[];
  data: TData[];
}

interface DataTableColumnHeaderProps<TData, TValue>
  extends React.HTMLAttributes<HTMLDivElement> {
  column: Column<TData, TValue>;
}

function DataTable<TData extends RowData<TData>, TValue>({
  columns,
  data
}: DataTableProps<TData, TValue>) {
  const [selectedFilter, setSelectedFilter] = React.useState<string>("");
  const [columnVisibility, setColumnVisibility] = React.useState({});
  const [pagination, setPagination] = React.useState<PaginationState>({
    pageIndex: 0,
    pageSize: 5
  });
  const [sorting, setSorting] = React.useState<SortingState>([]);
  const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
    []
  );
  const [expanded, setExpanded] = React.useState<ExpandedState>({});

  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    // Pagination APIs
    getPaginationRowModel: getPaginationRowModel(),
    onPaginationChange: setPagination,
    // Sorting APIs
    onSortingChange: setSorting,
    getSortedRowModel: getSortedRowModel(),
    onColumnFiltersChange: setColumnFilters,
    // Filter APIs
    getFilteredRowModel: getFilteredRowModel(),
    // Expansion APIs
    getExpandedRowModel: getExpandedRowModel(),
    onExpandedChange: setExpanded,
    getSubRows: (row) => row.subRows,
    // Column Visibility APIs
    onColumnVisibilityChange: setColumnVisibility,
    state: {
      sorting,
      columnFilters,
      pagination,
      expanded,
      columnVisibility
    },
    filterFns: {}
  });

  const columnWithHeaderContext = React.useMemo(() => {
    const columnsIdSet = new Set();
    let columns: {
      column: Column<TData, unknown>;
      getContext: () => HeaderContext<TData, unknown>;
    }[] = [];

    table.getHeaderGroups().map((headerGroup) => {
      const _columns = headerGroup.headers.map(({ column, getContext }) => {
        return {
          column,
          getContext
        };
      });

      columns = columns.concat(
        _columns.filter(({ column }) => {
          if (columnsIdSet.has(column.id)) {
            return false;
          }

          columnsIdSet.add(column.id);
          return column.getCanFilter();
        })
      );
    });

    return columns;
  }, [table]);

  return (
    <section className="tw-figr-flex tw-figr-flex-col tw-figr-gap-m">
      {/* Active Columns */}

      {/* Filter */}
      <section className="tw-figr-flex tw-figr-flex-col tw-figr-gap-sm">
        {/* Input */}
        <div className="tw-figr-flex tw-figr-items-center tw-figr-justify-between tw-figr-gap-m">
          <div className="tw-figr-flex tw-figr-w-full tw-figr-items-center tw-figr-gap-xs">
            <div className="tw-figr-w-1/5">
              <Select
                onValueChange={(value) => {
                  setSelectedFilter(value);
                }}
              >
                <SelectTrigger>
                  <SelectValue placeholder="Select a column" />
                </SelectTrigger>

                <SelectContent>
                  <SelectGroup>
                    {columnWithHeaderContext.map(({ column, getContext }) => {
                      console.log({ column });

                      return !column.getCanFilter() ? (
                        <></>
                      ) : (
                        <SelectItem value={column.id}>
                          {flexRender(column.columnDef.header, getContext())}
                        </SelectItem>
                      );
                    })}
                  </SelectGroup>
                </SelectContent>
              </Select>
            </div>

            <div className="tw-figr-w-2/5">
              <Input
                disabled={!selectedFilter}
                placeholder="Filter emails..."
                value={
                  (table
                    .getColumn(selectedFilter)
                    ?.getFilterValue() as string) ?? ""
                }
                onChange={(event) => {
                  const firstRow: any = data[0];
                  const value = event.target.value;

                  if (firstRow) {
                    table
                      .getColumn(selectedFilter)
                      ?.setFilterValue(
                        typeof firstRow[selectedFilter] === "number"
                          ? value
                            ? parseInt(value, 10)
                            : undefined
                          : value
                      );
                  } else {
                    table.getColumn(selectedFilter)?.setFilterValue(value);
                  }
                }}
              />
            </div>
          </div>

          <div className="tw-figr-shrink-0">
            <DataTableViewOptions
              columns={columnWithHeaderContext.map(({ column }) => column)}
            />
          </div>
        </div>

        {/* Chips */}
        <div className="tw-figr-flex tw-figr-flex-wrap tw-figr-gap-2.5">
          {table.getState().columnFilters.map((filter) => (
            <p className="tw-figr-flex tw-figr-items-center tw-figr-gap-sm tw-figr-rounded-full tw-figr-bg-neutral-200 tw-figr-px-sm tw-figr-py-xs tw-figr-text-base-black">
              <span className="tw-figr-text-sm">
                {filter.id}: {filter.value as string}
              </span>
              <X
                className="tw-figr-h-4 tw-figr-w-4 tw-figr-cursor-pointer"
                onClick={() => {
                  table.getColumn(filter.id)!.setFilterValue(undefined);
                }}
              />
            </p>
          ))}
        </div>
      </section>

      {/* Table */}
      <Table>
        <TableHeader>
          {table.getHeaderGroups().map((headerGroup) => (
            <TableRow key={headerGroup.id}>
              {headerGroup.headers.map((header) => {
                return (
                  <TableHead key={header.id} colSpan={header.colSpan}>
                    {header.isPlaceholder ? null : (
                      <DataTableColumnHeader column={header.column}>
                        {flexRender(
                          header.column.columnDef.header,
                          header.getContext()
                        )}
                      </DataTableColumnHeader>
                    )}
                  </TableHead>
                );
              })}
            </TableRow>
          ))}
        </TableHeader>

        <TableBody>
          {table.getRowModel().rows?.length ? (
            table.getRowModel().rows.map((row) => (
              <TableRow
                key={row.id}
                data-state={row.getIsSelected() && "selected"}
              >
                {row.getVisibleCells().map((cell) => (
                  <TableCell key={cell.id}>
                    {flexRender(cell.column.columnDef.cell, cell.getContext())}
                  </TableCell>
                ))}
              </TableRow>
            ))
          ) : (
            <TableRow>
              <TableCell
                colSpan={columns.length}
                className="tw-figr-h-12 tw-figr-text-center"
              >
                No results.
              </TableCell>
            </TableRow>
          )}
        </TableBody>
      </Table>

      {/* Controls */}
      <section className="tw-figr-flex tw-figr-flex-col tw-figr-gap-4">
        {/* Page Movement controls */}
        <div className="tw-figr-flex tw-figr-items-center tw-figr-justify-between">
          <div>
            <Button
              variant={"icon"}
              size={"icon"}
              onClick={() => table.setPageIndex(0)}
              disabled={!table.getCanPreviousPage()}
            >
              <ChevronsLeftIcon
                style={{ height: "1.25rem", width: "1.25rem" }}
              />
            </Button>

            <Button
              variant={"icon"}
              size={"icon"}
              onClick={() => table.previousPage()}
              disabled={!table.getCanPreviousPage()}
            >
              <ChevronLeftIcon
                style={{ height: "1.25rem", width: "1.25rem" }}
              />
            </Button>

            <Button
              variant={"icon"}
              size={"icon"}
              onClick={() => table.nextPage()}
              disabled={!table.getCanNextPage()}
            >
              <ChevronRightIcon
                style={{ height: "1.25rem", width: "1.25rem" }}
              />
            </Button>

            <Button
              variant={"icon"}
              size={"icon"}
              onClick={() => table.setPageIndex(table.getPageCount() - 1)}
              disabled={!table.getCanNextPage()}
            >
              <ChevronsRightIcon
                style={{ height: "1.25rem", width: "1.25rem" }}
              />
            </Button>
          </div>

          <p className="tw-figr-flex tw-figr-items-center tw-figr-gap-1">
            <span>Page</span>
            <strong>
              {table.getState().pagination.pageIndex + 1}&nbsp;of&nbsp;
              {table.getPageCount()}
            </strong>
          </p>
        </div>

        {/* Select page size and Page Jump input */}
        <div className="tw-figr-flex tw-figr-items-center tw-figr-justify-between tw-figr-gap-1">
          <div className="tw-figr-flex tw-figr-w-1/3 tw-figr-items-center tw-figr-gap-1">
            <p style={{ flexShrink: "0" }}>Go to page:</p>

            <Input
              type="number"
              defaultValue={table.getState().pagination.pageIndex + 1}
              onChange={(e) => {
                const page = e.target.value ? Number(e.target.value) - 1 : 0;
                table.setPageIndex(page);
              }}
            />
          </div>

          <div className="tw-figr-w-1/5">
            <Select
              value={table.getState().pagination.pageSize.toString()}
              onValueChange={(value) => {
                table.setPageSize(Number(value));
              }}
            >
              <SelectTrigger>
                <SelectValue placeholder="Select Row Size" />
              </SelectTrigger>

              <SelectContent>
                <SelectGroup>
                  {[5, 10, 20, 30, 40, 50].map((pageSize) => (
                    <SelectItem key={pageSize} value={pageSize.toString()}>
                      Show {pageSize}
                    </SelectItem>
                  ))}
                </SelectGroup>
              </SelectContent>
            </Select>
          </div>
        </div>
      </section>
    </section>
  );
}

function DataTableViewOptions<TData>({
  columns
}: {
  columns: Column<TData, unknown>[];
}) {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline-with-icon" size="sm">
          <span>Columns</span>
          <ChevronDownIcon className="tw-figr-h-4 tw-figr-w-4" />
        </Button>
      </DropdownMenuTrigger>

      <DropdownMenuContent align="end">
        <DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
        <DropdownMenuSeparator />
        {columns.map((column) => {
          return (
            <DropdownMenuCheckboxItem
              key={column.id}
              className="capitalize"
              checked={column.getIsVisible()}
              onCheckedChange={(value) => column.toggleVisibility(!!value)}
            >
              {column.id}
            </DropdownMenuCheckboxItem>
          );
        })}
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

function DataTableColumnHeader<TData, TValue>({
  column,
  children
}: DataTableColumnHeaderProps<TData, TValue>) {
  if (!column.getCanSort()) {
    return <div>{children}</div>;
  }

  return (
    <div>
      <DropdownMenu>
        <DropdownMenuTrigger asChild>
          <Button
            variant="ghost"
            size="sm"
            className="-tw-figr-ml-3 tw-figr-h-8 data-[state=open]:tw-figr-bg-neutral-500"
          >
            <span>{children}</span>

            {column.getIsSorted() === "desc" ? (
              <ArrowDownIcon className="tw-figr-ml-2 tw-figr-h-4 tw-figr-w-4" />
            ) : column.getIsSorted() === "asc" ? (
              <ArrowUpIcon className="tw-figr-ml-2 tw-figr-h-4 tw-figr-w-4" />
            ) : (
              <ArrowUpDown className="tw-figr-ml-2 tw-figr-h-4 tw-figr-w-4" />
            )}
          </Button>
        </DropdownMenuTrigger>

        <DropdownMenuContent align="start">
          <DropdownMenuItem onClick={() => column.toggleSorting(false)}>
            <ArrowUpIcon className="text-base-black tw-figr-mr-2 tw-figr-h-3 tw-figr-w-3" />
            Asc
          </DropdownMenuItem>

          <DropdownMenuItem onClick={() => column.toggleSorting(true)}>
            <ArrowDownIcon className="text-base-black tw-figr-mr-2 tw-figr-h-3 tw-figr-w-3" />
            Desc
          </DropdownMenuItem>

          <DropdownMenuSeparator />

          <DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
            <EyeIcon className="text-base-black tw-figr-mr-2 tw-figr-h-3 tw-figr-w-3" />
            Hide
          </DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </div>
  );
}

export { DataTable };
