import React from 'react';
import styled, { css, keyframes } from 'styled-components';

import type { JSX } from 'react';
import type { Keyframes } from 'styled-components';

import { Link } from '@redocly/theme/components/Link/Link';

export type ButtonSize = 'small' | 'medium' | 'large' | string;
export type ButtonVariant =
  | 'primary'
  | 'secondary'
  | 'outlined'
  | 'text'
  | 'link'
  | 'ghost'
  | string;
type ButtonTone = 'default' | 'danger';

export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  children?: React.ReactNode;
  disabled?: boolean;
  blinking?: boolean;
  fullWidth?: boolean;
  variant?: ButtonVariant;
  tone?: ButtonTone;
  size?: ButtonSize;
  extraClass?: string;
  to?: string;
  external?: boolean;
  languageInsensitive?: boolean;
  icon?: JSX.Element;
  iconPosition?: 'left' | 'right';
  title?: string;
  tabIndex?: number;
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  onClick?: (e?: any) => void;
  ref?: React.Ref<HTMLButtonElement>;
}

const getBlink = (): Keyframes => keyframes`
    0% {
        color: var(--button-content-color);
        background-color: var(--button-bg-color-active);
    }

    50% {
        background-color: var(--button-bg-color);
    }

    100% {
        color: var(--button-content-color);
        background-color: var(--button-bg-color-active);
    }
`;

const StyledButtonLink = styled(Link)`
  text-decoration: none;
  &:focus-visible {
    outline: 1px solid var(--button-border-color-focused);
  }
  border-radius: var(--button-border-radius);
`;

export function generateClassName({
  variant = 'secondary',
  tone = 'default',
  size = 'medium',
  extraClass = '',
}: ButtonProps) {
  const classNames = [
    'button',
    extraClass,
    `button-tone-${tone}`,
    `button-variant-${variant}`,
    `button-size-${size}`,
  ];

  return classNames
    .filter((className) => className)
    .join(' ')
    .trim();
}

const StyledButton = styled.button.attrs((props: ButtonProps) => ({
  className: generateClassName(props),
}))<ButtonProps & { iconOnly: boolean }>`
  width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--button-gap);
  margin: var(--button-margin);
  cursor: pointer;
  text-wrap: var(--button-text-wrap);

  color: var(--button-color);
  background-color: var(--button-bg-color);
  border: var(--button-border-width) var(--button-border-style) var(--button-border-color);
  box-shadow: var(--button-box-shadow);
  backdrop-filter: var(--button-backdrop-filter, none);

  font-weight: var(--button-font-weight);
  font-size: var(--button-font-size);
  line-height: var(--button-line-height);
  padding: ${({ icon, iconPosition, iconOnly }) =>
    icon
      ? `var(--button-icon-${iconOnly ? '' : `${iconPosition || 'left'}-`}padding)`
      : 'var(--button-padding)'};
  border-radius: var(--button-border-radius);

  svg {
    width: var(--button-icon-size);
    height: var(--button-icon-size);
  }

  &:hover {
    background-color: var(--button-bg-color-hover);
    color: var(--button-color-hover);
    border-color: var(--button-border-color-hover);
  }

  &:active,
  &.active {
    background-color: var(--button-bg-color-active);
    border-color: var(--button-border-color-active);
    color: var(--button-color-active);
  }

  &:focus-visible {
    outline: 1px solid var(--button-border-color-focused);
  }

  &.button-tone-danger {
    color: var(--button-content-color-danger);
    border-color: var(--button-border-color-danger);
    background-color: var(--button-bg-color-danger);

    &:hover {
      color: var(--button-content-color-danger-hover);
      border-color: var(--button-border-color-danger-hover);
      background-color: var(--button-bg-color-danger-hover);
    }

    &:active {
      color: var(--button-content-color-danger-pressed);
      border-color: var(--button-border-color-danger-pressed);
      background-color: var(--button-bg-color-danger-pressed);
    }
  }

  &:disabled {
    pointer-events: none;
    background-color: var(--button-bg-color-disabled);
    color: var(--button-content-color-disabled);
    border-color: var(--button-border-color-disabled);
    border-width: var(--button-border-width-disabled);
  }

  ${({ variant, size }) =>
    (variant === 'link' || variant === 'ghost') &&
    size === 'small' &&
    css`
      --button-font-size: var(--font-size-sm);
      --button-line-height: var(--line-height-sm);
      --button-icon-padding: var(--button-icon-padding-small) !important;
    `}

  ${({ blinking }) =>
    blinking &&
    css`
      pointer-events: none;
      animation: ${getBlink()} 1.2s infinite;
    `}
`;

const ButtonComponent: React.FC<ButtonProps> = (props) => {
  const { languageInsensitive, ...buttonProps } = props;
  const tabIndex = 'tabIndex' in props ? props.tabIndex : props.to ? -1 : undefined;

  const button = (
    <StyledButton
      data-component-name="Button/Button"
      {...buttonProps}
      iconOnly={!props.children && props.icon !== null}
      tabIndex={tabIndex}
    >
      {props.icon && props.iconPosition !== 'right' && props.icon}
      {props.children}
      {props.icon && props.iconPosition === 'right' && props.icon}
    </StyledButton>
  );

  if (props.to) {
    return (
      <StyledButtonLink
        to={props.to}
        external={props.external}
        languageInsensitive={languageInsensitive}
        onClick={props.onClick}
      >
        {button}
      </StyledButtonLink>
    );
  } else {
    return button;
  }
};

export const Button = styled(ButtonComponent)<ButtonProps>``;
