import * as React from 'react';
import clsx from 'clsx';
import plugin from 'tailwindcss/plugin';
import { useFallbackId } from '@modal-sh/react-utils';

/**
 * Derived HTML element of the {@link ToggleSwitch} component.
 */
export type ToggleSwitchDerivedElement = HTMLInputElement;

/**
 * Props of the {@link ToggleSwitch} component.
 */
export interface ToggleSwitchProps extends Omit<React.InputHTMLAttributes<ToggleSwitchDerivedElement>, 'type' | 'size'> {
  /**
   * Should the component occupy the whole width of its parent?
   */
  block?: boolean;
  /**
   * Complementary content of the component.
   */
  subtext?: React.ReactNode;
  /**
   * Is the component in an indeterminate state?
   */
  indeterminate?: boolean;
  /**
   * Label to display when the component is checked.
   */
  checkedLabel?: React.ReactNode;
  /**
   * Label to display when the component is unchecked.
   */
  uncheckedLabel?: React.ReactNode;
}

export const toggleSwitchPlugin = plugin(({ addComponents }) => {
  addComponents({
    '.toggle-switch': {
      '& + label + label + label > :first-child': {
        appearance: 'none',
        cursor: 'pointer',
        position: 'relative',
        overflow: 'hidden',
        height: '1.5em',
        width: '3em',
        display: 'block',
        'box-sizing': 'border-box',
        'border-radius': '9999px',
        color: 'rgb(var(--color-primary))',
      },

      '&:checked + label + label + label > :first-child': {
        color: 'rgb(var(--color-tertiary))',
      },

      '&:focus + label + label + label > :first-child': {
        color: 'rgb(var(--color-secondary))',
      },

      '& + label:active + label + label > :first-child': {
        color: 'rgb(var(--color-tertiary))',
      },

      '& + label + label:active + label > :first-child': {
        color: 'rgb(var(--color-tertiary))',
      },

      '& + label + label + label:active > :first-child': {
        color: 'rgb(var(--color-tertiary))',
      },

      '& + label + label + label > :first-child > :first-child': {
        width: '100%',
        height: '100%',
        'background-color': 'rgb(var(--color-primary) / 50%)',
        'border-radius': '9999px',
        display: 'block',
        'box-sizing': 'border-box',
        'background-clip': 'content-box',
        padding: '0.25em',
        appearance: 'none',
      },

      '&:checked + label + label + label > :first-child > :first-child': {
        'background-color': 'rgb(var(--color-tertiary) / 50%)',
      },

      '&:focus + label + label + label > :first-child > :first-child': {
        'background-color': 'rgb(var(--color-secondary) / 50%)',
      },

      '& + label:active + label + label > :first-child > :first-child': {
        'background-color': 'rgb(var(--color-tertiary) / 50%)',
      },

      '& + label + label:active + label > :first-child > :first-child': {
        'background-color': 'rgb(var(--color-tertiary) / 50%)',
      },

      '& + label + label + label:active > :first-child > :first-child': {
        'background-color': 'rgb(var(--color-tertiary) / 50%)',
      },

      '& + label + label + label > :first-child > :first-child > :first-child': {
        appearance: 'none',
        'border-radius': '9999px',
        display: 'block',
        width: '100%',
        height: '100%',
        margin: '-0.25em',
        'box-sizing': 'border-box',
        'background-clip': 'content-box',
      },

      '& + label + label + label > :first-child > :first-child > :first-child > :first-child': {
        width: '1.5em',
        height: '1.5em',
        margin: '-0.25em 0 0 0',
        display: 'block',
        'border-radius': '9999px',
        'background-color': 'currentColor',
        appearance: 'none',
        'aspect-ratio': '1 / 1',
        'z-index': '1',
        position: 'relative',
        'box-shadow': '-100000.5em 0 0 100000em rgb(var(--color-primary) / 50%)',
      },

      '&:checked + label + label + label > :first-child > :first-child > :first-child > :first-child': {
        'margin-left': 'calc(100% - 1em)',
        'box-shadow': '-100000.5em 0 0 100000em rgb(var(--color-tertiary) / 50%)',
      },

      '&:indeterminate + label + label + label > :first-child > :first-child > :first-child > :first-child': {
        'margin-left': 'calc(50% - 0.5em)',
      },

      '&:focus + label + label + label > :first-child > :first-child > :first-child > :first-child': {
        'box-shadow': '-100000.5em 0 0 100000em rgb(var(--color-secondary) / 50%)',
      },

      '& + label:active + label + label > :first-child > :first-child > :first-child > :first-child': {
        'box-shadow': '-100000.5em 0 0 100000em rgb(var(--color-tertiary) / 50%)',
      },

      '& + label + label:active + label > :first-child > :first-child > :first-child > :first-child': {
        'box-shadow': '-100000.5em 0 0 100000em rgb(var(--color-tertiary) / 50%)',
      },

      '& + label + label + label:active > :first-child > :first-child > :first-child > :first-child': {
        'box-shadow': '-100000.5em 0 0 100000em rgb(var(--color-tertiary) / 50%)',
      },
    },
  });
});

