import React, {
  useState,
  ChangeEvent,
  useRef,
  useEffect,
  useCallback,
} from 'react';
import Icon from '../Icon';
import Label, { LabelProps } from '../Label/Label';
import classNames from 'classnames';
import './Autocomplete.scss';
import { createPortal } from 'react-dom';

type SelectedOptionType = string | null;

export interface Option {
  id: number;
  label: string;
}

interface FilteredOption {
  original: Option;
  index: number;
}

interface AutocompleteProps {
  /**
   * Label props for Autocomplete
   */
  labelProps?: LabelProps;
  /**
   * Set true or false to display the Label
   */
  showLabel?: boolean;
  /**
   * set boolean value to ensure if error
   */
  error?: boolean;
  /**
   * set boolean value to display/disabled
   */
  disabled?: boolean;
  /**
   * set boolean value to display the border
   */
  showBorder?: boolean;
  /**
   * Customize the border based on boolean
   */
  showBorderOnHover?: boolean;
  /**
   * for customize the Autocomplete use className props(if required)
   */
  className?: string;
  /**
   * for error message passed argument as string
   */
  errorMsg?: string;
  /**
   * Required options for autocomplete in the appropriate manner
   */
  options: Option[];
  /**
   * placeholder for the Autocomplete
   */
  placeholder: string;
  /**
   * for controlled component pass the onChange props
   */
  onChange?: (option: Option) => void;
  /**
   * for clear the selected option from the input onClear is must
   */
  onClear?: () => void;
  /**
   * By Default select one option selectedData is must
   */
  selectedData?: Option;
}

interface DropdownListProps {
  positionX: number;
  positionY: number;
  dropdownWidth: number;
  filteredOptions: FilteredOption[];
  focusedOptionIndex: number;
  selectedOptionIndex: number;
  updateOption: (option: Option, index: number) => void;
  setSearchTerm: React.Dispatch<React.SetStateAction<string>>;
  setShowDropdown: React.Dispatch<React.SetStateAction<boolean>>;
  showDropdown: boolean;
  onBlurHandler: () => void;
  dropdownDirection: 'down' | 'up';
}

const DropdownList: React.FC<DropdownListProps> = ({
  positionX,
  positionY,
  dropdownWidth,
  filteredOptions,
  focusedOptionIndex,
  selectedOptionIndex,
  updateOption,
  setSearchTerm,
  setShowDropdown,
  showDropdown,
  onBlurHandler,
  dropdownDirection,
}) => {
  const handleClearSearch = () => {
    setSearchTerm('');
    setShowDropdown(false);
  };

  return (
    <>
      {showDropdown && (
        <div
          className="storybook-autocomplete-overlay"
          onClick={onBlurHandler}
        ></div>
      )}
      <div
        tabIndex={0}
        style={{
          left: positionX,
          top: dropdownDirection === 'down' ? positionY : undefined,
          bottom:
            dropdownDirection === 'up'
              ? window.innerHeight - positionY
              : undefined,
          width: dropdownWidth,
          marginTop: dropdownDirection === 'down' ? '5px' : undefined,
          marginBottom: dropdownDirection === 'up' ? '5px' : undefined,
        }}
        className="storybook-autocomplete-options-container"
      >
        <div className="storybook-autocomplete-options-wrapper">
          {filteredOptions.length > 0 ? (
            <ul
              className="storybook-autocomplete-option-list"
              id="dropdown-options"
            >
              {filteredOptions.map((option, index) => (
                <li
                  key={index}
                  id={`option-${index}`}
                  className={classNames(
                    'storybook-autocomplete-option-box',
                    { focused: focusedOptionIndex === index },
                    { selected: selectedOptionIndex === option.index }
                  )}
                  onClick={() => updateOption(option.original, option.index)}
                >
                  {option.original.label}
                </li>
              ))}
            </ul>
          ) : (
            <ul
              className="storybook-autocomplete-empty-options"
              onClick={handleClearSearch}
            >
              no options
            </ul>
          )}
        </div>
      </div>
    </>
  );
};

