import React, { useEffect, useId, useRef, useState } from 'react';
import { AsyncPaginate } from 'react-select-async-paginate';
import axios from 'axios';

const areStringArraysEqual = (left = [], right = []) =>
  left.length === right.length &&
  left.every((value, index) => value === right[index]);

const areSelectOptionsEqual = (left = [], right = []) =>
  left.length === right.length &&
  left.every(
    (option, index) =>
      option?.value === right[index]?.value &&
      option?.label === right[index]?.label,
  );

export const SelectFieldMany = ({
  options,
  field,
  form,
  itemRef,
  showField,
}) => {
  const [value, setValue] = useState([]);
  const appliedOptionsSignatureRef = useRef<string | null>(null);
  const PAGE_SIZE = 50;

  useEffect(() => {
    if (field.value?.[0] && typeof field.value[0] !== 'string') {
      const normalizedValue = field.value.map((el) => el.id);
      const isAlreadyNormalized =
        Array.isArray(field.value) &&
        areStringArraysEqual(field.value, normalizedValue);

      if (!isAlreadyNormalized) {
        form.setFieldValue(field.name, normalizedValue, false);
      }
    } else if (!field.value || field.value.length === 0) {
      setValue((currentValue) =>
        currentValue.length === 0 ? currentValue : [],
      );
    }
  }, [field.name, field.value, form]);

  useEffect(() => {
    if (Array.isArray(options) && options.length > 0) {
      const selectedOptions = options.map((el) => ({
        value: el.id,
        label: el[showField],
      }));
      const selectedIds = options.map((el) => el.id);
      const optionsSignature = selectedIds.join('|');
      const shouldApplyInitialOptions =
        appliedOptionsSignatureRef.current !== optionsSignature;
      const fieldValueMatchesInitialOptions = areStringArraysEqual(
        field.value,
        selectedIds,
      );

      if (shouldApplyInitialOptions) {
        appliedOptionsSignatureRef.current = optionsSignature;
        form.setFieldValue(field.name, selectedIds, false);
      }

      if (shouldApplyInitialOptions || fieldValueMatchesInitialOptions) {
        setValue((currentValue) =>
          areSelectOptionsEqual(currentValue, selectedOptions)
            ? currentValue
            : selectedOptions,
        );
      }
    }
  }, [field.name, field.value, form, options, showField]);

  const mapResponseToValuesAndLabels = (data) => ({
    value: data.id,
    label: data.label,
  });

  const handleChange = (
    data: Array<{ value: string; label: string }> | null,
  ) => {
    const selectedOptions = data || [];
    setValue(selectedOptions);
    form.setFieldValue(
      field.name,
      selectedOptions.map((el) => el?.value || null),
    );
  };

  async function callApi(
    inputValue: string,
    loadedOptions: Array<{ value: string; label: string }>,
  ) {
    const path = `/${itemRef}/autocomplete?limit=${PAGE_SIZE}&offset=${loadedOptions.length}${inputValue ? `&query=${inputValue}` : ''}`;
    const { data } = await axios(path);
    return {
      options: data.map(mapResponseToValuesAndLabels),
      hasMore: data.length === PAGE_SIZE,
    };
  }
  return (
    <AsyncPaginate
      classNames={{
        control: () => 'px-1 py-2',
      }}
      classNamePrefix='react-select'
      instanceId={useId()}
      value={value}
      isMulti
      debounceTimeout={1000}
      loadOptions={callApi}
      onChange={handleChange}
      defaultOptions
      isClearable
    />
  );
};
