import { useEffect, useRef, useState } from 'react'

import { omitThemingProps, styled, useStyleConfig, vui } from '../core'
import { ChangeEvent, cs, filterUndefined, FocusEvent } from '../utils'
import { switchColors } from './consts'
import { useSwitchContext } from './context'
import { SwitchButtonProps } from './switch.types'

const SwitchInput = styled.inputBox`
  cursor: inherit;
  height: calc(100% + 10px);
  left: -5px;
  margin: 0;
  opacity: 0;
  padding: 0;
  position: absolute;
  top: -5px;
  width: calc(100% + 10px);
  z-index: 1;
`

const SwitchFoundation = styled.spanBox`
  border-radius: 100px;
  display: inline-flex;
  flex-shrink: 0;
  transition-duration: fast;
  position: relative;
`

/** Circular element that moves along the track when toggled. */
const SwitchThumb = styled(SwitchFoundation)`
  position: absolute;
`

/** Track element along which the thumb moves when toggled. */
const SwitchTrack = styled(SwitchFoundation)`
  align-items: center;
  flex: 1;
`

export const SwitchButtonBase = styled(SwitchFoundation)`
  color: white;
  cursor: pointer;
  --x-ring-color: green;

  &[aria-disabled='true'] {
    cursor: not-allowed;

    .vui-switchThumb {
      background-color: ${switchColors.disabled};
    }

    .vui-switchTrack {
      --x-ring-color: ${switchColors.disabled};
      background-color: ${switchColors.disabledBg};
      color: ${switchColors.disabled};
    }
  }
`

/**
 * Controls the logic and visuals of multiple inner parts, such as input, track or thumb elements.
 * Handles controlled and uncontrolled modes.
 */
export const SwitchButton = vui<'span', SwitchButtonProps>((props, ref) => {
  const mergedProps = { ...useSwitchContext(), ...props }
  const {
    checked,
    className,
    defaultChecked,
    disabled,
    id,
    innerLabelOff,
    innerLabelOn,
    inputProps,
    inputRef,
    name,
    onBlur,
    onChange,
    onFocus,
    required,
    value,
    ...rest
  } = omitThemingProps(mergedProps)
  const { current: isControlled } = useRef(checked !== undefined)
  const [isChecked, setIsChecked] = useState(checked ?? defaultChecked)
  const [isFocused, setIsFocused] = useState(false)
  const styles = useStyleConfig('Switch', useSwitchContext())
  const { thumb: thumbStyles, track: trackStyles, ...buttonStyles } = styles.button

  useEffect(() => {
    isControlled && setIsChecked(checked)
  }, [checked, isControlled])

  function handleOnBlur(e: FocusEvent) {
    setIsFocused(false)
    onBlur?.(e)
  }

  function handleOnChange(e: ChangeEvent) {
    !isControlled && setIsChecked(e.target.checked)
    onChange?.(e)
  }

  function handleOnFocus(e: FocusEvent) {
    setIsFocused(true)
    onFocus?.(e)
  }

  const hDiff = (thumbStyles.h - trackStyles.h) / 2
  const thumbOffset = `calc(100% - ${Math.abs(hDiff - thumbStyles.h)}px)`
  const labelOffset = `${thumbStyles.h - hDiff + 4}px`

  const aliasedProps = filterUndefined({
    'aria-disabled': disabled,
    'data-focused': isFocused ? true : false,
  })

  const thumbAliasedProps = filterUndefined({
    left: isChecked ? thumbOffset : -hDiff,
    ring: isFocused ? thumbStyles.ring : 0,
  })

  const trackAliasedProps = filterUndefined({
    bg: isChecked ? trackStyles.bg : innerLabelOff ? switchColors.offBgContrast : switchColors.offBg,
    pl: isChecked ? 1 : labelOffset,
    pr: isChecked ? labelOffset : 1,
    ring: !disabled ? (isFocused ? trackStyles.ring : 0) : 3,
  })

  return (
    <SwitchButtonBase
      className={cs('vui-switchButton', className)}
      ref={ref}
      {...buttonStyles}
      {...aliasedProps}
      {...rest}
    >
      <SwitchInput
        className="vui-switchInput"
        onBlur={handleOnBlur}
        onChange={handleOnChange}
        onFocus={handleOnFocus}
        ref={inputRef}
        tabIndex={-1}
        type="checkbox"
        {...{ checked, defaultChecked, disabled, id, name, required, value }}
        {...inputProps}
      />

      <SwitchTrack className="vui-switchTrack" tabIndex={0} {...trackStyles} {...trackAliasedProps}>
        {!isChecked && innerLabelOff}

        <SwitchThumb className="vui-switchThumb" {...thumbStyles} {...thumbAliasedProps} />

        {isChecked && innerLabelOn}
      </SwitchTrack>
    </SwitchButtonBase>
  )
})

SwitchButton.displayName = 'SwitchButton'

export default SwitchButton
