import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useFormContext } from 'react-hook-form';

import { Box } from 'components/common';
import { LocationSearch } from 'components/location';
import { LocationFeature } from 'components/location/LocationSearch';
import { RangeInput } from 'components/inputs';

import { inputStyles } from './inputStyles';

type LocationSearchOptions = {
  minDistance?: number;
  maxDistance?: number;
  radiusPlaceholder?: string;
  searchPlaceholder?: string;
  searchInputDelay?: number;
  searchLayers?: string;
  searchSize?: number;
};

const DEFAULT_OPTIONS: LocationSearchOptions = {
  minDistance: 0,
  // maxDistance: 20000,
  radiusPlaceholder: 'Distance around (km)',
  searchPlaceholder: 'Search...',
  searchInputDelay: 500,
  // searchLayers: '',
  // searchSize: 10,
};

type LocationSearchFilterState = [string, string, string, string];

export const LocationSearchFilter = ({
  id,
  options,
}: {
  id: string;
  options: LocationSearchOptions[];
}) => {
  const settings = useMemo(
    () => Object.assign(DEFAULT_OPTIONS, options?.[0]),
    [options]
  );

  const { setValue, getValues, formState } = useFormContext();
  const state = getValues(id) as LocationSearchFilterState | undefined; // ['label', 'lat', 'lng', 'distance']
  const initialState = state?.[0];

  const [disableInput, setDisableInput] = useState(!initialState);

  // TODO refactor react-hook-form reset workaround
  const wasDirty = useRef(formState.isDirty);
  const keyRef = useRef<number>();
  const resetKey = useMemo(() => {
    const reset = wasDirty.current === true && formState.isDirty === false;
    keyRef.current = reset ? +new Date() : keyRef.current;
    wasDirty.current = formState.isDirty;
    return keyRef.current;
  }, [formState.isDirty]);

  useEffect(() => {
    setDisableInput(!initialState);
  }, [resetKey, initialState]);

  const [distance, setDistance] = useState(settings.maxDistance + '');
  const handleDistance: React.ChangeEventHandler<HTMLInputElement> = (ev) => {
    setDistance(ev.target.value);
    const state = [...getValues(id)];
    state[3] = ev.target.value;
    setValue(id, state, { shouldDirty: true });
  };

  const handleChange: React.ChangeEventHandler<HTMLInputElement> = useCallback(
    (ev) => {
      if (formState.isDirty === false) {
        const state = getValues(id);
        setValue(id, state, { shouldTouch: true, shouldDirty: true });
      }
      const { value } = ev.target;
      if (value === '') {
        setValue(id, ['', '', '', '']);
        setDisableInput(true);
      }
    },
    [id, getValues, setValue, formState.isDirty]
  );

  const handleSelect = useCallback(
    (feature: LocationFeature) => {
      const {
        geometry: { coordinates },
        properties: { label },
      } = feature;

      setValue(
        id,
        [label, `${coordinates[0]}`, `${coordinates[1]}`, distance],
        { shouldDirty: true }
      );

      setDisableInput(false);
    },
    [id, setValue, distance]
  );

  return (
    <Box paddingX={3} spacingY={2}>
      <Box width="100%">
        <LocationSearch
          name="location"
          className="medium"
          style={inputStyles}
          initialState={initialState}
          onChange={handleChange}
          onSelect={handleSelect}
          placeholder={settings.searchPlaceholder}
          searchInputDelay={settings.searchInputDelay}
          searchLayers={settings.searchLayers}
          searchSize={settings.searchSize}
          key={resetKey}
        />
      </Box>
      <Box>
        <RangeInput
          label={settings.radiusPlaceholder || 'Distance'}
          min={settings.minDistance}
          max={settings.maxDistance}
          value={distance}
          formattedValue={distance + ' km'}
          onChange={handleDistance}
          disabled={disableInput}
        />
      </Box>
    </Box>
  );
};
