import React from 'react';
import { twMerge } from 'tailwind-merge';

import Button, {
  Props as ButtonProps,
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import {
  InfinityIcon,
  CommentIcon,
  MoreHorizontalIcon,
  NextTrackIcon,
  PauseIcon,
  PlayIcon,
  PreviousTrackIcon,
  QueueIcon,
  RepeatIcon,
  ShareArrowIcon,
  ShuffleIcon,
  ThumbsDownIcon,
  ThumbsUpIcon,
  Volume0Icon,
  VolumeDownIcon,
  VolumeOnIcon,
} from '@/icons';

export enum OmniplayerButtonType {
  PlayPause = 'playPause',
  Previous = 'previous',
  Next = 'next',
  Volume = 'volume',
  Shuffle = 'shuffle',
  Repeat = 'repeat',
  Autoplay = 'autoplay',
  Queue = 'queue',
  MoreActions = 'moreActions',
  Like = 'like',
  Dislike = 'dislike',
  Comments = 'comments',
  Share = 'share',
}

export const DEFAULT_BUTTONS = [
  OmniplayerButtonType.Repeat,
  OmniplayerButtonType.Previous,
  OmniplayerButtonType.PlayPause,
  OmniplayerButtonType.Next,
  OmniplayerButtonType.Autoplay,
];

/**
 * Omniplayer control buttons can take the following types of props
 *
 * - `MouseEventHandler`: Button will invoke the handler on click
 * - `undefined`: Button will not be hidden, but will be disabled
 * - `string`: Button will function as a link
 * - `false`: Button will be hidden
 */
type ControlButtonHandler =
  | React.MouseEventHandler
  | undefined
  | string
  | false;

type ButtonPropMap = Record<
  OmniplayerButtonType,
  | (OmniplayerButtonProps & { label?: string })
  | (() => OmniplayerButtonProps & { label?: string })
>;

export type OmniplayerControlsProps = {
  buttons?: OmniplayerButtonType[];
  buttonPropMapOverrides?: Partial<ButtonPropMap>;
  buttonClassName?: string;
  buttonIconClassName?: string;
  buttonVariant?: ButtonVariant;
  buttonSize?: ButtonSize;
  buttonShape?: ButtonShape;
  isPlaying?: boolean;
  volume?: number;
  muted?: boolean;
  shuffle?: boolean;
  repeat?: boolean;
  autoplay?: boolean;
  isLiked?: boolean;
  isDisliked?: boolean;
  disabledPlayPause?: boolean;
  disabledPrevious?: boolean;
  disabledNext?: boolean;
  disabledRepeat?: boolean;
  disabledAutoplay?: boolean;
  onPlayPauseClick?: ControlButtonHandler;
  onPreviousClick?: ControlButtonHandler;
  onNextClick?: ControlButtonHandler;
  onVolumeClick?: ControlButtonHandler;
  onShuffleClick?: ControlButtonHandler;
  onRepeatClick?: ControlButtonHandler;
  onAutoplayClick?: ControlButtonHandler;
  onQueueClick?: ControlButtonHandler;
  onMoreActionsClick?: ControlButtonHandler;
  onLikeClick?: ControlButtonHandler;
  onDislikeClick?: ControlButtonHandler;
  onCommentsClick?: ControlButtonHandler;
  onShareClick?: ControlButtonHandler;
};

type OmniplayerButtonProps = Omit<ButtonProps, 'onClick'> & {
  onClick?: ControlButtonHandler;
};

const OmniplayerButton: React.FC<OmniplayerButtonProps> = (props) => (
  <Button
    {...({
      ...props,
      disabled: props.disabled ?? props.onClick === undefined,
      href: typeof props.onClick === 'string' ? props.onClick : undefined,
      onClick: typeof props.onClick === 'function' ? props.onClick : undefined,
    } as ButtonProps)}
  />
);

export type Props = Omit<
  React.HTMLAttributes<HTMLDivElement>,
  keyof OmniplayerControlsProps
> &
  OmniplayerControlsProps;

const OmniplayerControls: React.FC<Props> = (props) => {
  const {
    className,
    children,
    buttons = DEFAULT_BUTTONS,
    buttonClassName = 'bg-transparent before:border-0',
    buttonIconClassName = 'w-6 h-6 m-0',
    buttonVariant = ButtonVariant.Fog,
    buttonSize = ButtonSize.Small,
    buttonShape = ButtonShape.Pill,
    isPlaying,
    volume = 1,
    muted = volume === 0,
    shuffle = false,
    repeat = false,
    autoplay = true,
    isLiked,
    isDisliked,
    disabledPlayPause,
    disabledPrevious,
    disabledNext,
    disabledRepeat,
    disabledAutoplay,
    onPlayPauseClick,
    onPreviousClick,
    onNextClick,
    onVolumeClick,
    onShuffleClick,
    onRepeatClick,
    onAutoplayClick,
    onQueueClick,
    onMoreActionsClick,
    onLikeClick,
    onDislikeClick,
    onCommentsClick,
    onShareClick,
    ...restProps
  } = props;

  const propMap: ButtonPropMap = {
    [OmniplayerButtonType.PlayPause]: () =>
      isPlaying
        ? { icon: PauseIcon, onClick: onPlayPauseClick, label: 'Pause' }
        : { icon: PlayIcon, onClick: onPlayPauseClick, label: 'Play' },
    [OmniplayerButtonType.Previous]: {
      icon: PreviousTrackIcon,
      onClick: onPreviousClick,
      label: 'Previous',
    },
    [OmniplayerButtonType.Next]: {
      icon: NextTrackIcon,
      onClick: onNextClick,
      label: 'Next',
    },
    [OmniplayerButtonType.Volume]: () => ({
      icon:
        volume === 0 || muted
          ? Volume0Icon
          : volume < 0.5
            ? VolumeDownIcon
            : VolumeOnIcon,
      onClick: onVolumeClick,
      label: 'Volume',
    }),
    [OmniplayerButtonType.Shuffle]: {
      active: shuffle,
      icon: ShuffleIcon,
      onClick: onShuffleClick,
      label: 'Shuffle',
    },
    [OmniplayerButtonType.Repeat]: {
      active: repeat,
      icon: RepeatIcon,
      onClick: onRepeatClick,
      label: 'Repeat',
    },
    [OmniplayerButtonType.Autoplay]: {
      active: autoplay,
      icon: InfinityIcon,
      onClick: onAutoplayClick,
      label: 'Autoplay',
    },
    [OmniplayerButtonType.Queue]: {
      icon: QueueIcon,
      onClick: onQueueClick,
      label: 'Queue',
    },
    [OmniplayerButtonType.MoreActions]: {
      icon: MoreHorizontalIcon,
      onClick: onMoreActionsClick,
      label: 'MoreActions',
    },
    [OmniplayerButtonType.Like]: {
      active: isLiked,
      icon: ThumbsUpIcon,
      onClick: onLikeClick,
      label: 'Like',
    },
    [OmniplayerButtonType.Dislike]: {
      active: isDisliked,
      icon: ThumbsDownIcon,
      onClick: onDislikeClick,
      label: 'Dislike',
    },
    [OmniplayerButtonType.Comments]: {
      icon: CommentIcon,
      onClick: onCommentsClick,
      label: 'Comments',
    },
    [OmniplayerButtonType.Share]: {
      icon: ShareArrowIcon,
      onClick: onShareClick,
      label: 'Share',
    },
  };

  return (
    <div
      className={twMerge(
        'relative flex flex-row gap-2',
        'bg-background-primary text-foreground-primary',
        className
      )}
      {...restProps}
    >
      {buttons.map((buttonType, i) => {
        const buttonProps =
          typeof propMap[buttonType] === 'function'
            ? propMap[buttonType]()
            : propMap[buttonType];
        return (
          <OmniplayerButton
            key={`${buttonType}.${i}`}
            className={buttonClassName}
            iconClassName={buttonIconClassName}
            variant={buttonVariant}
            size={buttonSize}
            shape={buttonShape}
            aria-label={buttonProps.label}
            {...buttonProps}
          />
        );
      })}
    </div>
  );
};

export default OmniplayerControls;
