/* eslint-disable import/prefer-default-export */
/* eslint-disable jsx-a11y/label-has-associated-control */
/* eslint-disable jsx-a11y/control-has-associated-label */
import React, { CSSProperties, useState } from 'react'
import {
  getCoreRowModel,
  getPaginationRowModel,
  useReactTable,
  flexRender,
  ColumnDef,
  Row,
  getSortedRowModel,
  SortingState,
} from '@tanstack/react-table'
import classNames from 'classnames'
import { GrFormPrevious, GrFormNext } from 'react-icons/gr'
import { BiSkipNext, BiSkipPrevious } from 'react-icons/bi'
import './loader.css'
import { TiArrowUnsorted, TiArrowSortedUp, TiArrowSortedDown } from 'react-icons/ti'

export type DataType = {
  [key: string]: any
}

export type ColumnAlignment = 'left' | 'center' | 'right'

export type ExtendedColumnDef<T> = ColumnDef<T> & {
  align?: ColumnAlignment
  width?: string | number
  maxWidth?: string | number
  grow?: boolean
  clip?: boolean
  sticky?: 'left' | 'right'
  padding?: 'low' | 'normal'
  omit?: boolean
}

export type PaginationType = {
  page: number
  perPage: number
  total: number
  lastPage: number
}

export const defaultPagination: PaginationType = {
  page: 1,
  perPage: 20,
  total: 0,
  lastPage: 1,
}

export type RowWrapperProps = {
  row: Row<DataType>
  index: number
  children: React.ReactElement
}

interface ListTableProps {
  data: DataType[]
  columns: ExtendedColumnDef<DataType>[]
  noDataText?: string
  isLoading?: boolean
  serverPagination?: boolean
  pagination?: PaginationType
  setPagination?: (pagination: PaginationType) => void
  disablePagination?: boolean
  rowWrapper?: (props: RowWrapperProps) => React.ReactNode
  getRowClassName?: (row: Row<DataType>, index: number) => string
  containerClassName?: string
  cellPaddingClassName?: string
  highlightOnHover?: boolean
}

