import { ArrowDownIcon, ArrowUpIcon, ChevronLeftIcon, ChevronRightIcon } from '@trail-ui/icons';
import { Avatar } from '../avatar';
import { AvatarGroup } from '../avatar';
import { Checkbox } from '../checkbox';
import { Lozenge } from '../lozenge';
import { Button } from '../button';
import { clsx } from '@trail-ui/shared-utils';

import { commonColors } from '@trail-ui/theme';

import type { ColumnDef, ColumnFiltersState, Header, SortingState } from '@tanstack/react-table';
import {
  flexRender,
  getCoreRowModel,
  getFacetedRowModel,
  getFacetedUniqueValues,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  useReactTable,
} from '@tanstack/react-table';

import React, { ReactNode, useEffect, useState } from 'react';

import TableSubHeader from './table-header';

type ObjectFitProps = 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';

export interface Column {
  id: string;
  name: string;
  accessor: string;
  type:
    | 'id'
    | 'text'
    | 'date'
    | 'number'
    | 'status'
    | 'percentage'
    | 'usersList'
    | 'image'
    | 'link'
    | 'username'
    | 'profileImage';
  width?: number;
  objectFit?: ObjectFitProps;
  linkTo: string;
  filterFn: string;
}

export interface Row {
  getValue(columnId: string): unknown;
}

const includesString = (row: Row, columnId: string, filterValue: string) => {
  const rowValue = row.getValue(columnId);
  if (typeof rowValue !== 'string' || typeof filterValue !== 'string') {
    return false;
  }
  return rowValue.toLowerCase().includes(filterValue.toLowerCase());
};

const multiSelectFilter = (row: Row, columnId: string, filterValue: string[]) => {
  if (!Array.isArray(filterValue)) return true;

  const rowValue = row.getValue(columnId) as string;
  return filterValue.includes(rowValue);
};

