import React from 'react'
import PropTypes from 'prop-types'
import {
  selectSystemProps,
  useSafeLayoutEffect,
  useThemeTokens,
  getTokensPropType
} from '@telus-uds/components-base'
import throttle from 'lodash.throttle'
import styled from 'styled-components'
import { htmlAttrs } from '../utils'

const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])

const StyledContainer = styled.div`
  overflow: auto;
  padding-bottom: ${(props) => (props.$isScrollable ? `${props.$tablePaddingBottom}px` : 0)};
`

const StyledTable = styled.table`
  margin: 0;
  padding: 0;
  width: ${({ $fullWidth, $tableWidth }) => ($fullWidth ? '100%' : `${$tableWidth}px`)};
  border: ${(props) => `${props.$borderWidth}px solid ${props.$borderColor}`};
  border-collapse: collapse;
`

const TableContext = React.createContext({})

export const useTableContext = () => React.useContext(TableContext)

/**
 * Use `Table` to display tabular data.
 *
 * - Takes an optional `spacing` variant (compact)
 * - Available in 2 text styles (medium, small)
 * - When `Table` exceeds viewport, the first column becomes sticky, enabling horizontal scrolling
 * - Right-align prices and numbers that display decimal points
 *
 * ### Building up a `Table`
 * - Use `TableHeader` and `TableBody` as direct children of `Table`
 * - Use `TableSubHeading` to render an intermediate data heading row
 * - Use `TableRow` and `TableCell` to build up the tabular data
 * - Use `TableCell`'s `type` prop to visually mark it as a row heading (`type="rowHeading"`)
 */
export const Table = React.forwardRef(
  (
    { children, spacing = 'default', fullWidth = true, text = 'medium', tokens, variant, ...rest },
    ref
  ) => {
    const themeTokens = useThemeTokens('Table', tokens, variant)
    const { tablePaddingBottom, borderColor, borderWidth } = themeTokens
    const internalRef = React.useRef()
    const containerRef = ref ?? internalRef
    const tableRef = React.useRef()

    const [containerWidth, setContainerWidth] = React.useState(0)
    const [tableWidth, setTableWidth] = React.useState(0)

    useSafeLayoutEffect(() => {
      const updateDimensions = () => {
        const containerClientWidth = containerRef.current?.clientWidth
        const responsiveTableWidth = fullWidth
          ? containerClientWidth
          : tableRef.current?.clientWidth
        setContainerWidth(containerClientWidth)
        setTableWidth(responsiveTableWidth)
      }

      const throttledUpdateDimensions = throttle(updateDimensions, 100, { leading: false })

      updateDimensions()

      // a pretty naive way of repeating the measurement after the fonts are loaded
      window.addEventListener('load', updateDimensions)
      window.addEventListener('resize', throttledUpdateDimensions)

      return () => {
        window.removeEventListener('load', updateDimensions)
        window.removeEventListener('resize', throttledUpdateDimensions)
      }
    }, [])

    const isScrollable = tableWidth > containerWidth
    return (
      <StyledContainer
        ref={containerRef}
        $isScrollable={isScrollable}
        $tablePaddingBottom={tablePaddingBottom}
      >
        <TableContext.Provider
          value={{
            text,
            isScrollable,
            tokens,
            spacing,
            variant,
            themeTokens
          }}
        >
          <StyledTable
            $borderColor={borderColor}
            $borderWidth={borderWidth}
            $tableWidth={tableWidth}
            cellSpacing={0}
            ref={tableRef}
            $fullWidth={fullWidth}
            {...selectProps(rest)}
          >
            {children}
          </StyledTable>
        </TableContext.Provider>
      </StyledContainer>
    )
  }
)

Table.displayName = 'Table'

Table.propTypes = {
  ...selectedSystemPropTypes,
  /** Theme tokens for Table */
  tokens: getTokensPropType('Table'),
  /** Table content (rows, headers, etc) */
  children: PropTypes.node,
  /** Sets text style */
  text: PropTypes.oneOf(['medium', 'small']),
  /** Table spacing variant */
  spacing: PropTypes.oneOf(['default', 'compact']),
  /** If true, table takes 100% width of container */
  fullWidth: PropTypes.bool,
  /** Variant for Table styling */
  variant: PropTypes.object
}