export const ListTable = (props: ListTableProps) => {
  const {
    data = [] as DataType[],
    columns,
    noDataText = 'No Data',
    isLoading,
    serverPagination = false,
    pagination = defaultPagination,
    setPagination,
    disablePagination = false,
    rowWrapper,
    getRowClassName,
    containerClassName,
    cellPaddingClassName,
    highlightOnHover = true,
  } = props

  const [sorting, setSorting] = useState<SortingState>([])

  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    onSortingChange: setSorting,
    enableSorting: true,
    defaultColumn: {
      enableSorting: false,
    },
    initialState: {
      pagination: {
        pageIndex: 0,
        pageSize: pagination.perPage ?? 20,
      },
      columnVisibility: Object.fromEntries(
        columns.filter((col) => col.omit).map((col) => [(col as any).accessorKey ?? (col as any).id, false]),
      ),
    },
    state: {
      sorting,
      ...(serverPagination && !disablePagination && pagination.page !== undefined
        ? {
            pagination: {
              pageIndex: Math.max((pagination.page ?? 1) - 1, 0),
              pageSize: pagination.perPage ?? 10,
            },
          }
        : {}),
    },

    ...(disablePagination
      ? {}
      : serverPagination
      ? {
          manualPagination: true,
          pageCount: pagination.lastPage ?? -1,
        }
      : {
          getPaginationRowModel: getPaginationRowModel(),
        }),

    debugTable: true,
  })

  // Keep 1-based for our logic (API uses 1-based)
  const currentPage = disablePagination
    ? 1
    : serverPagination
    ? pagination.page ?? 1
    : table.getState().pagination.pageIndex + 1

  const currentPageSize = disablePagination
    ? data.length
    : serverPagination
    ? pagination.perPage ?? defaultPagination.perPage
    : table.getState().pagination.pageSize

  const totalPageCount = disablePagination
    ? 1
    : serverPagination
    ? pagination.lastPage && pagination.lastPage > 0
      ? pagination.lastPage
      : 1
    : table.getPageCount()

  // For 1-based pagination: can go previous if page > 1
  const canPreviousPage = !disablePagination && (serverPagination ? currentPage > 1 : table.getCanPreviousPage())

  const canNextPage = !disablePagination && (serverPagination ? currentPage < totalPageCount : table.getCanNextPage())

  const handlePageChange = (newPage: number) => {
    // newPage is 1-based (from our handlers)
    if (serverPagination && setPagination) {
      setPagination({
        ...pagination,
        page: newPage,
      })
    } else {
      // For client pagination, convert to 0-based
      table.setPageIndex(newPage - 1)
    }
  }

  const handlePageSizeChange = (newPageSize: number) => {
    if (serverPagination && setPagination) {
      setPagination({
        ...pagination,
        perPage: newPageSize,
        page: 1, // reset to first page when changing size
      })
    } else {
      table.setPageSize(newPageSize)
    }
  }

  const handleFirstPage = () => {
    handlePageChange(1)
  }

  const handlePreviousPage = () => {
    handlePageChange(currentPage - 1)
  }

  const handleNextPage = () => {
    handlePageChange(currentPage + 1)
  }

  const handleLastPage = () => {
    handlePageChange(totalPageCount)
  }

  const getAlignmentClass = (align?: ColumnAlignment) => {
    switch (align) {
      case 'center':
        return 'text-center'
      case 'right':
        return 'text-right'
      case 'left':
      default:
        return 'text-left'
    }
  }

  const getColumnStyle = (
    column: ExtendedColumnDef<DataType>,
    index: number,
    allColumns?: ExtendedColumnDef<DataType>[],
    isHeader: boolean = false,
  ): CSSProperties => {
    const style: CSSProperties = {}

    if (column.grow) {
      // For growing columns, use width as minWidth if provided, otherwise let it grow freely
      if (column.width !== undefined) {
        style.minWidth = typeof column.width === 'number' ? `${column.width}px` : column.width
      }
      // Apply maxWidth if provided
      if (column.maxWidth !== undefined) {
        style.maxWidth = typeof column.maxWidth === 'number' ? `${column.maxWidth}px` : column.maxWidth
      }
      // Don't set a fixed width, let the column grow to fill available space
    } else if (column.width !== undefined) {
      // For non-growing columns, set fixed width
      style.width = typeof column.width === 'number' ? `${column.width}px` : column.width
      style.minWidth = style.width
      style.maxWidth =
        column.maxWidth !== undefined
          ? typeof column.maxWidth === 'number'
            ? `${column.maxWidth}px`
            : column.maxWidth
          : style.width
    } else if (column.maxWidth !== undefined) {
      // If only maxWidth is provided without width
      style.maxWidth = typeof column.maxWidth === 'number' ? `${column.maxWidth}px` : column.maxWidth
    }

    // Handle sticky columns
    if (column.sticky === 'left' && allColumns) {
      // Calculate left position by summing widths of previous sticky left columns
      let leftOffset = 0
      for (let i = 0; i < index; i += 1) {
        const prevColumn = allColumns[i] as ExtendedColumnDef<DataType>
        if (prevColumn.sticky === 'left') {
          const prevWidth = prevColumn.width
          if (prevWidth !== undefined) {
            leftOffset += typeof prevWidth === 'number' ? prevWidth : parseInt(prevWidth, 10)
          }
        }
      }
      style.position = 'sticky'
      style.left = `${leftOffset}px`
      style.zIndex = isHeader ? 50 : 10
    } else if (column.sticky === 'left') {
      style.position = 'sticky'
      style.left = 0
      style.zIndex = isHeader ? 50 : 10
    } else if (column.sticky === 'right') {
      let rightOffset = 0
      if (allColumns) {
        for (let i = index + 1; i < allColumns.length; i += 1) {
          const nextColumn = allColumns[i] as ExtendedColumnDef<DataType>
          if (nextColumn.sticky === 'right') {
            const nextWidth = nextColumn.width
            if (nextWidth !== undefined) {
              rightOffset += typeof nextWidth === 'number' ? nextWidth : parseInt(nextWidth as string, 10)
            }
          }
        }
      }
      style.position = 'sticky'
      style.right = `${rightOffset}px`
      style.zIndex = isHeader ? 50 : 10
    }

    return style
  }

  const leftStickyWidth = columns
    .filter((c) => c.sticky === 'left')
    .reduce((acc, c) => acc + (typeof c.width === 'number' ? c.width : parseInt((c.width as string) || '0', 10)), 0)

  const rightStickyWidth = columns
    .filter((c) => c.sticky === 'right')
    .reduce((acc, c) => acc + (typeof c.width === 'number' ? c.width : parseInt((c.width as string) || '0', 10)), 0)

  return (
    <div className="w-full relative">
      <div
        className={classNames(
          'w-full flex items-start bg-white rounded-b-md flex-row flex-wrap items-center gap-2 justify-between overflow-auto rounded-t-md',
          'max-h-[60svh] md:max-h-[62svh]',
          'max-w-[94svw] md:max-w-[84svw]',
          containerClassName,
        )}
      >
        <table className="w-full border-l border-r">
          <thead className="sticky top-0 z-30">
            {table.getHeaderGroups().map((headerGroup) => (
              <tr key={headerGroup.id}>
                {headerGroup.headers.map((header, headerIndex) => {
                  const columnDef = header.column.columnDef as ExtendedColumnDef<DataType>
                  const align = columnDef.align || 'left'
                  const alignmentClass = getAlignmentClass(align)
                  const columnStyle = getColumnStyle(columnDef, headerIndex, columns, true)
                  const padding = columnDef.padding === 'low' ? 'p-1' : cellPaddingClassName || 'px-6 py-2'
                  return (
                    <th
                      key={header.id}
                      colSpan={header.colSpan}
                      rowSpan={header.placeholderId ? 1 : undefined}
                      scope="col"
                      className={classNames(
                        'text-sm font-semibold bg-gray-100 text-gray-700 border-b border-gray-200',
                        padding,
                        alignmentClass,
                        columnDef.sticky && 'bg-gray-100',
                        header.column.getCanSort() && 'cursor-pointer select-none',
                      )}
                      style={columnStyle}
                      onClick={header.column.getToggleSortingHandler()}
                    >
                      {header.isPlaceholder ? null : (
                        <div
                          className={classNames(
                            'flex items-center gap-1.5 w-full',
                            align === 'center' && 'justify-center',
                            align === 'right' && 'justify-end',
                            align === 'left' && 'justify-start',
                          )}
                        >
                          {flexRender(header.column.columnDef.header, header.getContext())}

                          {header.column.getCanSort() && (
                            <span className="flex items-center justify-center shrink-0">
                              {(() => {
                                const isSorted = header.column.getIsSorted()

                                if (isSorted === 'asc') {
                                  return <TiArrowSortedUp size={18} className="text-gray-500 hover:text-gray-700" />
                                }
                                if (isSorted === 'desc') {
                                  return <TiArrowSortedDown size={18} className="text-gray-500 hover:text-gray-700" />
                                }

                                return <TiArrowUnsorted size={16} className="text-gray-500 hover:text-gray-700" />
                              })()}
                            </span>
                          )}
                        </div>
                      )}
                    </th>
                  )
                })}
              </tr>
            ))}
          </thead>
          <tbody>
            {isLoading ? (
              <tr className="bg-base-100 text-center border">
                <td colSpan={1000} className="py-12 whitespace-nowrap text-sm text-gray-900 ">
                  <div className="lds-ring">
                    <div />
                    <div />
                    <div />
                    <div />
                  </div>
                </td>
              </tr>
            ) : table.getRowModel().rows.length ? (
              table.getRowModel().rows.map((row, index) => {
                const extraRowClass = getRowClassName?.(row, index) ?? ''
                const allColumnDefs = row
                  .getVisibleCells()
                  .map((c) => c.column.columnDef as ExtendedColumnDef<DataType>)
                const rowContent = (
                  <tr
                    key={row.id}
                    className={classNames(
                      'group border transition duration-300 ease-in-out',
                      index % 2 === 1
                        ? `bg-gray-50 ${highlightOnHover ? 'group-hover:bg-gray-100' : ''}`
                        : `bg-base-100 ${highlightOnHover ? 'group-hover:bg-gray-100' : ''}`,
                      extraRowClass,
                    )}
                  >
                    {row.getVisibleCells().map((cell, cellIndex) => {
                      const columnDef = cell.column.columnDef as ExtendedColumnDef<DataType>
                      const align = columnDef.align || 'left'
                      const alignmentClass = getAlignmentClass(align)
                      const columnStyle = getColumnStyle(columnDef, cellIndex, allColumnDefs, false)
                      const shouldWrap = columnDef.clip
                      const padding = columnDef.padding === 'low' ? 'p-1' : cellPaddingClassName || 'px-6 py-2'
                      const isSticky = columnDef.sticky === 'left' || columnDef.sticky === 'right'
                      const rowBgClass = index % 2 === 1 ? 'bg-gray-50' : 'bg-base-100' // bg-gray-50 for odd, bg-base-100 for even

                      const cellBgClass = extraRowClass
                        ? `${extraRowClass} ${highlightOnHover ? 'group-hover:bg-gray-200' : ''}`
                        : `${rowBgClass} ${highlightOnHover ? 'group-hover:bg-gray-100' : ''}`

                      return (
                        <td
                          key={cell.id}
                          className={classNames(
                            'text-sm text-gray-900',
                            padding,
                            shouldWrap ? '' : 'whitespace-nowrap',
                            alignmentClass,
                            cellBgClass,
                          )}
                          style={columnStyle}
                        >
                          {flexRender(cell.column.columnDef.cell, cell.getContext())}
                        </td>
                      )
                    })}
                  </tr>
                )
                return rowWrapper ? rowWrapper({ row, index, children: rowContent } as RowWrapperProps) : rowContent
              })
            ) : (
              <tr className="bg-base-100 text-center border-b">
                <td colSpan={1000} className="py-12 whitespace-nowrap text-lg">
                  {noDataText}
                </td>
              </tr>
            )}
          </tbody>
          {/* footer (need to make reusable component) */}
          {/* <tfoot className="bg-base-100 ">
          <tr>
            <td colSpan={10000}>

            </td>
          </tr>
        </tfoot> */}
        </table>
      </div>
      {(leftStickyWidth > 0 || rightStickyWidth > 0) && (
        <div
          aria-hidden="true"
          style={{
            position: 'absolute',
            bottom: 0,
            left: 0,
            width: '100%',
            maxWidth: '86svw',
            display: 'flex',
            justifyContent: 'space-between',
            // height: 17,
            pointerEvents: 'none',
            zIndex: 60,
          }}
        >
          {leftStickyWidth > 0 && <div style={{ width: leftStickyWidth, background: 'white', flexShrink: 0 }} />}
          {rightStickyWidth > 0 && (
            <div style={{ width: rightStickyWidth, background: 'white', flexShrink: 0, marginLeft: 'auto' }} />
          )}
        </div>
      )}
      {!disablePagination && (
        <div className="w-full flex items-start bg-white rounded-b-md flex-row flex-wrap items-center gap-2 justify-between py-2 px-6 ">
          <div className="hidden md:flex items-center gap-2">
            <label htmlFor="page-size-select">Row per Page</label>
            <select
              id="page-size-select"
              aria-label="Row per Page"
              className="border border-gray-300 rounded-md px-2 py-1"
              value={currentPageSize}
              onChange={(e) => {
                handlePageSizeChange(Number(e.target.value))
              }}
            >
              {[10, 15, 20, 25, 30, 40, 50].map((pageSize) => (
                <option key={pageSize} value={pageSize}>
                  {pageSize}
                </option>
              ))}
            </select>
          </div>
          <div className="flex items-end flex-row flex-wrap items-center gap-8">
            <div className="flex items-center gap-4">
              <button
                type="button"
                className="w-8 h-8 grid place-content-center border text-center items-center cursor-pointer rounded-md hover:brightness-75"
                onClick={handleFirstPage}
                disabled={!canPreviousPage}
              >
                <BiSkipPrevious size={25} />
              </button>
              <button
                type="button"
                className="w-8 h-8 grid place-content-center border text-center items-center cursor-pointer rounded-md hover:brightness-75"
                onClick={handlePreviousPage}
                disabled={!canPreviousPage}
              >
                <GrFormPrevious size={25} />
              </button>
              <span className="flex items-center gap-1">
                <div>Page</div>
                <strong>
                  {currentPage} of {totalPageCount === 0 ? 1 : totalPageCount}
                </strong>
              </span>
              <button
                type="button"
                className=" w-8 h-8 grid place-content-center border text-center items-center cursor-pointer rounded-md hover:brightness-75"
                onClick={handleNextPage}
                disabled={!canNextPage}
              >
                <GrFormNext size={25} />
              </button>
              <button
                type="button"
                className="w-8 h-8 grid place-content-center border text-center items-center cursor-pointer rounded-md hover:brightness-75"
                onClick={handleLastPage}
                disabled={!canNextPage || currentPage >= totalPageCount}
              >
                <BiSkipNext size={25} />
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  )
}
