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

import type { JSX } from 'react';

import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon';
import { CheckmarkFilledIcon } from '@redocly/theme/icons/CheckmarkFilledIcon/CheckmarkFilledIcon';

type DefaultStatusColor = 'success' | 'processing' | 'error' | 'warning' | 'default';
type ActionStatusColor = 'approved' | 'declined' | 'pending';
type SubjectStatusColor = 'active' | 'draft' | 'deprecated' | 'product';
type HttpColor = 'get' | 'post' | 'put' | 'delete' | 'option' | 'patch' | 'head' | 'hook' | 'link';
type ActionColor = 'receive' | 'send';
type ChannelColor = 'channel';
type HttpStatusColor = 'http-deprecated' | 'http-additional-operation';
type StatusColor =
  | DefaultStatusColor
  | ActionStatusColor
  | SubjectStatusColor
  | HttpColor
  | ActionColor
  | ChannelColor
  | HttpStatusColor;
type Color =
  | 'red'
  | 'green'
  | 'blue'
  | 'grey'
  | 'turquoise'
  | 'magenta'
  | 'purple'
  | 'carrot'
  | 'raspberry'
  | 'orange'
  | 'grass'
  | 'persian-green'
  | 'sky'
  | 'blueberry';

export type TagProps = {
  className?: string;
  children?: React.ReactNode;
  closable?: boolean;
  color?: StatusColor | Color | string;
  borderless?: boolean;
  withStatusDot?: boolean;
  statusDotColor?: string;
  size?: string;
  icon?: React.ReactNode;
  active?: boolean;
  tabIndex?: number;
  style?: React.CSSProperties;
  onClick?: (event: React.MouseEvent) => void;
  onKeyDown?: (event: React.KeyboardEvent) => void;
  onClose?: (event: React.MouseEvent) => void;
  maxLength?: number;
  textTransform?: 'uppercase' | 'lowercase' | 'capitalize' | 'none';
  variant?: 'outline' | 'filled';
  selectable?: boolean;
};

export function Tag({
  children,
  color,
  icon,
  active,
  closable,
  tabIndex,
  onClick,
  onKeyDown,
  onClose,
  size,
  borderless,
  withStatusDot,
  statusDotColor = 'var(--tag-status-dot-color-default)',
  maxLength,
  textTransform,
  variant = 'filled',
  selectable,
  ...otherProps
}: TagProps): JSX.Element {
  const truncateText = (text: string, maxLen: number): string => {
    if (text.length <= maxLen) return text;
    return text.slice(0, maxLen) + '...';
  };

  const extractTextFromElement = (element: React.ReactNode): string => {
    if (typeof element === 'string') {
      return element;
    }
    if (typeof element === 'number') {
      return element.toString();
    }
    if (React.isValidElement(element)) {
      const props = element.props as { children?: React.ReactNode };
      if (typeof props.children === 'string') {
        return props.children;
      }
    }
    return '';
  };

  const truncateJSXElement = (element: React.ReactNode, maxLen: number): React.ReactNode => {
    if (typeof element === 'string') {
      return truncateText(element, maxLen);
    }
    if (typeof element === 'number') {
      const numStr = element.toString();
      return numStr.length > maxLen ? truncateText(numStr, maxLen) : element;
    }
    if (React.isValidElement(element)) {
      const textContent = extractTextFromElement(element);
      if (textContent.length <= maxLen) {
        return element;
      }

      const props = element.props as { children?: React.ReactNode };

      if (typeof props.children === 'string') {
        return React.cloneElement(element, {
          ...props,
          children: truncateText(props.children, maxLen),
          /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
        } as any);
      }
    }
    return element;
  };

  const renderChildren = (): React.ReactNode => {
    if (!maxLength) {
      return children;
    }

    if (typeof children === 'string') {
      return truncateText(children, maxLength);
    }

    if (React.isValidElement(children)) {
      return truncateJSXElement(children, maxLength);
    }

    return children;
  };

  return (
    <TagWrapper
      tabIndex={tabIndex}
      data-component-name="Tag/Tag"
      borderless={borderless}
      color={color}
      size={size}
      onClick={onClick}
      onKeyDown={onKeyDown}
      hasCloseButton={closable}
      textTransform={textTransform}
      variant={variant}
      selectable={selectable}
      {...otherProps}
    >
      {withStatusDot ? <StatusDot color={statusDotColor} /> : icon ? icon : null}
      <ContentWrapper>{renderChildren()}</ContentWrapper>
      {closable && (
        <CloseButton
          onClick={(event) => {
            onClose?.(event);
          }}
        >
          <CloseIcon />
        </CloseButton>
      )}
      {active && <ActiveIcon />}
    </TagWrapper>
  );
}

export const ContentWrapper = styled.div`
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-wrap: nowrap;

  padding: var(--tag-content-padding);
  gap: var(--tag-content-gap);
`;

const CloseButton = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  align-self: stretch;
  border-radius: 0 var(--tag-border-radius) var(--tag-border-radius) 0;
  margin: calc(-1 * var(--tag-border-width));
  padding: var(--tag-border-width);

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

  &:focus-visible {
    background: var(--tag-close-button-bg-color-focus);
  }
`;

const TagWrapper = styled.div.attrs<
  TagProps & {
    hasCloseButton?: boolean;
    textTransform?: 'uppercase' | 'lowercase' | 'capitalize' | 'none';
  }
>(({ className, color, size, variant }) => ({
  className: [
    className,
    'tag-default',
    color && `tag-${color}`,
    size && `tag-size-${size}`,
    `tag-variant-${variant || 'filled'}`,
  ]
    .filter(Boolean)
    .join(' '),
}))`
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-wrap: nowrap;
  position: relative;

  padding: var(--tag-padding);
  ${({ hasCloseButton }) => (hasCloseButton ? 'padding-right: 0;' : '')};
  margin: var(--tag-margin);

  &:last-child {
    margin-right: 0;
  }

  gap: var(--tag-gap);

  font-size: var(--tag-font-size);
  font-family: var(--tag-font-family);
  font-weight: var(--tag-font-weight);
  line-height: var(--tag-line-height);
  box-shadow: var(--tag-box-shadow);
  ${({ textTransform }) =>
    `text-transform: ${textTransform ? `${textTransform}` : 'var(--tag-text-transform)'};`}

  color: var(--tag-color);
  background-color: var(--tag-bg-color);
  ${({ borderless }) =>
    borderless
      ? ''
      : `border: var(--tag-border-width) var(--tag-border-style) var(--tag-border-color);`}
  border-radius: var(--tag-border-radius);

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

  ${({ selectable }) =>
    selectable &&
    css`
      &:hover {
        background-color: var(--tag-bg-color-hover);
        border-color: var(--tag-border-color-hover);
      }

      &:focus-visible {
        outline: 1px solid var(--tag-border-color-focused);
        outline-offset: 2px;
        border-radius: var(--tag-border-radius-focused);
      }
    `};
`;

const StatusDot = styled.div<{ color: string }>`
  display: inline-block;
  width: var(--tag-badge-size);
  height: var(--tag-badge-size);
  border: var(--tag-badge-border-width) solid var(--tag-badge-border-color);
  border-radius: 50%;
  background-color: ${({ color }) => color};
`;

const ActiveIcon = styled(CheckmarkFilledIcon)`
  width: 12px;
  height: 12px;
  position: absolute;
  right: -4px;
  top: -4px;
`;
