import React, { forwardRef, MouseEvent, ReactNode, useMemo } from 'react'
import PropTypes from 'prop-types'

import { CTableBody, CTableBodyProps } from '../table/CTableBody'
import { CFormCheck } from '../form'
import { CTableDataCell, CTableRow } from '../table'

import { isObjectInArray } from '../../utils'

import { ITEM_INTERNAL_KEYS } from './consts'
import type { Column, Item, ScopedColumns } from './types'
import {
  getClickedColumnName,
  getColumnName,
  getColumns,
  getTableDataCellProps,
  getTableDataCellStyles,
} from './utils'

export interface CSmartTableBodyProps extends CTableBodyProps {
  clickableRows?: boolean
  columnNames: string[]
  columns: (Column | string)[]
  currentItems: Item[]
  firstItemOnActivePageIndex: number
  noItemsLabel?: string | ReactNode
  onRowChecked?: (item: Item, value: boolean) => void
  onRowClick?: (item: Item, index: number, columnName: string, event: MouseEvent | boolean) => void
  scopedColumns?: ScopedColumns
  selectable?: boolean
  selected?: Item[]
}

export const CSmartTableBody = forwardRef<HTMLTableSectionElement, CSmartTableBodyProps>(
  (
    {
      clickableRows,
      columnNames,
      columns,
      currentItems,
      firstItemOnActivePageIndex,
      noItemsLabel,
      onRowChecked,
      onRowClick,
      scopedColumns,
      selectable,
      selected,
      ...rest
    },
    ref,
  ) => {
    const colspan: number = selectable ? columns.length + 1 : columns.length
    const _columns = useMemo(() => getColumns(columns), [columns])
    return (
      <CTableBody
        {...(clickableRows && {
          style: { cursor: 'pointer' },
        })}
        {...rest}
        ref={ref}
      >
        {currentItems.length > 0 ? (
          currentItems.map((item: Item, trIndex) => {
            return (
              <React.Fragment key={trIndex}>
                <CTableRow
                  {...(item._props && { ...item._props })}
                  {...(clickableRows && { tabIndex: 0 })}
                  onClick={(event) =>
                    onRowClick &&
                    onRowClick(
                      item,
                      trIndex + firstItemOnActivePageIndex,
                      getClickedColumnName(
                        event.target as HTMLElement,
                        columnNames,
                        selectable,
                      ),
                      event,
                    )
                  }
                >
                  {selectable && (
                    <CTableDataCell>
                      <CFormCheck
                        checked={
                          selected &&
                          isObjectInArray(selected, item, ITEM_INTERNAL_KEYS)
                        }
                        disabled={item._selectable === false}
                        onChange={(event) => {
                          const _item = { ...item }
                          for (const key of ITEM_INTERNAL_KEYS ) {
                            delete _item[key]
                          }
                          onRowChecked && onRowChecked(_item, event.target.checked)
                        }}
                      />
                    </CTableDataCell>
                  )}
                  {_columns.map((column, index) => {
                    const colName = getColumnName(column)
                    return (
                      (scopedColumns &&
                        scopedColumns[colName] &&
                        React.cloneElement(
                          scopedColumns[colName](item, trIndex + firstItemOnActivePageIndex),
                          {
                            key: index,
                          },
                        )) ||
                      (item[colName] !== undefined && (
                        <CTableDataCell
                          {...getTableDataCellProps(column, item, colName)}
                          style={getTableDataCellStyles(column, item, colName)}
                          key={index}
                        >
                          {item[colName]}
                        </CTableDataCell>
                      ))
                    )
                  })}
                </CTableRow>
                {scopedColumns && scopedColumns.details && (
                  <>
                    <CTableRow>
                      <CTableDataCell
                        colSpan={colspan}
                        className="p-0"
                        style={{ borderBottomWidth: 0 }}
                        tabIndex={-1}
                      ></CTableDataCell>
                    </CTableRow>
                    <CTableRow className="p-0" key={`details${trIndex}`}>
                      <CTableDataCell colSpan={colspan} className="p-0" style={{ border: 0 }}>
                        {scopedColumns.details(item, trIndex + firstItemOnActivePageIndex)}
                      </CTableDataCell>
                    </CTableRow>
                  </>
                )}
              </React.Fragment>
            )
          })
        ) : (
          <CTableRow>
            <CTableDataCell colSpan={colspan}>{noItemsLabel}</CTableDataCell>
          </CTableRow>
        )}
      </CTableBody>
    )
  },
)

CSmartTableBody.propTypes = {
  clickableRows: PropTypes.bool,
  columns: PropTypes.array.isRequired,
  currentItems: PropTypes.array.isRequired,
  firstItemOnActivePageIndex: PropTypes.number.isRequired,
  noItemsLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
  onRowChecked: PropTypes.func,
  onRowClick: PropTypes.func,
  columnNames: PropTypes.array.isRequired,
  scopedColumns: PropTypes.object,
  selectable: PropTypes.bool,
  selected: PropTypes.array,
}

CSmartTableBody.displayName = 'CSmartTableBody'
