import { useEffect, useState, useCallback, useRef } from "react"

const BREAKPOINT_VALUES = {
  xs: 576,
  sm: 768,
  md: 992,
  lg: 1200,
}

const DEFAULT_BREAKPOINTS = ["xs", "sm", "md", "lg"]

/**
 * Use mobile first breakpoints to determine values in a react component.
 *
 * ## Example
 * const columns = useBreakpoints([1, 2, 3])
 * // Here, if the screen is less than 576px, then `columns == 1`.
 * // If it's greater than 576px, but less than 758px, then `columns == 2`.
 * // Ifi it's greater than 768px, then `columns == 3`.
 *
 * @param {Array} values
 * @param {Object} options
 * @param {Array} options.breakpoints
 */
function useResponsiveValue(
  values,
  { breakpoints = DEFAULT_BREAKPOINTS } = {}
) {
  const breakpointValues = breakpoints.map(
    breakpointName => BREAKPOINT_VALUES[breakpointName]
  )

  // if window.matchMedia is not defined (i.e in test environments), this value
  // will end up being empty, which will cause the value to be the default, or
  // the first in the array.
  const mediaQueryLists = useRef(
    window.matchMedia
      ? breakpointValues.map(breakpoint =>
          window.matchMedia(`(min-width: ${breakpoint}px)`)
        )
      : []
  )

  const getValue = useCallback(() => {
    const queryList = [...mediaQueryLists.current]
      .reverse()
      .find(query => query.matches)
    const index = mediaQueryLists.current.indexOf(queryList)

    if (index < 0) {
      return values[0]
    } else {
      return typeof values[index + 1] !== "undefined"
        ? values[index + 1]
        : values[values.length - 1]
    }
  }, [...values]) // anytime `values` changes, we need to update this function

  const [value, setValue] = useState(getValue)

  useEffect(() => {
    const currentQueryLists = mediaQueryLists.current

    const handleMediaQueryEvent = () => {
      setValue(getValue())
    }

    currentQueryLists.forEach(queryList =>
      queryList.addListener(handleMediaQueryEvent)
    )

    handleMediaQueryEvent()

    return () =>
      currentQueryLists.forEach(queryList =>
        queryList.removeListener(handleMediaQueryEvent)
      )
  }, [getValue, ...breakpoints]) // anytime `breakpoints` or `getValue` change, we need to reset our listeners

  return value
}

export default useResponsiveValue