import React, {
  useEffect,
  useLayoutEffect,
  useRef,
  useState,
  createRef,
} from "react"
import PropTypes from "prop-types"
import Button from "../button"

function usePrevious(value) {
  const ref = useRef()

  useEffect(() => {
    ref.current = value
  }, [value])

  return ref.current
}

function useArrayRef(length) {
  const ref = useRef([])

  if (ref.current.length !== length) {
    ref.current = Array(length)
      .fill()
      .map((_, i) => ref.current[i] || createRef())
  }

  return ref
}

function Text({ visible, fixed, children, setWidth, style, ...props }, ref) {
  const transition = { transition: "opacity 200ms ease-in-out" }

  const showing = { opacity: 1, ...transition, ...style }
  const hidden = { opacity: 0, ...transition, ...style }
  const className = fixed ? "absolute left-0" : ""

  return (
    <span
      ref={ref}
      className={className}
      style={visible ? showing : hidden}
      {...props}
    >
      {children}
    </span>
  )
}

Text = React.forwardRef(Text)

function SaveButton({ children, loading, labels = {}, ...props }) {
  const [state, setState] = useState("initial")
  const [lastTransition, setLastTransition] = useState(0)
  const previousLoading = usePrevious(loading)
  const [width, setWidth] = useState("auto")
  const texts = useArrayRef(3)

  const finish = () => {
    setState("done")
    setTimeout(() => {
      setState("initial")
    }, 1500)
  }

  useLayoutEffect(() => {
    const max = texts.current.sort(
      (a, b) => b.current.offsetWidth - a.current.offsetWidth
    )[0].current.offsetWidth

    setWidth(max)
  }, [])

  useEffect(() => {
    if (loading && !previousLoading) {
      setState("loading")
      setLastTransition(new Date().getTime())
    } else if (!loading && previousLoading) {
      const time = new Date().getTime()
      const elapsed = time - lastTransition
      const timeout = Math.max(1000 - elapsed, 0)

      setTimeout(finish, timeout)
    }
  }, [loading, previousLoading, lastTransition])

  return (
    <Button
      is="primary"
      {...props}
      disabled={state !== "initial"}
      aria-label={children}
    >
      <span className="relative block text-center" style={{ width }}>
        <Text
          ref={texts.current[0]}
          visible={state === "initial"}
          style={{ width }}
        >
          {children}
        </Text>
        <Text
          ref={texts.current[1]}
          fixed
          visible={state === "loading"}
          style={{ width }}
        >
          {labels.loading || "Saving"}
        </Text>
        <Text
          ref={texts.current[2]}
          fixed
          visible={state === "done"}
          style={{ width }}
        >
          {labels.complete || "Done"}
        </Text>
      </span>
    </Button>
  )
}

SaveButton.propTypes = {
  children: PropTypes.string,
  loading: PropTypes.bool,
  labels: PropTypes.object,
}

export default SaveButton
