import React from 'react'
import clsx from 'clsx'
import usePagination from './hook'
import type {
  ButtonProps,
  IPagination,
  IPaginationProps,
  PageButtonProps,
} from './type'

const defaultState: IPagination = {
  currentPage: 0,
  setCurrentPage: () => {},
  truncableText: '...',
  truncableClassName: '',
  pages: [],
  hasPreviousPage: false,
  hasNextPage: false,
  previousPages: [],
  isPreviousTruncable: false,
  middlePages: [],
  isNextTruncable: false,
  nextPages: [],
}

const PaginationContext: React.Context<IPagination> = React.createContext<IPagination>(defaultState)

export const PrevButton = ({
  className,
  children,
  dataTestId,
  as = <button />,
  ...buttonProps
}: ButtonProps) => {
  const pagination = React.useContext(PaginationContext)
  const previous = () => {
    if (pagination.currentPage + 1 > 1)
      pagination.setCurrentPage(pagination.currentPage - 1)
  }

  const disabled = pagination.currentPage === 0

  return (
    <as.type
      {...buttonProps}
      {...as.props}
      className={clsx(className, as.props.className)}
      onClick={() => previous()}
      tabIndex={disabled ? '-1' : 0}
      disabled={disabled}
      data-testid={dataTestId}
      onKeyPress={(event: React.KeyboardEvent) => {
        event.preventDefault()
        if (event.key === 'Enter' && !disabled)
          previous()
      }}
    >
      {as.props.children ?? children}
    </as.type>
  )
}

export const NextButton = ({
  className,
  children,
  dataTestId,
  as = <button />,
  ...buttonProps
}: ButtonProps) => {
  const pagination = React.useContext(PaginationContext)
  const next = () => {
    if (pagination.currentPage + 1 < pagination.pages.length)
      pagination.setCurrentPage(pagination.currentPage + 1)
  }

  const disabled = pagination.currentPage === pagination.pages.length - 1

  return (
    <as.type
      {...buttonProps}
      {...as.props}
      className={clsx(className, as.props.className)}
      onClick={() => next()}
      tabIndex={disabled ? '-1' : 0}
      disabled={disabled}
      data-testid={dataTestId}
      onKeyPress={(event: React.KeyboardEvent) => {
        event.preventDefault()
        if (event.key === 'Enter' && !disabled)
          next()
      }}
    >
      {as.props.children ?? children}
    </as.type>
  )
}

type ITruncableElementProps = {
  prev?: boolean
}

const TruncableElement = ({ prev }: ITruncableElementProps) => {
  const pagination: IPagination = React.useContext(PaginationContext)

  const {
    isPreviousTruncable,
    isNextTruncable,
    truncableText,
    truncableClassName,
  } = pagination

  return ((isPreviousTruncable && prev === true) || (isNextTruncable && !prev))
    ? (
      <li className={truncableClassName || undefined}>{truncableText}</li>
    )
    : null
}

export const PageButton = ({
  as = <a />,
  className,
  dataTestIdActive,
  dataTestIdInactive,
  activeClassName,
  inactiveClassName,
  renderExtraProps,
}: PageButtonProps) => {
  const pagination: IPagination = React.useContext(PaginationContext)

  const renderPageButton = (page: number) => (
    <li key={page}>
      <as.type
        data-testid={
          clsx({
            [`${dataTestIdActive}`]:
              dataTestIdActive && pagination.currentPage + 1 === page,
            [`${dataTestIdInactive}-${page}`]:
              dataTestIdActive && pagination.currentPage + 1 !== page,
          }) || undefined
        }
        tabIndex={0}
        onKeyPress={(event: React.KeyboardEvent) => {
          if (event.key === 'Enter')
            pagination.setCurrentPage(page - 1)
        }}
        onClick={() => pagination.setCurrentPage(page - 1)}
        className={clsx(
          className,
          pagination.currentPage + 1 === page
            ? activeClassName
            : inactiveClassName,
        )}
        {...as.props}
        {...(renderExtraProps ? renderExtraProps(page) : {})}
      >
        {page}
      </as.type>
    </li>
  )

  return (
    <>
      {pagination.previousPages.map(renderPageButton)}
      <TruncableElement prev />
      {pagination.middlePages.map(renderPageButton)}
      <TruncableElement />
      {pagination.nextPages.map(renderPageButton)}
    </>
  )
}

export const Pagination = ({
  dataTestId,
  ...paginationProps
}: IPaginationProps & { dataTestId?: string }) => {
  const pagination = usePagination(paginationProps)

  return (
    <PaginationContext.Provider value={pagination}>
      <div className={paginationProps.className} data-testid={dataTestId}>
        {paginationProps.children}
      </div>
    </PaginationContext.Provider>
  )
}

Pagination.PrevButton = PrevButton
Pagination.NextButton = NextButton
Pagination.PageButton = PageButton