import React, { useState, useEffect, useContext } from "react"
import { useId } from "@floating-ui/react"
import PropTypes from "prop-types"
import cx from "classnames"
import IconButton from "./icon-button"
import ButtonGroup from "./button-group"
import CKIcon from "../ck-icon"
import { ButtonGroupContext } from "./button-group"

const SIZES = {
  sm: "text-sm py-1 px-3",
  md: "text-sm py-2 px-4 h-10",
  lg: "text-sm py-3 px-5",
  icon: "p-2 h-10",
}

const GROUP_SIZES = {
  "horizontal-sm": "h-full px-2",
  "horizontal-md": "h-full px-3",
  "horizontal-lg": "h-full px-4",
  "vertical-sm": "p-1 w-full",
  "vertical-md": "p-1 w-full",
  "vertical-lg": "p-2 w-full",
}

export const TOKENS = {
  primary: "text-white hover:text-white focus:text-white bg-red-500 hover:bg-red-600",
  secondary: "text-gray-800 bg-white hover:bg-gray-50 border-gray-200",
  tertiary: "text-gray-800 bg-transparent hover:bg-gray-50",
  plain: "",
}

const DISABLED_CLASSES = "text-gray-600 bg-gray-200"

const COLOR_FALLBACK = {
  red: TOKENS["primary"],
  white: TOKENS["secondary"],
  plain: TOKENS["tertiary"],
  gray: TOKENS["secondary"],
  yellow: "text-white bg-yellow-500 hover:bg-yellow-600",
  blue: "text-white bg-blue-500 hover:bg-blue-600",
  green: "text-white bg-green-500 hover:bg-green-600",
  "light-blue": "text-gray-900 bg-blue-200 hover:bg-blue-300",
  "light-green": "text-gray-900 bg-green-200 hover:bg-green-300 focus:bg-green-300",
  "light-yellow": "text-gray-900 bg-yellow-200 hover:bg-yellow-300",
  "light-red": "text-gray-900 bg-red-200 hover:bg-red-300",
}

/**
 * @param {Object} props
 * @param {"primary"|"secondary"|"tertiary"|"plain"} props.is
 * @param {"sm"|"md"|"lg"} props.size
 * @param {string} props.icon
 * @param {boolean} props.disabled
 * @param className any custom classes to be applied to button
 */
const Button = React.forwardRef(
  (
    {
      is,
      color,
      size = "md",
      icon,
      disabled = false,
      className,
      children,
      onMouseDown = () => {},
      onBlur = () => {},
      onClick,
      active,
      ...props
    },
    ref
  ) => {
    const id = useId()
    const groupContext = useContext(ButtonGroupContext)
    const [clicked, setClicked] = useState(false)
    const DEFAULT = `leading-snug rounded border border-solid border-transparent no-underline font-sans font-semibold transition duration-200 hover:no-underline focus:no-underline focus:outline-none align-middle text-center`
    const isDisabled = disabled && (Object.keys(TOKENS).includes(is) || color || groupContext.present)
    const usesTokenStyles = Object.keys(TOKENS).includes(is) || isDisabled || color
    const isCurrentActiveGroupMember =  groupContext.activeId === id || null
    const classes = cx(
      className,
      disabled ? "cursor-default" : "cursor-pointer",
      {
        [DEFAULT]: usesTokenStyles || groupContext.present,
        [SIZES[size]]: usesTokenStyles && !groupContext.present,
        [COLOR_FALLBACK[color]]: color && !isDisabled && !groupContext.present,
        [TOKENS[is]]: is && !isDisabled && !groupContext.present,
        [DISABLED_CLASSES]: isDisabled,
        "focus:outline-blue": !clicked,
        "inline-block": props.href,
        "inline-flex items-center justify-center": Boolean(icon) && usesTokenStyles,
        [GROUP_SIZES[groupContext.groupSize]]: groupContext.present && size !== "icon",
        "h-10 w-10": groupContext.present && size === "icon",
        "hover:bg-gray-50": groupContext.present && !isDisabled,
        "bg-gray-100": isCurrentActiveGroupMember,
      }
    )

    // Set active state in button group
    useEffect(() => {
      if(!groupContext.present) return
      if(active) {
        groupContext.setActiveId(id)
      }
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [active])

    const handleMouseDown = (event) => {
      setClicked(true)
      onMouseDown(event)
    }

    const handleBlur = (event) => {
      setClicked(false)
      onBlur(event)
    }

    const handleClick = event => {
      if (groupContext.setActiveId) {
        groupContext.setActiveId(id)
      }
      if (onClick) onClick(event)
    }

    const Component = props.href ? "a" : "button"

    return (
      <Component
        {...props}
        ref={ref}
        className={classes}
        disabled={disabled}
        onMouseDown={handleMouseDown}
        onBlur={handleBlur}
        onClick={handleClick}
        aria-pressed={isCurrentActiveGroupMember}
      >
        {icon && (
          <CKIcon name={icon} className="mr-2 pointer-events-none" />
        )}
        {children}
      </Component>
    )
  }
)

Button.propTypes = {
  type: PropTypes.oneOf(["button", "submit", "reset"]),
  className: PropTypes.string,
  children: PropTypes.node,
  is: PropTypes.oneOf([
    "primary", // main color
    "secondary", // white
    "tertiary", // transparent with bg on hover
    "plain", // sets defaults without color for easy customization
  ]),
  /** Temporary fallback for "is" prop */
  color: PropTypes.oneOf([
    "red",
    "white",
    "plain",
  ]),
  disabled: PropTypes.bool,
  size: PropTypes.oneOf(Object.keys(SIZES)),
  icon: PropTypes.elementType,
  href: PropTypes.string,
  /** Used to manage active state in button group */
  active: PropTypes.bool,
  onMouseDown: PropTypes.func,
  onBlur: PropTypes.func,
  onClick: PropTypes.func,
}

export { ButtonGroup, IconButton }

export default Button
