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

export interface Option {
  label: string;
  value: string;
  disabled?: boolean;
}

export interface SelectProps {
  /**
   * Label properties
   */
  labelProps?: LabelProps;
  /**
   * Label properties
   */
  variant?: 'default' | 'approved' | 'review' | 'rejected';

  /**
   * If true, Field label will be displayed else it will be hidden
   */
  showLabel?: boolean;
  /**
   * Error | If true, error styles are applied
   */
  error?: boolean;
  /**
   * Disabled | If true, disabled styles are applied and field becomes non interactive
   */
  disabled?: boolean;
  /**
   * showBorder | If true, border will be added to select-box
   */
  showBorder?: boolean;
  /**
   * showBorderOnHover | If true, border will be added to select-box on hover
   */
  showBorderOnHover?: boolean;
  /**
   * SelectBox classes
   */
  className?: string;
  /**
   * If error is true, this message will be displayed
   */
  errorMsg?: string;
  /**
   * Options for the dropdown
   */
  options: Option[] | [];
  /**
   * Seleceted Option
   */
  selectedOption?: Option;
  /**
   * Placeholder | when the field is empty this will be displayed inside select box
   */
  placeholder: string;
  /**
   * on change handler
   */
  onChange?: (option: Option) => void;

  optionZindex?: string | number;
}
type DrowdownPosition = {
  posX: number;
  posY: number;
  width: number;
  fromBottom: number;
};
export interface DropDownListProps {
  dropdownPosition: DrowdownPosition;
  options: Option[] | [];
  selectedOption?: Option;
  onOptionClickHandler: (option: Option) => void;
  onBlurHandler: () => void;
  optionZindex: string | number;
}

const DropDownList = ({
  dropdownPosition,
  options,
  onOptionClickHandler,
  selectedOption,
  onBlurHandler,
  optionZindex,
}: DropDownListProps) => {
  const optionsContainerRef = useRef<HTMLDivElement>(null);
  useClickOutside(optionsContainerRef, onBlurHandler);
  const selectDivHeight = 36;
  const marginHeight = 5;
  const singleOptionHeight = 35;
  const dropdownContainerPadding = 16;
  let dropdownContainerHeight =
    options.length > 5
      ? 207
      : options.length * singleOptionHeight + dropdownContainerPadding * 2;
  if (options.length === 0) {
    dropdownContainerHeight = 67;
  }

  useEffect(() => {
    if (optionsContainerRef.current !== document.activeElement) {
      optionsContainerRef?.current?.focus();
    }
  }, [optionsContainerRef.current]);

  return (
    <>
      <div className="storybook-select-overlay"></div>
      <div
        tabIndex={0}
        ref={optionsContainerRef}
        style={
          dropdownPosition.fromBottom > dropdownContainerHeight + marginHeight
            ? {
                left: dropdownPosition.posX,
                top: dropdownPosition.posY,
                width: dropdownPosition.width,
                zIndex: optionZindex,
              }
            : {
                left: dropdownPosition.posX,
                top:
                  dropdownPosition.posY -
                  selectDivHeight -
                  dropdownContainerHeight -
                  marginHeight,
                width: dropdownPosition.width,
                zIndex: optionZindex,
                marginTop: 0,
              }
        }
        className="storybook-select-options-container"
      >
        <div className="options-wrapper">
          {options.length ? (
            options.map((option: Option) => {
              const isSelected = option.value === selectedOption?.value;
              return (
                <p
                  key={option.value}
                  className={classNames('storybook-select-option-list', {
                    ['storybook-select-option-list--selected']: isSelected,
                    ['storybook-select-option-list--disabled']: option.disabled,
                  })}
                  onClick={() => {
                    if (!option.disabled) {
                      onOptionClickHandler(option);
                    }
                  }}
                >
                  {option.label}
                </p>
              );
            })
          ) : (
            <p className="storybook-select-empty-options">No Options</p>
          )}
        </div>
      </div>
    </>
  );
};

