import React, { useEffect, useRef, useMemo, useCallback, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { QSMRootState } from '../../store/qsm-store';
import { setFieldValue, setSearchResults, setActiveSearchField, setMobileFilterType, setActiveSearchFieldProps } from '../../store/qsm-slice';
import useMediaQuery from '../../../shared/utils/use-media-query-util';
import SearchInput from '../search-input';
import Icon from '../../../shared/components/icon';
import { BaseFieldConfig, TypeaheadOption, OptionType } from '../../types';

interface Props {
  fieldConfig: BaseFieldConfig;
  enableMobileFilter?: boolean;
  highlightTarget?: string;
  isSecondInput?: boolean;
  isDoubleInput?: boolean;
  readOnlyForced?: boolean;
  isDisabled?: boolean;
}

const SearchInputGroup: React.FC<Props> = ({
  fieldConfig,
  enableMobileFilter = true,
  highlightTarget = '',
  isSecondInput = false,
  isDoubleInput = false,
  readOnlyForced = false,
  isDisabled = false
}) => {
  const dispatch = useDispatch();
  const ref = useRef<HTMLLabelElement>(null);
  const small = useMediaQuery('(max-width: 768px)');
  if (!fieldConfig) {
    return null;
  }
  const { fieldKey, label, placeholder, options, autoComplete } = fieldConfig;
  const [isLoading, setIsLoading] = useState(false);

  const selector = useMemo(() => (state: QSMRootState) => ((state.qsm as any)[fieldKey] ?? '') as string, [fieldKey]);
  const value = useSelector(selector);
  const { searchResults, activeSearchField } = useSelector((state: QSMRootState) => state.qsm);

  const selectedOption = options.find((option) => option.value === value);
  const typeOfSelectedOption: OptionType = selectedOption?.type ?? 'other';

  const match = useCallback(
    (input: string) => {
      if (!input) {
        return [];
      }

      const lowered = input.toLowerCase();

      return options
        .filter((option) => option.value.toLowerCase().includes(lowered) || option.iataCode?.toLowerCase().includes(lowered))
        .sort((a, b) => {
          const aExactIata = a.iataCode?.toLowerCase() === lowered;
          const bExactIata = b.iataCode?.toLowerCase() === lowered;

          if (aExactIata && !bExactIata) return -1;
          if (!aExactIata && bExactIata) return 1;

          return 0;
        });
    },
    [options]
  );

  const handleInputChange = useCallback(
    (input: string) => {
      dispatch(setFieldValue({ fieldKey, value: input }));
      dispatch(setSearchResults([]));

      if (small) return;

      dispatch(setActiveSearchField(fieldKey));

      // if field has custom onChange (API search)
      if (fieldConfig.onChange) {
        fieldConfig.onChange(input);
        setIsLoading(true);
        return;
      }

      // fallback to local filtering
      dispatch(setSearchResults(match(input)));
    },
    [dispatch, fieldKey, small, match, fieldConfig]
  );

  useEffect(() => {
    setIsLoading(false);
  }, [options]);

  useEffect(() => {
    if (!value || activeSearchField !== fieldKey) return;

    dispatch(setSearchResults(match(value)));
  }, [options, value, activeSearchField, fieldConfig, fieldKey]);

  const handleKeyDown = useCallback(
    (e: React.KeyboardEvent<HTMLInputElement>) => {
      if (!['Tab', 'Enter'].includes(e.key)) return;

      if (value.length === 3 && autoComplete) {
        const exactIataMatch = findExactIataMatch(options, value);

        if (exactIataMatch) {
          if (e.key === 'Enter') {
            e.preventDefault();
          }
          dispatch(setFieldValue({ fieldKey, value: exactIataMatch.value }));
          dispatch(setSearchResults([]));
          dispatch(setActiveSearchField(null));
        }
      }
    },
    [value, autoComplete, options, dispatch, fieldKey]
  );

  const handleOptionSelect = useCallback(
    (option: TypeaheadOption) => {
      dispatch(setFieldValue({ fieldKey, value: option.value }));
      dispatch(setSearchResults([]));
      dispatch(setActiveSearchField(null));
    },
    [dispatch, fieldKey]
  );

  const click = () => {
    if (isDisabled) return;

    dispatch(setActiveSearchField(fieldKey));
    dispatch(setSearchResults([]));
    if (small && enableMobileFilter) {
      dispatch(
        setActiveSearchFieldProps({
          fieldKey,
          label,
          placeholder,
          value,
          options
        })
      );
      dispatch(setMobileFilterType('search'));
    } else if (value.trim()) {
      dispatch(setSearchResults(match(value)));
    }
  };

  const findExactIataMatch = (options: TypeaheadOption[], input: string): TypeaheadOption | undefined =>
    options.find((option) => option.iataCode && option.iataCode.toLowerCase() === input.toLowerCase());

  useEffect(() => {
    const outside = (e: MouseEvent) => {
      if (ref.current && !ref.current.contains(e.target as Node)) {
        dispatch(setActiveSearchField(null));
      }
    };
    const esc = (e: KeyboardEvent) => e.key === 'Escape' && dispatch(setActiveSearchField(null));

    if (!small && activeSearchField === fieldKey) {
      document.addEventListener('mousedown', outside);
      document.addEventListener('keydown', esc);
    }
    return () => {
      document.removeEventListener('mousedown', outside);
      document.removeEventListener('keydown', esc);
    };
  }, [dispatch, small, activeSearchField, fieldKey]);

  return (
    <label className="qsm__single-input-wrapper" ref={ref}>
      <Icon name={typeOfSelectedOption === 'hotel' ? 'ui-hotel' : typeOfSelectedOption === 'airport' ? 'ui-flight' : 'ui-location'} height={16} />

      <span className={`qsm__label${isSecondInput ? ' qsm__label--second-input-label qsm__label--splittable' : ''}`}>{label}</span>

      <input
        type="text"
        id={fieldKey}
        name={fieldKey}
        value={value}
        disabled={isDisabled}
        readOnly={small || readOnlyForced || isDisabled}
        onFocus={click}
        onClick={(e) => e.stopPropagation()}
        onKeyDown={handleKeyDown}
        onChange={(e) => !small && !readOnlyForced && !isDisabled && handleInputChange(e.target.value)}
        className={`qsm__input${isSecondInput ? ' qsm__input--splittable' : ' u-ps-2'}`}
        placeholder={placeholder}
      />

      {!small && activeSearchField === fieldKey && (
        <SearchInput
          onChange={handleInputChange}
          searchResults={searchResults}
          onOptionSelect={handleOptionSelect}
          highlightTarget={highlightTarget}
          label={label}
          isSecondInput={isSecondInput}
          isDoubleInput={isDoubleInput}
          isDisabled={isDisabled}
          isLoading={isLoading}
        />
      )}
    </label>
  );
};

export default SearchInputGroup;
