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

import type { SelectOption } from '@redocly/theme/core/types';

import { Tag } from '@redocly/theme/components/Tag/Tag';
import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon';
import { Button } from '@redocly/theme/components/Button/Button';

type SelectInputProps<T> = {
  id?: string;
  listboxId?: string;
  selectedOptions: SelectOption<T>[];
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  searchValue: any;
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  stickyValue: any;
  onlyIcon?: boolean;
  icon?: React.ReactNode;
  customIcon?: React.ReactNode;
  placeholder?: string;
  multiple?: boolean;
  searchable?: boolean;
  clearable?: boolean;
  inputRef?: React.ForwardedRef<HTMLInputElement>;
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  clearHandler?: (value?: any) => void;
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  inputBlurHandler?: (e?: any) => void;
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  inputFocusHandler?: (e?: any) => void;
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  searchHandler?: (e?: any) => void;
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  clickHandler?: (e?: any) => void;
};

export function SelectInput<T>(props: SelectInputProps<T>): React.ReactNode {
  const {
    id,
    listboxId,
    onlyIcon,
    icon,
    customIcon,
    selectedOptions,
    placeholder,
    stickyValue,
    multiple,
    searchable,
    clearable,
    clearHandler,
    searchHandler,
    clickHandler,
    searchValue,
    inputBlurHandler,
    inputFocusHandler,
  } = props;
  const inputRef = useRef<HTMLInputElement | null>(null);

  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  const onChangeHandler = (e: any) => {
    searchHandler?.(e);
    inputRef.current?.focus();
  };

  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  const onKeyDownHandler = (e: any) => {
    if (e.key === 'Backspace' && !searchValue && selectedOptions.length) {
      clearHandler?.(selectedOptions[selectedOptions.length - 1]);
      inputRef.current?.focus();
    }
  };

  const onClickHandler = (e: React.MouseEvent) => {
    clickHandler?.(e);
  };

  const onFocusHandler = (e: React.FocusEvent<HTMLDivElement>) => {
    inputFocusHandler?.(e);
    inputRef.current?.focus();
  };

  const onBlurHandler = (e: React.FocusEvent<HTMLDivElement>) => {
    inputBlurHandler?.(e);
  };

  const onClearAllHandler = (e: React.MouseEvent) => {
    e.stopPropagation();
    clearHandler?.();
  };

  const selectTags = selectedOptions.map((option, index) => (
    <SelectInputTag
      closable
      tabIndex={0}
      key={index}
      onClose={() => {
        clearHandler?.(option);
        inputRef.current?.focus();
      }}
    >
      {option.label || (option.value as string) || option.element}
    </SelectInputTag>
  ));

  const selectInput = (
    <SelectInternalInput
      value={
        searchValue ||
        (!multiple && !stickyValue && selectedOptions.length
          ? selectedOptions[0].label || selectedOptions[0].value
          : '')
      }
      placeholder={
        searchValue || (multiple && selectedOptions.length) ? '' : stickyValue || placeholder
      }
      onChange={onChangeHandler}
      onKeyDown={onKeyDownHandler}
      onBlur={onBlurHandler}
      ref={(input) => {
        if (!input) return;

        inputRef.current = input;

        if (!props.inputRef) return;

        if (typeof props.inputRef === 'function') {
          props.inputRef(input);
        } else {
          props.inputRef.current = input;
        }
      }}
      width={multiple ? (!searchValue && selectedOptions.length ? '10px' : 'auto') : '100%'}
      tabIndex={0}
      onFocus={(e) => {
        e.stopPropagation();
      }}
      onMouseDown={(e) => {
        e.preventDefault();
      }}
    />
  );

  const simpleValue = selectedOptions.length ? (
    selectedOptions[0].label || selectedOptions[0].element || (selectedOptions[0].value as string)
  ) : (
    <SelectInternalInputPlaceholder>{placeholder}</SelectInternalInputPlaceholder>
  );

  const multipleValues = selectedOptions.length ? (
    selectTags
  ) : (
    <SelectInternalInputPlaceholder>{placeholder}</SelectInternalInputPlaceholder>
  );

  return (
    <SelectInputWrapper
      {...props}
      id={id}
      onFocus={onFocusHandler}
      onClick={onClickHandler}
      role="button"
      aria-haspopup="listbox"
      aria-controls={listboxId}
      aria-owns={listboxId}
      aria-label="Select option"
    >
      {!onlyIcon && (
        <>
          <SelectInputValue>
            {multiple ? (
              searchable ? (
                <>
                  {selectTags}
                  {selectInput}
                </>
              ) : (
                multipleValues
              )
            ) : searchable ? (
              selectInput
            ) : (
              simpleValue
            )}
          </SelectInputValue>
          {!!(clearable && selectedOptions.length) && (
            <Button size="small" variant="text" icon={<CloseIcon />} onClick={onClearAllHandler} />
          )}
        </>
      )}
      {customIcon || icon}
    </SelectInputWrapper>
  );
}

export const SelectInputWrapper = styled.div`
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-radius: var(--select-input-border-radius);
  padding: var(--select-input-padding);
  cursor: pointer;
  gap: var(--select-input-gap);
`;

const SelectInputValue = styled.div`
  width: calc(100% - 20px);
  display: flex;
  min-width: 0;
  text-overflow: ellipsis;
  overflow: hidden;
  flex-wrap: wrap;
  gap: var(--select-input-value-gap);
`;

const SelectInputTag = styled(Tag)`
  --tag-content-padding: 0;
`;

const SelectInternalInput = styled.input.attrs<{ width?: string }>({
  type: 'text',
})`
  outline: none;
  border-radius: var(--select-input-border-radius);
  border: none;
  font-size: var(--select-input-font-size);
  font-weight: var(--select-input-font-weight);
  line-height: var(--select-input-line-height);
  background-color: var(--select-input-bg-color);

  &::placeholder {
    color: var(--select-input-placeholder-color);
  }

  width: ${({ width }) => width || 'auto'};
`;

const SelectInternalInputPlaceholder = styled.div`
  color: var(--select-input-placeholder-color);
  padding-left: 8px;
`;
