import React from 'react'
import { Platform } from 'react-native'
import PropTypes from 'prop-types'
import { useThemeTokens } from '../ThemeProvider/useThemeTokens'
import { StackView } from '../StackView/StackView'
import { a11yProps } from '../utils/props/a11yProps'
import { getTokensPropType } from '../utils/props/tokens'
import { focusHandlerProps } from '../utils/props/handlerProps'
import { pressProps } from '../utils/props/pressProps'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { useHash } from '../utils/useHash'
import { useInputValue } from '../utils/input'
import { useResponsiveProp } from '../utils/useResponsiveProp'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'
import { withLinkRouter } from '../utils/withLinkRouter'
import { HorizontalScroll, horizontalScrollUtils } from '../HorizontalScroll/HorizontalScroll'
import { HorizontalScrollButton } from '../HorizontalScroll/HorizontalScrollButton'
import { TabsItem } from './TabsItem'
import { TabsDropdown } from './TabsDropdown'

const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
  a11yProps,
  focusHandlerProps,
  pressProps,
  viewProps
])

const { selectHorizontalScrollTokens, useItemPositions } = horizontalScrollUtils

const getDefaultTabItemAccessibilityRole = (parentRole) => {
  switch (parentRole) {
    case 'tablist':
      return 'tab'
    case 'navigation':
      return 'link'
    default:
      return undefined
  }
}

const getStackViewTokens = (variant) => {
  const equalWidth = variant?.equalWidth
  return Platform.select({
    web: {
      justifyContent: equalWidth ? 'space-evenly' : 'flex-start',
      width: equalWidth ? '100%' : 'auto'
    },
    default: {}
  })
}

/**
 * Tabs renders a horizontally-scrolling menu of selectable buttons which may link
 * to a page or control what content is displayed on this page.
 *
 * By default, Tabs always renders as horizontal scrolling tabs regardless of viewport.
 * To enable dropdown mode, you must explicitly pass `variant={{ dropdown: true }}`.
 * When dropdown is enabled, it will only render as a dropdown on mobile and tablet
 * viewports (XS and SM). On larger viewports (MD, LG, XL), it will still render as
 * horizontal tabs even with dropdown enabled.
 *
 * If you are using Tabs to navigate to a new page (web-only) you should pass
 * `navigation`as the `accessibilityRole` to te Tabs component, this will cause
 * TabItems to default to a role of link and obtain aria-current behaviour.
 */
export const Tabs = React.forwardRef(
  (
    {
      tokens,
      itemTokens,
      scrollButtonTokens,
      variant,
      value,
      initialValue,
      onChange,
      items = [],
      LinkRouter,
      linkRouterProps,
      ...rest
    },
    ref
  ) => {
    const { space, ...themeTokens } = useThemeTokens('Tabs', tokens, variant)
    const { currentValue, setValue } = useInputValue({ value, initialValue, onChange })

    const [itemPositions, isPositioningReady] = useItemPositions()

    useHash(
      React.useCallback(
        (hash, event) => {
          const hashItem = hash && items.find(({ href }) => hash === href)
          const hashId = hashItem && (hashItem.id || hashItem.label)
          if (hashId) setTimeout(setValue(hashId, event), 500)
        },
        [items, setValue]
      ),
      isPositioningReady
    )

    const restProps = selectProps(rest)
    const parentAccessibilityRole = restProps.accessibilityRole ?? 'tablist'
    const defaultTabItemAccessibiltyRole =
      getDefaultTabItemAccessibilityRole(parentAccessibilityRole)
    const stackViewTokens = getStackViewTokens(variant)

    // Render dropdown only if explicitly requested via variant AND viewport is xs or sm
    const isSmallViewport = useResponsiveProp(
      { xs: true, sm: true, md: false, lg: false, xl: false },
      false
    )
    const shouldRenderDropdown = variant?.dropdown === true && isSmallViewport

    if (shouldRenderDropdown) {
      return (
        <TabsDropdown
          ref={ref}
          tokens={tokens}
          itemTokens={itemTokens}
          variant={variant}
          value={currentValue}
          onChange={setValue}
          items={items}
          LinkRouter={LinkRouter}
          linkRouterProps={linkRouterProps}
          accessibilityRole={
            parentAccessibilityRole === 'tablist' ? 'button' : parentAccessibilityRole
          }
          {...restProps}
        />
      )
    }
    return (
      <HorizontalScroll
        ref={ref}
        ScrollButton={HorizontalScrollButton}
        itemPositions={itemPositions}
        tokens={selectHorizontalScrollTokens(themeTokens)}
        scrollButtonTokens={scrollButtonTokens}
        accessibilityRole={parentAccessibilityRole}
        {...restProps}
      >
        <StackView space={space} direction="row" tokens={stackViewTokens}>
          {items.map(
            (
              {
                href,
                label,
                id,
                accessibilityRole = defaultTabItemAccessibiltyRole,
                onPress,
                ref: itemRef,
                LinkRouter: ItemLinkRouter = LinkRouter,
                linkRouterProps: itemLinkRouterProps,
                render,
                ...itemRest
              },
              index
            ) => {
              const itemId = id ?? label
              const isSelected = Boolean(currentValue && currentValue === itemId)
              const handlePress = (event) => {
                if (typeof onPress === 'function') onPress(event)
                setValue(itemId, event)
              }

              const itemProps = selectItemProps(itemRest)
              return (
                <TabsItem
                  ref={itemRef}
                  key={itemId}
                  id={itemId}
                  href={href}
                  variant={variant}
                  tokens={itemTokens}
                  onPress={handlePress}
                  selected={isSelected}
                  itemPositions={itemPositions}
                  index={index}
                  accessibilityRole={accessibilityRole}
                  LinkRouter={ItemLinkRouter}
                  linkRouterProps={{ ...linkRouterProps, ...itemLinkRouterProps }}
                  render={render}
                  {...itemProps}
                >
                  {label}
                </TabsItem>
              )
            }
          )}
        </StackView>
      </HorizontalScroll>
    )
  }
)
Tabs.displayName = 'Tabs'

Tabs.propTypes = {
  ...selectedSystemPropTypes,
  ...withLinkRouter.propTypes,
  /**
   * Array of `TabsItem`s
   */
  items: PropTypes.arrayOf(
    PropTypes.shape({
      ...selectedItemPropTypes,
      ...withLinkRouter.propTypes,
      href: PropTypes.string,
      label: PropTypes.string,
      id: PropTypes.string,
      ref: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({ current: PropTypes.any })]),
      render: PropTypes.func
    })
  ),
  /**
   * `id` property of the current tab in the items array
   */
  value: PropTypes.string,
  /**
   * `id` of the tab selected initially when `Tabs` is uncontrolled.
   * Updating `initialValue` will **not** change the selected tab.
   */
  initialValue: PropTypes.string,
  /**
   * Callback for when the selected tab changes
   */
  onChange: PropTypes.func,
  tokens: getTokensPropType('Tabs'),
  /**
   * Custom tokens for `TabsItem`
   */
  itemTokens: getTokensPropType('TabsItem'),
  /**
   * Custom tokens for `HorizontalScrollButton`
   */
  scrollButtonTokens: getTokensPropType('HorizontalScrollButton'),
  variant: variantProp.propType
}
