import React, { useState, useMemo, useEffect, Children } from "react"
import PropTypes from "prop-types"
import {
  createColumnHelper,
  flexRender,
  getCoreRowModel,
  getExpandedRowModel,
  getSortedRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  useReactTable,
} from "@tanstack/react-table"
import cx from "classnames"
import camelCase from "lodash.camelcase"
import CKIcon from "../ck-icon"
import Pagination from "./pagination"
import DataTableContext from "./context"
import ClientSidePagination from "./client-side-pagination"
import { IconButton } from "../button"

export default function DataTable({
  children,
  columns,
  data,
  search = null,
  selectableRows = false,
  paginateExpandedRows = false,
  rowSelection = {},
  setRowSelection = () => {},
}) {
  const columnHelper = createColumnHelper()
  const [sorting, setSorting] = useState([])
  const [globalFilter, setGlobalFilter] = useState("")

  useEffect(() => {
    setGlobalFilter(search)
  }, [search])

  const dataColumns = useMemo(
    () =>
      columns.map((column, index) => {
        const accessor = column.id ? column.id : camelCase(column.header)
        return columnHelper.accessor(accessor, {
          accessorKey: accessor,
          enableGlobalFilter: !!column.searchable,
          cell: (info) => {
            const showExpandButton =
              index === 0 &&
              table.getCanSomeRowsExpand() &&
              info.cell.row.depth === 0

            const isFirstColumnInParentRow =
              index === 0 && info.cell.row.depth === 0
            const isFirstColumnInSubRow = index === 0 && info.cell.row.depth > 0

            return (
              <div className={`flex flex-row gap-4 items-center`}>
                {showExpandButton ? (
                  <IconButton
                    icon={info.cell.row.getIsExpanded() ? "down" : "right"}
                    is="plain"
                    disabled={!info.cell.row.getCanExpand()}
                    label={
                      info.cell.row.getIsExpanded()
                        ? "Collapse row"
                        : "Expand row"
                    }
                    {...{
                      onClick: info.cell.row.getToggleExpandedHandler(),
                    }}
                    className={
                      "-ml-4 items-center rounded-full text-gray-800 hover:bg-gray-50"
                    }
                  />
                ) : null}
                <div
                  className={cx({
                    "text-gray-700": info.cell.row.depth > 0,
                    "font-semibold": isFirstColumnInParentRow,
                  })}
                  style={{ marginLeft: isFirstColumnInSubRow ? 42 : 0 }}
                >
                  {column.cell
                    ? column.cell(info.getValue(), info.cell.row.original)
                    : info.getValue()}
                </div>
              </div>
            )
          },
          header: () => column.header,
          enableSorting: !!column.sortable,
        })
      }),
    []
  )

  if (selectableRows) {
    dataColumns.unshift({
      id: "select",
      header: ({ table }) => (
        <IndeterminateCheckbox
          {...{
            checked: table.getIsAllRowsSelected(),
            indeterminate: table.getIsSomeRowsSelected(),
            onChange: table.getToggleAllRowsSelectedHandler(),
          }}
        />
      ),
      cell: ({ row }) => (
        <IndeterminateCheckbox
          {...{
            checked: row.getIsSelected(),
            disabled: !row.getCanSelect(),
            indeterminate: row.getIsSomeSelected(),
            onChange: row.getToggleSelectedHandler(),
          }}
        />
      ),
    })
  }

  const paginationSlot = Children.toArray(children).find(
    (child) => child.type === Pagination || child.type === ClientSidePagination
  )
  const [expanded, setExpanded] = React.useState({})

  /* eslint-disable react/prop-types */
  const table = useReactTable({
    data,
    columns: dataColumns,
    state: {
      rowSelection,
      sorting,
      globalFilter,
      expanded,
    },
    getSubRows: (row) => row.subRows,
    onExpandedChange: setExpanded,
    enableRowSelection: selectableRows,
    onSortingChange: setSorting,
    onGlobalFilterChange: setGlobalFilter,
    onRowSelectionChange: setRowSelection,
    getCoreRowModel: getCoreRowModel(),
    getExpandedRowModel: getExpandedRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    paginateExpandedRows: paginateExpandedRows,
    manualPagination: paginationSlot?.type !== ClientSidePagination,
    debugTable: true,
  })

  return (
    <DataTableContext.Provider value={{ table, data }}>
      <div>
        <table className="w-full">
          <thead className="bg-gray-50 rounded">
            {table.getHeaderGroups().map((headerGroup) => {
              return (
                <tr key={headerGroup.id} className="rounded">
                  {headerGroup.headers.map((header) => (
                    <th
                      key={header.id}
                      className={cx(
                        "text-gray-700 text-xs text-left font-semibold uppercase py-2 px-4 h-10 whitespace-no-wrap",
                        {
                          "rounded-l w-full":
                            header.index == 0 && !selectableRows,
                          "w-1/3": header.index == 1 && selectableRows,
                          "rounded-r": header.index == dataColumns.length - 1,
                        }
                      )}
                    >
                      {header.isPlaceholder ? null : header.column.getCanSort() ? (
                        <button
                          {...{
                            onClick: header.column.getToggleSortingHandler(),
                          }}
                          className={cx(
                            "-mx-1 px-1 flex flex-row items-center leading-6 font-semibold uppercase hover:text-gray-800",
                            {
                              "text-gray-800": header.column.getIsSorted(),
                              "text-gray-700": !header.column.getIsSorted(),
                            }
                          )}
                        >
                          {flexRender(
                            header.column.columnDef.header,
                            header.getContext()
                          )}
                          {{
                            asc: (
                              <CKIcon
                                name="arrow-up"
                                size="sm"
                                className="ml-1"
                              />
                            ),
                            desc: (
                              <CKIcon
                                name="arrow-down"
                                size="sm"
                                className="ml-1"
                              />
                            ),
                          }[header.column.getIsSorted()] ?? null}
                        </button>
                      ) : (
                        <span>
                          {flexRender(
                            header.column.columnDef.header,
                            header.getContext()
                          )}
                        </span>
                      )}
                    </th>
                  ))}
                </tr>
              )
            })}
          </thead>
          <tbody>
            {table.getRowModel().rows.map((row) => (
              <tr
                key={row.id}
                className={cx("group h-16", {
                  "hover:bg-gray-50": selectableRows,
                })}
              >
                {row.getVisibleCells().map((cell, idx) => {
                  return (
                    <td
                      key={cell.id}
                      className={cx("text-gray-800 text-sm py-2 px-4", {
                        "rounded-l": idx == 0,
                        "rounded-r": idx == dataColumns.length - 1,
                      })}
                    >
                      {flexRender(
                        cell.column.columnDef.cell,
                        cell.getContext()
                      )}
                    </td>
                  )
                })}
              </tr>
            ))}
          </tbody>
        </table>
        {paginationSlot}
      </div>
    </DataTableContext.Provider>
  ) /* eslint-enable react/prop-types */
}

function IndeterminateCheckbox({ indeterminate, className = "", ...rest }) {
  const ref = React.useRef(null)

  React.useEffect(() => {
    if (typeof indeterminate === "boolean") {
      ref.current.indeterminate = !rest.checked && indeterminate
    }
  }, [ref, indeterminate])

  return (
    <input
      type="checkbox"
      ref={ref}
      className={cx(
        className,
        "design-custom-box",
        "design-checkbox",
        "text-base",
        "cursor-pointer"
      )}
      {...rest}
    />
  )
}

DataTable.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node,
  ]),
  columns: PropTypes.arrayOf(PropTypes.object).isRequired,
  data: PropTypes.arrayOf(PropTypes.object).isRequired,
  search: PropTypes.any,
  selectableRows: PropTypes.bool,
  rowSelection: PropTypes.object,
  setRowSelection: PropTypes.func,
  paginateExpandedRows: PropTypes.bool
}
DataTable.Pagination = Pagination
DataTable.ClientSidePagination = ClientSidePagination
