import * as React from 'react'
import Transition, { TransitionStatus } from 'react-transition-group/Transition'

export const getDuration = (timeout: TimeoutType | number): TimeoutType => {
  if (typeof timeout === 'object') {
    return timeout as TimeoutType
  }
  return { enter: timeout, exit: timeout }
}

export const STATES = {
  ENTERED: 'entered',
  ENTERING: 'entering',
  EXITING: 'exiting',
  EXITED: 'exited',
}
export const isEntered = (state: string) => state === STATES.ENTERED
export const isEntering = (state: string) => state === STATES.ENTERING
export const isExiting = (state: string) => state === STATES.EXITING
export const isExited = (state: string) => state === STATES.EXITED
export type TimeoutType = {
  enter: number
  exit: number
}
export type AnimationProps = {
  timeout: TimeoutType | number
  in?: boolean
  children: (props: TransitionProps) => React.ReactNode
}

type AnimationState = {
  enter: number
  exit: number
}

export type TransitionProps = {
  entering: boolean
  entered: boolean
  duration: number
  exiting: boolean
  exited: boolean
  state: TransitionStatus
}
/* Animation component which behaves as state machine for
 * animating entering and exiting behaviours in nice and easy way
 * usually used to change props on children that trigger CSS rules
 * changes with different kinds of transitions */
export class Animation extends React.PureComponent<AnimationProps, AnimationState> {
  state = getDuration(this.props.timeout)

  static defaultProps = {
    timeout: 500,
    in: false,
  }

  static getDerivedStateFromProps(nextProps: AnimationProps) {
    return getDuration(nextProps.timeout)
  }
  render() {
    const { in: inProp, children, timeout, ...rest } = this.props
    return (
      <Transition in={inProp} timeout={timeout} {...rest}>
        {state => {
          if (isEntering(state)) {
            return children({
              entering: true,
              entered: false,
              duration: this.state.enter,
              exiting: false,
              exited: false,
              state,
            })
          }
          if (isEntered(state)) {
            return children({
              entered: true,
              entering: false,
              duration: this.state.enter,
              exiting: false,
              exited: false,
              state,
            })
          }
          if (isExiting(state)) {
            children({
              entered: false,
              entering: false,
              duration: this.state.exit,
              exiting: true,
              exited: false,
              state,
            })
          }
          if (isExited(state)) {
            children({
              entered: false,
              entering: false,
              duration: this.state.enter,
              exiting: false,
              exited: true,
              state,
            })
          }
          return children({
            entered: false,
            entering: false,
            duration: this.state.enter,
            exiting: false,
            exited: true,
            state,
          })
        }}
      </Transition>
    )
  }
}