export default function TanstackTable({
  columns,
  data,
  tableData,
  showName = true,
  defaultSortingHeader,
  tableName,
  checkBoxSelectedName,
  WithoutCheckbox,
  tableHeight,
  showFilter = true,
}: {
  columns: Column[];
  data: any[];
  tableData?: number;
  showName?: boolean;
  defaultSortingHeader?: string;
  tableName?: string;
  checkBoxSelectedName?: string;
  WithoutCheckbox?: boolean;
  tableHeight?: string;
  showFilter?: boolean;
}) {
  const tableColumns: ColumnDef<object>[] = columns.map((column: Column) => {
    return {
      id: column.id,
      size: column.width,
      header: () => {
        switch (column.type) {
          case 'id':
            return (
              !WithoutCheckbox && (
                <div className="text-center">
                  <Checkbox
                    aria-label={`Select All ${tableName}`}
                    classNames={{ control: 'mr-0' }}
                    {...{
                      isSelected: Object.keys(rowSelection).length === data.length,
                      isIndeterminate:
                        Object.keys(rowSelection).length > 0 &&
                        Object.keys(rowSelection).length < data.length,
                      checked: Object.keys(rowSelection).length === data.length,
                      onChange: () => {
                        const allSelected = Object.keys(rowSelection).length === data.length;
                        const newSelection = allSelected
                          ? {}
                          : data.reduce((acc, row) => {
                              acc[row.id] = true;
                              return acc;
                            }, {});
                        setRowSelection(newSelection);
                      },
                    }}
                  />
                </div>
              )
            );
          default:
            return column.name;
        }
      },
      accessorKey: column.accessor,

      filterFn: column.type === 'text' ? multiSelectFilter : multiSelectFilter,
      cell: ({ cell }) => {
        switch (column.type) {
          case 'id':
            return (
              !WithoutCheckbox && (
                <div className="text-center">
                  <Checkbox
                    classNames={{ control: 'mr-0' }}
                    aria-label={`${(cell.row.original as Record<string, unknown>)[checkBoxSelectedName ?? '']}`}
                    {...{
                      isSelected: cell.row.getIsSelected(),
                      onChange: () => cell.row.toggleSelected(),
                    }}
                  />
                </div>
              )
            );
          case 'text':
            return cell.getValue() as string;
          case 'date':
            return new Date(cell.getValue() as string).toLocaleDateString();
          case 'number':
            return cell.getValue() as number;
          case 'status':
            return (
              <p className="w-max">
                <Lozenge
                  size="sm"
                  variant="light"
                  value={cell.getValue() as string}
                  color={
                    cell.getValue() === 'Completed'
                      ? 'green'
                      : cell.getValue() === 'Regression'
                        ? 'red'
                        : 'yellow'
                  }
                />
              </p>
            );

          case 'percentage':
            return (
              <Lozenge
                size="sm"
                variant="light"
                value={`${cell.getValue() as number}`}
                color={
                  (cell.getValue() as number) > 0
                    ? 'green'
                    : (cell.getValue() as number) < 0
                      ? 'red'
                      : 'yellow'
                }
              />
            );

          case 'image':
            return (
              <figure className="min-w-36">
                <img
                  className="h-10"
                  src={cell.getValue() as string}
                  style={{
                    objectFit: column.objectFit ? column.objectFit : 'cover',
                    width: column.objectFit === 'contain' ? 'max-content' : '100%',
                  }}
                  alt=""
                />
              </figure>
            );

          case 'profileImage':
            return (
              <figure>
                <img
                  className="h-10"
                  src={cell.getValue() as string}
                  style={{
                    objectFit: column.objectFit ? column.objectFit : 'cover',
                    width: column.objectFit === 'contain' ? 'max-content' : '100%',
                  }}
                  alt=""
                />
              </figure>
            );

          case 'username':
            return (
              <p className="flex items-center gap-1 text-sm text-neutral-900">
                <Avatar
                  size="md"
                  is="span"
                  name={cell.getValue() as string}
                  getInitials={(name: string) => name.toUpperCase().slice(0, 2)}
                  showFallback
                  color={
                    ['green', 'red', 'yellow', 'default', 'purple', 'blue'][
                      Math.floor(Math.random() * 6)
                    ] as 'green' | 'red' | 'yellow' | 'default' | 'purple' | 'blue' | undefined
                  }
                />
                {showName && (
                  <span id={cell.getValue() as string} className="text-sm text-neutral-900">
                    {cell.getValue() as string}
                  </span>
                )}
              </p>
            );

          case 'usersList':
            return (
              <AvatarGroup size="md" max={2}>
                {((cell.getValue() as any[]) || []).map((user: any) => (
                  <Avatar
                    size="md"
                    key={user.id}
                    is="span"
                    src={user.avatar}
                    name={user.name}
                    getInitials={(name: string) => name.toUpperCase().slice(0, 2)}
                    showFallback
                    color={
                      ['green', 'red', 'yellow', 'default', 'purple', 'blue'][
                        Math.floor(Math.random() * 6)
                      ] as 'green' | 'red' | 'yellow' | 'default' | 'purple' | 'blue' | undefined
                    }
                  />
                ))}
              </AvatarGroup>
            );

          default:
            return cell.getValue() as ReactNode;
        }
      },
    };
  });

  const [sorting, setSorting] = useState<SortingState>([]);
  const [rowSelection, setRowSelection] = useState({});
  const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
  const [globalFilter, setGlobalFilter] = React.useState('');

  const table = useReactTable<(typeof data)[number]>({
    enableRowSelection: true,
    columns: tableColumns as ColumnDef<(typeof data)[number], typeof data>[],
    data: data,
    defaultColumn: {
      size: 100,
    },
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),

    onColumnFiltersChange: setColumnFilters,
    onRowSelectionChange: setRowSelection,
    onSortingChange: setSorting,
    getPaginationRowModel: getPaginationRowModel(),
    getFacetedRowModel: getFacetedRowModel(),
    getFacetedUniqueValues: getFacetedUniqueValues(),
    getFilteredRowModel: getFilteredRowModel(),
    filterFns: {
      includesString,
      multiSelectFilter,
    },
    state: {
      sorting,
      rowSelection,
      columnFilters,
      globalFilter,
    },
    initialState: {
      pagination: {
        pageSize: tableData ? tableData : 10,
        pageIndex: 0,
      },
    },
  });

  const currentPage = table.getState().pagination.pageIndex + 1;
  const pageCount = table.getPageCount();

  const pageNumbers = [];
  const maxButtonsToShow = 5;

  if (pageCount <= maxButtonsToShow) {
    for (let i = 1; i <= pageCount; i++) {
      pageNumbers.push(i);
    }
  } else {
    let startPage, endPage;
    const middleButtonsToShow = maxButtonsToShow - 2;
    const halfMiddleButtonsToShow = Math.floor(middleButtonsToShow / 2);

    if (currentPage <= halfMiddleButtonsToShow + 1) {
      startPage = 2;
      endPage = middleButtonsToShow + 1;
    } else if (currentPage + halfMiddleButtonsToShow >= pageCount) {
      startPage = pageCount - middleButtonsToShow;
      endPage = pageCount - 1;
    } else {
      startPage = currentPage - halfMiddleButtonsToShow;
      endPage = currentPage + halfMiddleButtonsToShow;
    }

    pageNumbers.push(1);
    if (startPage > 2) {
      pageNumbers.push('...');
    }

    for (let i = startPage; i <= endPage; i++) {
      pageNumbers.push(i);
    }

    if (endPage < pageCount - 1) {
      pageNumbers.push('...');
    }
    pageNumbers.push(pageCount);
  }

  const alignRightData = [
    'salesHours',
    'plannedHours',
    'teamSize',
    'plannedVsActual',
    'actualVsPlanned',
    'salesVsActual',
    'actualHours',
    'noOfHours',
    'noOfUnits',
    'report',
    'support',
    'fixing',
    'meeting',
    'qualityAssurance',
    'bbMeeting',
    'documentation',
    'training',
    'offProduction',
    'peerReview',
    'projects',
    'reported',
    'perfectIssues',
    'perfectIssuesPercentage',
  ];

  const [currentSort, setCurrentSort] = useState<string | null>(
    defaultSortingHeader ? defaultSortingHeader : 'projectName',
  );

  function toggleSort(header: Header<any, any>) {
    const sortingHandler = header.column.getToggleSortingHandler();
    if (sortingHandler) {
      sortingHandler({ persist: () => {} });
    }
    setCurrentSort(header.id);
  }

  const stateSorting = table.getState().sorting;

  useEffect(() => {
    const sortedHeader = table
      .getHeaderGroups()
      .flatMap((headerGroup) => headerGroup.headers)
      .find((header) => header.column.getIsSorted());

    if (sortedHeader) {
      setCurrentSort(sortedHeader.id);
    } else {
      setCurrentSort(null);
    }
  }, [stateSorting, table]);

  const [openFilter, setOpenFilter] = useState(false);

  return (
    <div className="rounded">
      {showFilter && (
        <TableSubHeader
          openFilter={openFilter}
          setOpenFilter={setOpenFilter}
          table={table}
          globalFilter={globalFilter}
          setGlobalFilter={setGlobalFilter}
          data={data}
          columns={columns}
          tableName={tableName}
        />
      )}

      <div className="p-6">
        <div className="h-full overflow-auto rounded border border-neutral-200">
          <div className={`${tableHeight ? tableHeight : ''}`}>
            <table className="w-full overflow-auto bg-white ">
              <thead className="sticky top-0 z-10 border-b border-b-neutral-200">
                <tr>
                  {table.getHeaderGroups().map((headerGroup) =>
                    headerGroup.headers.map((header) => (
                      <th
                        id={header.id}
                        {...(header.id === 'id'
                          ? {
                              'aria-label': `Select All ${tableName}`,
                            }
                          : {})}
                        aria-sort={
                          header.column.getIsSorted() === 'asc'
                            ? 'ascending'
                            : header.column.getIsSorted() === 'desc'
                              ? 'descending'
                              : 'none'
                        }
                        key={header.id}
                        scope="col"
                        colSpan={header.colSpan}
                        className={`${alignRightData.includes(header.id) ? 'text-right' : 'text-left'} ${WithoutCheckbox ? 'first:auto' : 'first:w-10'} h-10 min-h-10 border-r border-r-neutral-200 bg-neutral-100 p-2 text-sm font-semibold text-neutral-900 first:text-center last:border-r-0`}
                      >
                        {header.isPlaceholder &&
                        header.column.getIndex() === 0 ? null : header.id == 'id' ? (
                          <span>
                            {flexRender(header.column.columnDef.header, header.getContext())}
                          </span>
                        ) : (
                          <Button
                            appearance="transparent"
                            className={clsx(
                              header.column.getCanSort() ? 'cursor-pointer select-none' : '',
                              `${alignRightData.includes(header.id) ? 'justify-end' : 'justify-start'} w-full items-center px-0 text-sm font-semibold text-neutral-900`,
                            )}
                            onPress={() => toggleSort(header)}
                            onKeyDown={(event) => {
                              if (event.key === 'Enter' || event.key === ' ') {
                                header.column.getToggleSortingHandler();
                              }
                            }}
                          >
                            <span
                              className={`${alignRightData.includes(header.id) ? 'justify-end' : 'justify-between'} flex w-full items-center gap-4`}
                            >
                              {flexRender(header.column.columnDef.header, header.getContext())}
                              {header.column.getCanSort() &&
                                header.id !== 'id' &&
                                (currentSort === header.id ? (
                                  <span className="sort-icon">
                                    {{
                                      asc: (
                                        <ArrowUpIcon
                                          color={commonColors.neutral[800]}
                                          width={16}
                                          height={16}
                                        />
                                      ),
                                      desc: (
                                        <ArrowDownIcon
                                          color={commonColors.neutral[800]}
                                          width={16}
                                          height={16}
                                        />
                                      ),
                                    }[header.column.getIsSorted() as string] ?? null}
                                  </span>
                                ) : null)}
                            </span>
                          </Button>
                        )}
                      </th>
                    )),
                  )}
                </tr>
              </thead>
              <tbody>
                {table.getRowModel().rows?.length ? (
                  table.getRowModel().rows.map((row) => (
                    <tr key={row.id} data-state={row.getIsSelected() && 'selected'}>
                      {row.getVisibleCells().map((cell) =>
                        cell.column.id === checkBoxSelectedName ? (
                          <th
                            id={`head-${row.id}`}
                            className={`${alignRightData.includes(cell.column.id) ? 'text-right' : 'text-left'} ${WithoutCheckbox ? 'first:auto' : 'first:w-10'} h-10 border-b border-l border-b-neutral-200 border-l-neutral-200 !bg-neutral-50 px-2 py-2 text-sm font-normal text-neutral-900 first:border-l-0`}
                            key={cell.id}
                          >
                            {flexRender(cell.column.columnDef.cell, cell.getContext())}
                          </th>
                        ) : (
                          <td
                            headers={`head-${row.id} ${cell.column.id}`}
                            className={`${alignRightData.includes(cell.column.id) ? 'text-right' : 'text-left'} ${WithoutCheckbox ? 'first:auto' : 'first:w-10'} h-10 border-b border-l border-b-neutral-200 border-l-neutral-200 px-2 py-2 text-sm font-normal text-neutral-900 first:border-l-0`}
                            key={cell.id}
                          >
                            {flexRender(cell.column.columnDef.cell, cell.getContext())}
                          </td>
                        ),
                      )}
                    </tr>
                  ))
                ) : (
                  <tr>
                    <td colSpan={columns.length} className="h-24 text-center">
                      No results.
                    </td>
                  </tr>
                )}
              </tbody>
            </table>
          </div>
        </div>

        <div className="mt-2 flex items-center justify-between space-x-2 p-2">
          <div>
            <p className="text-sm text-neutral-600">
              Showing {table.getRowModel().rows.length} out of {data.length} results
            </p>
          </div>
          <nav aria-label="Pagination" className="flex justify-end">
            <ul className="justtify-end flex gap-2">
              <li>
                <Button
                  className="px-1"
                  aria-label="Previous page"
                  appearance="transparent"
                  isDisabled={!table.getCanPreviousPage()}
                  onPress={() => table.previousPage()}
                >
                  <ChevronLeftIcon
                    color={`${currentPage === 1 ? '#B0ACBB' : '#302E38'}`}
                    width={24}
                    height={24}
                  />
                </Button>
              </li>
              {pageNumbers.map((pageNumber: any, index: number) => (
                <li className="h-8 w-8" key={index}>
                  {pageNumber === '...' ? (
                    <span>...</span>
                  ) : (
                    <button
                      {...(pageNumber === currentPage ? { 'aria-current': 'page' } : {})}
                      aria-current={pageNumber === currentPage ? 'page' : undefined}
                      onClick={() => {
                        if (pageNumber !== '...') {
                          table.setPageIndex(pageNumber - 1);
                        }
                      }}
                      className={`h-full w-full text-sm font-normal text-neutral-800 ${
                        pageNumber === currentPage
                          ? 'rounded border border-purple-600 bg-purple-100'
                          : 'bg-white'
                      } ${pageNumber !== '...' ? 'underline' : ''}`}
                    >
                      {pageNumber}
                    </button>
                  )}
                </li>
              ))}
              <li>
                <Button
                  className="px-1"
                  appearance="transparent"
                  isDisabled={!table.getCanNextPage()}
                  onPress={() => table.nextPage()}
                  aria-label="Next page"
                >
                  <ChevronRightIcon
                    color={!table.getCanNextPage() ? '#B0ACBB' : '#302E38'}
                    width={24}
                    height={24}
                  />
                </Button>
              </li>
            </ul>
          </nav>
        </div>
      </div>
    </div>
  );
}
