import React, { useState, useEffect, useCallback, FormEvent } from 'react';
import { Button, AutocompleteTextBox, FormWrapper, StepTitle } from '@nova-hf/ui';
import { isEmpty, debounce } from 'lodash';
import { inject, observer } from 'mobx-react';
import { useApolloClient } from '@apollo/client';
import { ObservableMap } from 'mobx';
import { ADDRESSES, ADDRESS_CONNECTION } from 'graphql/queries/address';
import { PROPERTIES } from 'graphql/queries/properties';
import { useTranslation } from 'utils/i18n';

interface IProps {
  fiber?: any;
  color?: string;
  subscriptionId: string;
  onNext(): void;
}

const Addresses = ({ fiber, onNext, color }: IProps) => {
  const [address, setAddress] = useState('');
  const [selectedAddress, setSelectedAddress] = useState({
    street: '',
    houseNumber: '',
    zip: '',
    line: '',
    city: '',
  });
  const [addressStatus, setAddressStatus] = useState('');
  const [addressMessage, setAddressMessage] = useState('');

  const [checking, setChecking] = useState(false);
  const [submitted, setSubmitted] = useState(false);
  const [shouldSearch, setShouldSearch] = useState(true);
  const [autoComplete, setAutocomplete] = useState(new ObservableMap());

  const client = useApolloClient();
  const { t } = useTranslation('flutningur');

  useEffect(() => {
    if (shouldSearch) {
      search(address);
    }
  }, [address]);

  const search = useCallback(
    debounce((value: string) => {
      streetAutocomplete(value);
    }, 200),
    [],
  );

  const streetAutocomplete = async (query: string) => {
    if (autoComplete.has(query) || isEmpty(query)) {
      return;
    }

    const { data } = await client.query({
      query: ADDRESSES,
      variables: { input: { query } },
    });

    if (!data?.addresses) return;

    setAutocomplete(autoComplete.set(query, data.addresses));
  };

  const onChangeStreet = ({ target }: any) => {
    setSelectedAddress({ street: '', houseNumber: '', zip: '', line: '', city: '' });
    setShouldSearch(target.value.length > 0);
    setAddress(target.value as string);
    setSubmitted(false);
    setAddressMessage('');
    setAddressStatus('');

    fiber.confirmedAvailability = {};
  };

  const connectionState = async (selected: any) => {
    setAddressMessage(t('addresses.loading'));

    const { data } = await client.query({
      query: ADDRESS_CONNECTION,
      variables: {
        input: {
          street: selected.street,
          houseNumber: selected.houseNumber,
          zip: selected.zip,
        },
      },
    });

    if (!data?.addressConnection) {
      setAddressMessage(t('addresses.error'));
      setAddressStatus('error');
      return;
    }

    const {
      addressConnection: { status },
    } = data;

    if (status < 4) {
      setAddressMessage(t('addresses.warning.notReady'));
      setAddressStatus('warning');
      return;
    }

    setAddressMessage(t('addresses.success'));
    setAddressStatus('success');

    fiber.confirmedAvailability = selected;
  };

  const onSelectIndex = (index: string) => {
    if (isEmpty(address)) return;

    const res = autoComplete.get(address) || [];

    if (res.length > 0) {
      setShouldSearch(false);
      setSelectedAddress(
        res[index] as React.SetStateAction<{
          street: string;
          houseNumber: string;
          zip: string;
          line: string;
          city: string;
        }>,
      );
      setAddress((res[index]?.line || '') as string);

      if (address.length > 0 && !isEmpty(res[index])) {
        connectionState(res[index]);
      }
    }
  };

  const onSubmit = async (e: FormEvent) => {
    e.preventDefault();

    if (!address || addressStatus !== 'success' || !selectedAddress) {
      setAddressMessage(t('addresses.warning.addressMissing'));
      setAddressStatus('warning');
      setChecking(false);
      return;
    }

    setSubmitted(true);
    setChecking(true);

    try {
      await connectionState(selectedAddress);
    } catch (error) {
      setChecking(false);
      setAddressMessage(t('addresses.error'));
      setAddressStatus('error');

      return;
    }

    fiber.properties = [];
    fiber.property = null;

    try {
      const { data } = await client.query({
        query: PROPERTIES,
        variables: {
          input: {
            street: selectedAddress.street,
            houseNumber: selectedAddress.houseNumber,
            zip: selectedAddress.zip,
          },
        },
      });

      setChecking(false);

      if (!data?.properties) {
        setAddressMessage(t('addresses.error'));
        setAddressStatus('error');
        return;
      }

      fiber.properties = data?.properties;

      onNext();
    } catch (error) {
      setChecking(false);
      setAddressMessage(t('addresses.error'));
      setAddressStatus('error');
    }
  };

  const results = autoComplete.get(address) || [];
  const { zip, city } = selectedAddress;

  return (
    <form onSubmit={onSubmit}>
      <StepTitle title={t('addresses.title')} subtitle={t('addresses.subtitle')} color={color} />
      <FormWrapper color={color}>
        <AutocompleteTextBox
          label={t('addresses.label')}
          name="street"
          loading={!isEmpty(address) && isEmpty(results) && shouldSearch}
          value={address}
          onChange={onChangeStreet}
          onSelectIndex={onSelectIndex}
          autoComplete="off"
          data-key="street"
          message={addressMessage}
          status={addressStatus}
          autoCompleteInfo={zip && city && `${zip} ${city}`}
          submitted={submitted}
        >
          {results?.slice(0, 30).map((r: any) => (
            <div key={`${r.line}-${r.zip}`}>{`${r.line}, ${r.zip} ${r.city}`}</div>
          ))}
        </AutocompleteTextBox>
        <Button big loading={checking} type="submit" fill background={color} arrowRight>
          {t('button')}
        </Button>
      </FormWrapper>
    </form>
  );
};

export default inject('fiber')(observer(Addresses));
