import React, { useState } from 'react'

import { Typography } from 'antd'
import clsx from 'clsx'

export interface ColumnType<RecordType> {
  className?: string
  width?: number
  title: React.ReactNode
  dataIndex: string
  render?: (value: any, record: RecordType, index: number) => React.ReactElement
}

interface Data<RecordType> {
  values: RecordType
  subValues: RecordType[]
}

interface TableProps<RecordType> {
  className?: string
  dataSource: readonly Data<RecordType>[]
  columns: readonly ColumnType<RecordType>[]
  expandable?: Expandable<RecordType>
}

interface Expandable<RecordType> {
  rowExpandable?: (record: Data<RecordType>) => boolean
}

export default function Table<RecordType extends { [k: string]: any } = any>(
  props: TableProps<RecordType>
) {
  const { className, dataSource, columns, expandable } = props

  return (
    <div className={clsx('w-full overflow-x-auto rounded-sm ', className)}>
      <table className='border-collapse'>
        <thead>
          <tr>
            {columns.map((col) => (
              <th
                className='text-left bg-[#fafafa] px-2 py-1 border-r border-solid border-[#f0f0f0]'
                key={col.dataIndex}
                {...(col.width ? { style: { minWidth: col.width } } : {})}
              >
                {col.title}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {dataSource.map((row, index) => {
            return (
              <ExpandableRow
                row={row}
                index={index}
                key={`${index}`}
                columns={columns}
                expandable={expandable}
              />
            )
          })}
        </tbody>
      </table>
    </div>
  )
}

interface RowType<RecordType> {
  row: Data<RecordType>
  index: number
  columns: readonly ColumnType<RecordType>[]
  expandable?: Expandable<RecordType>
  className?: string
}

const ExpandableRow = <T,>({
  row,
  index,
  columns,
  expandable,
  className
}: RowType<T>) => {
  const [expanded, setExpanded] = useState(false)
  const _expandable =
    expandable && (!expandable.rowExpandable || expandable.rowExpandable(row))
  return (
    <>
      <tr className={className}>
        {columns.map((col, index) => {
          const { className: colClassName, dataIndex: keyName, render } = col
          const value = (row.values as { [k: string]: any })[keyName]
          const displayVal = ['string', 'number'].some(
            (type) => typeof value === type
          )
            ? value
            : '-'

          return (
            <td
              key={keyName}
              className={clsx(
                'px-2 py-1 border-r border-solid border-[#f0f0f0]',
                colClassName
              )}
              {...(col.width ? { style: { minWidth: col.width } } : {})}
            >
              <span className='flex justify-between'>
                <span>
                  {render ? render(value, row.values, index) : displayVal}
                </span>
                {index === 0 && _expandable && (
                  <Typography.Link
                    className='ml-4 select-none'
                    onClick={() => setExpanded((v) => !v)}
                  >
                    {expanded ? 'Hide' : 'More'}
                  </Typography.Link>
                )}
              </span>
            </td>
          )
        })}
      </tr>
      {_expandable &&
        expanded &&
        row.subValues.map((subRow, subIndex) => (
          <ExpandableRow
            row={subRow as any}
            index={subIndex}
            key={`${index}.${subIndex}`}
            columns={columns}
            className='bg-[#fafafa]'
          />
        ))}
    </>
  )
}