/**
 * Component for toggling a Boolean value in an appearance of a toggle switch.
 */
export const ToggleSwitch = React.forwardRef<ToggleSwitchDerivedElement, ToggleSwitchProps>((
  {
    uncheckedLabel,
    checkedLabel,
    block = false,
    id: idProp,
    className,
    subtext,
    style,
    indeterminate = false,
    ...etcProps
  },
  forwardedRef,
) => {
  const defaultRef = React.useRef<ToggleSwitchDerivedElement>(null);
  const ref = forwardedRef ?? defaultRef;
  const id = useFallbackId(idProp);

  React.useEffect(() => {
    if (typeof ref === 'function') {
      const defaultElement = defaultRef.current as ToggleSwitchDerivedElement;
      defaultElement.indeterminate = indeterminate;
      ref(defaultElement);
      return;
    }
    const element = ref.current as ToggleSwitchDerivedElement;
    element.indeterminate = indeterminate;
  }, [indeterminate, defaultRef, ref]);

  return (
    <div
      className={clsx(
        'gap-x-4 flex-wrap',
        block && 'flex',
        !block && 'inline-flex align-center',
        className,
      )}
      style={style}
      data-testid="base"
    >
      <input
        {...etcProps}
        ref={typeof ref === 'function' ? defaultRef : ref}
        type="checkbox"
        id={id}
        className="sr-only peer/radio toggle-switch"
      />
      <label
        htmlFor={id}
        className="peer/children order-3 cursor-pointer peer-disabled/radio:cursor-not-allowed"
      >
        <span
          data-testid="children"
        >
          {checkedLabel}
        </span>
      </label>
      <label
        htmlFor={id}
        className="peer/children order-1 cursor-pointer peer-disabled/radio:cursor-not-allowed"
      >
        {uncheckedLabel && (
          <>
            <span className="sr-only">
              {' / '}
            </span>
            <span
              data-testid="uncheckedLabel"
            >
              {uncheckedLabel}
            </span>
          </>
        )}
      </label>
      <label
        htmlFor={id}
        className={clsx(
          'order-2 block rounded-full ring-secondary/50 overflow-hidden gap-4 leading-none select-none cursor-pointer',
          'peer-focus/radio:outline-0 peer-focus/radio:ring-4 peer-focus/radio:ring-secondary/50',
          'active:ring-tertiary/50 active:ring-4',
          'peer-active/children:ring-tertiary/50 peer-active/children:ring-4 peer-active/children:text-tertiary',
          'peer-disabled/radio:opacity-50 peer-disabled/radio:cursor-not-allowed peer-disabled/radio:ring-0',
          'text-primary peer-disabled/radio:text-primary peer-focus/radio:text-secondary peer-checked/radio:text-tertiary active:text-tertiary',
          !uncheckedLabel && '-ml-4',
        )}
      >
        <span>
          <span>
            <span>
              <span />
            </span>
          </span>
        </span>
      </label>
      {subtext && (
        <div
          className={clsx(
            'block w-full text-xs order-4',
            !uncheckedLabel && 'pl-16',
            uncheckedLabel && 'pt-2',
          )}
          data-testid="subtext"
        >
          {subtext}
        </div>
      )}
    </div>
  );
});

ToggleSwitch.displayName = 'ToggleSwitch';

ToggleSwitch.defaultProps = {
  block: false,
  subtext: undefined,
  indeterminate: false,
  checkedLabel: undefined,
  uncheckedLabel: undefined,
};