const Autocomplete: React.FC<AutocompleteProps> = ({
  options = [],
  placeholder = '',
  labelProps,
  disabled,
  className,
  errorMsg,
  error,
  onChange,
  onClear,
  showLabel = true,
  showBorder = true,
  showBorderOnHover = true,
  selectedData,
  ...props
}: AutocompleteProps) => {
  const [showDropdown, setShowDropdown] = useState<boolean>(false);
  const [selectedOption, setSelectedOption] =
    useState<SelectedOptionType>(null);
  const [selectedOptionIndex, setSelectedOptionIndex] = useState<number>(-1);
  const [searchTerm, setSearchTerm] = useState<string>('');
  const [focusedOptionIndex, setFocusedOptionIndex] = useState<number>(-1);
  const [dropdownDirection, setDropdownDirection] = useState<'down' | 'up'>(
    'down'
  );
  const inputRef = useRef<HTMLInputElement>(null);
  const parentCheckingRef = useRef<HTMLDivElement>(null);

  const [dropdownPosition, setDropdownPosition] = useState({
    posX: 0,
    posY: 0,
    width: 0,
  });

  const OFFSET = 45;

  const getDropdownPosition = () => {
    if (parentCheckingRef?.current) {
      const rect = parentCheckingRef.current.getBoundingClientRect();
      const viewportHeight = window.innerHeight;

      const spaceBelow = viewportHeight - rect.bottom;
      const minimumSpaceRequired = 200;

      const newDropdownDirection =
        spaceBelow >= minimumSpaceRequired ? 'down' : 'up';
      setDropdownDirection(newDropdownDirection);

      setDropdownPosition({
        posX: rect.left + window.scrollX,
        posY:
          newDropdownDirection === 'down'
            ? rect.bottom + window.scrollY
            : rect.top + window.scrollY - OFFSET,
        width: parentCheckingRef.current.offsetWidth,
      });
    }
  };

  const handleInput = () => {
    setShowDropdown(true);
    getDropdownPosition();
    if (selectedOptionIndex !== -1) {
      setFocusedOptionIndex(selectedOptionIndex);
    }
  };

  const updateOption = (option: Option, index: number) => {
    setSelectedOption(option.label);
    setSelectedOptionIndex(index);
    setSearchTerm('');
    setShowDropdown(false);
    setFocusedOptionIndex(index);
    if (inputRef.current) {
      inputRef.current.focus();
      inputRef.current.setSelectionRange(
        option.label.length,
        option.label.length
      );
    }
    if (onChange) {
      onChange(option);
    }
  };

  const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
    const value = event.target.value;
    setSearchTerm(value);
    setShowDropdown(true);
    setFocusedOptionIndex(-1);
    if (value === '') {
      clearSelection();
    }
  };

  const handleKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
    if (event.key === 'Backspace' && searchTerm === '' && selectedOption) {
      clearSelection();
    }
  };

  const onBlurHandler = useCallback(() => {
    setDropdownPosition({
      posX: 0,
      posY: 0,
      width: 0,
    });
    setShowDropdown(false);
  }, []);

  const filteredOptions: FilteredOption[] = options
    .map((option, index) => ({
      original: option,
      index,
    }))
    .filter((option) =>
      option.original.label.toLowerCase().includes(searchTerm.toLowerCase())
    );

  useEffect(() => {
    filteredOptions.forEach((value) => {
      if (value?.original?.label === selectedData?.label) {
        updateOption(value?.original, value?.index);
      }
    });
  }, [selectedData?.label]);

  const handleArrowKey = (event: KeyboardEvent) => {
    if (!showDropdown || disabled) return;
    if (event.key === 'ArrowDown') {
      setFocusedOptionIndex((prevIndex) => {
        const newIndex = Math.min(prevIndex + 1, filteredOptions.length - 1);
        scrollOptionIntoView(newIndex);
        return newIndex;
      });
    } else if (event.key === 'ArrowUp') {
      setFocusedOptionIndex((prevIndex) => {
        const newIndex = Math.max(prevIndex - 1, 0);
        scrollOptionIntoView(newIndex);
        return newIndex;
      });
    } else if (event.key === 'Enter' && focusedOptionIndex >= 0) {
      updateOption(
        filteredOptions[focusedOptionIndex].original,
        filteredOptions[focusedOptionIndex].index
      );
    }
  };

  const scrollOptionIntoView = (index: number) => {
    const optionElement = document.querySelector(`#option-${index}`);
    if (optionElement) {
      optionElement.scrollIntoView({ block: 'nearest' });
    }
  };

  const clearSelection = () => {
    setSelectedOption(null);
    setSearchTerm('');
    setFocusedOptionIndex(-1);
    setSelectedOptionIndex(-1);
    setShowDropdown(false);
    if (onClear) {
      onClear();
    }
  };

  useEffect(() => {
    document.addEventListener('keydown', handleArrowKey);
    return () => {
      document.removeEventListener('keydown', handleArrowKey);
    };
  }, [showDropdown, filteredOptions, focusedOptionIndex]);

  useEffect(() => {
    const updateElementPosition = () => {
      if (parentCheckingRef.current) {
        const rect = parentCheckingRef.current.getBoundingClientRect();
        setDropdownPosition({
          posX: rect.left + window.scrollX,
          posY:
            dropdownDirection === 'down'
              ? rect.bottom + window.scrollY
              : rect.top + window.scrollY - OFFSET,
          width: parentCheckingRef.current.offsetWidth,
        });
      }
    };

    const disableScroll = () => {
      const bodyScrollWidth = window.innerWidth - document.body.clientWidth;
      if (document.body.scrollHeight > window.innerHeight) {
        document.body.style.paddingRight = `${bodyScrollWidth}px`;
      }
      document.body.style.overflow = 'hidden';
    };

    const enableScroll = () => {
      document.body.style.paddingRight = '';
      document.body.style.overflow = '';
    };

    if (showDropdown) {
      disableScroll();
      updateElementPosition();
    } else {
      enableScroll();
    }
    const handleResize = () => {
      updateElementPosition();
    };
    window.addEventListener('resize', handleResize);
    window.addEventListener('scroll', handleResize, true);
    return () => {
      enableScroll();
      window.removeEventListener('resize', handleResize);
      window.removeEventListener('scroll', handleResize);
    };
  }, [showDropdown, dropdownDirection]);

  const toggleDropdown = () => {
    setShowDropdown((prevState) => !prevState);
  };

  return (
    <>
      <div className="storybook-autocomplete-container">
        {showLabel && (
          <Label
            {...labelProps}
            label={labelProps?.label || ''}
            color={error ? 'danger' : 'default'}
          />
        )}
        <div
          {...props}
          className={classNames(`storybook-autocomplete`, className, {
            ['storybook-select--disabled']: disabled,
            ['storybook-autocomplete--border']: showBorder,
            ['storybook-autocomplete--error']: error && showBorder,
            ['storybook-autocomplete--focused']: showDropdown && showBorder,
            ['storybook-autocomplete--no-border']: !showBorder,
            ['storybook-autocomplete--no-borderOnHover']: !showBorderOnHover,
            ['storybook-autocomplete--default']: showDropdown && !showBorder,
          })}
        >
          <span className="storybook-autocomplete-input-container">
            <input
              ref={inputRef}
              type="text"
              placeholder={placeholder}
              onClick={handleInput}
              value={selectedOption || searchTerm}
              onChange={handleInputChange}
              onKeyDown={handleKeyPress}
              className="storybook-autocomplete-inputField"
              autoComplete="off"
              disabled={disabled}
            />
            {/* if cross mark is required use this... */}
            {/* {(selectedOption || searchTerm) && !disabled && (
              <Icon
                name="close"
                height={10}
                className="storybook-autocomplete-close"
                onClick={clearSelection}
              />
            )} */}
          </span>
          <span className={`option-icon`} onClick={toggleDropdown}>
            {showDropdown ? (
              <Icon name="arrow_up" hoverEffect={false} height={6} width={9} />
            ) : (
              <Icon
                name="arrow_down"
                hoverEffect={false}
                height={6}
                width={9}
              />
            )}
          </span>
        </div>
        <div ref={parentCheckingRef}>
          {showDropdown &&
            !disabled &&
            createPortal(
              <DropdownList
                dropdownWidth={dropdownPosition.width}
                positionX={dropdownPosition.posX}
                positionY={dropdownPosition.posY}
                filteredOptions={filteredOptions}
                focusedOptionIndex={focusedOptionIndex}
                selectedOptionIndex={selectedOptionIndex}
                updateOption={updateOption}
                setSearchTerm={setSearchTerm}
                showDropdown={showDropdown}
                setShowDropdown={setShowDropdown}
                onBlurHandler={onBlurHandler}
                dropdownDirection={dropdownDirection}
              />,
              document.body
            )}
        </div>
        {error && (
          <span className="storybook-autocomplete--error-text">{errorMsg}</span>
        )}
      </div>
    </>
  );
};

export default Autocomplete;