const Select = ({
  options = [],
  selectedOption,
  placeholder = '',
  labelProps,
  disabled,
  className,
  errorMsg,
  error,
  onChange,
  showLabel = true,
  showBorder = true,
  showBorderOnHover = true,
  optionZindex = 999,
  variant = 'default',
  ...props
}: SelectProps) => {
  const [showOptions, setShowOptions] = useState(false);
  const [dropdownPosition, setDropdownPosition] = useState({
    posX: 0,
    posY: 0,
    width: 0,
    fromBottom: 0,
  });
  const dropdownListRef = useRef<HTMLDivElement>(null);

  const onSelectDivClickHandler = () => {
    if (!showOptions && !disabled) {
      if (dropdownListRef?.current) {
        const rect = dropdownListRef.current.getBoundingClientRect();
        setDropdownPosition({
          posX: rect.left + window.scrollX,
          posY: rect.top + window.scrollY,
          width: dropdownListRef.current.offsetWidth,
          fromBottom: window.innerHeight - rect.top,
        });
      }
      setShowOptions((prev) => !prev);
    }
  };

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

  const onOptionClickHandler = (option: Option) => {
    onBlurHandler();
    if (onChange) {
      onChange(option);
    }
  };

  useEffect(() => {
    const updateElementPosition = () => {
      if (dropdownListRef.current) {
        const rect = dropdownListRef.current.getBoundingClientRect();
        setDropdownPosition({
          posX: rect.left + window.scrollX,
          posY: rect.top + window.scrollY,
          width: dropdownListRef.current.offsetWidth,
          fromBottom: window.innerHeight - rect.top,
        });
      }
    };

    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 (showOptions) {
      disableScroll();
    } else {
      enableScroll();
    }

    const handleResize = () => {
      updateElementPosition();
    };
    window.addEventListener('resize', handleResize);
    window.addEventListener('scroll', handleResize);
    return () => {
      enableScroll();
      window.removeEventListener('resize', handleResize);
      window.removeEventListener('scroll', handleResize);
    };
  }, [showOptions]);

  return (
    <div className="storybook-select-container">
      {showLabel && (
        <Label
          {...labelProps}
          label={labelProps?.label || ''}
          color={error ? 'danger' : 'default'}
        />
      )}
      <div
        {...props}
        onClick={onSelectDivClickHandler}
        className={classNames(
          `storybook-select storybook-select--${variant}`,
          className,
          {
            ['storybook-select--disabled']: disabled,
            ['storybook-select--error']: error && showBorder,
            ['storybook-select--focused']: showOptions && showBorder,
            ['storybook-select--border']: showBorder,
            ['storybook-select--no-border']: !showBorder,
            ['storybook-select--no-borderOnHover']: !showBorderOnHover,
            ['storybook-select--default']: showOptions && !showBorder,
          }
        )}
      >
        <span
          className={classNames(
            `storybook-select-selected-option storybook-select-selected-option--${variant}`,
            {
              ['storybook-select-selected-option--placeholder']:
                !selectedOption,
            }
          )}
        >
          {selectedOption ? selectedOption.label : placeholder}
        </span>
        <span className={`option-icon option-icon--${variant}`}>
          {showOptions ? (
            <Icon name="arrow_up" hoverEffect={false} height={14} width={14} />
          ) : (
            <Icon
              name="arrow_down"
              hoverEffect={false}
              height={14}
              width={14}
            />
          )}
        </span>
      </div>
      <div ref={dropdownListRef}>
        {showOptions &&
          createPortal(
            <DropDownList
              dropdownPosition={dropdownPosition}
              onOptionClickHandler={onOptionClickHandler}
              options={options}
              selectedOption={selectedOption}
              onBlurHandler={onBlurHandler}
              optionZindex={optionZindex}
            />,
            document.body
          )}
      </div>
      {error && (
        <span className="storybook-select--error-text">{errorMsg}</span>
      )}
    </div>
  );
};

export default Select;
