import React, {
   useRef,
   useEffect,
   InputHTMLAttributes,
   useState,
   useImperativeHandle,
   useCallback,
   useMemo,
} from 'react';
import { useDeepEffect, useDidUpdateEffect } from '@devesharp/react-hooks';
import { useField } from '@unform/core';
import isEqual from 'lodash/isEqual';
import { ISelectProps } from './Select.interface';

const ReactNative = typeof navigator !== 'undefined' && navigator?.product === 'ReactNative';

function isJson(str): boolean {
   if (typeof str === 'number' || str == null) return false;

   try {
      JSON.parse(str);
   } catch (e) {
      return false;
   }
   return true;
}

export function convertToJSON(value: any): string {
   if (value === null) return '';
   if (typeof value === 'object') return JSON.stringify(value);

   return value;
}

export function convertToString(value: any): string {
   if (isJson(value)) return JSON.parse(value);
   if (value === '') return null;

   return value;
}

export const SelectCore = React.forwardRef<InputHTMLAttributes<any>, ISelectProps>(function SelectCore(
   props,
   inputRef: any,
) {
   if (props.name) {
      return <SelectCoreWithUnform {...props} ref={inputRef} />;
   }

   return <SelectCoreWithoutUnform {...props} ref={inputRef} value={props.value ?? null} />;
});

export const SelectCoreWithUnform = React.forwardRef<InputHTMLAttributes<any>, ISelectProps>(
   function SelectCoreWithUnform(props, inputRef: any) {
      const ref = useRef<any>();
      useImperativeHandle(inputRef, () => ref.current, []);

      const { fieldName, registerField, defaultValue = null, error, clearError } = useField(props.name);

      const getDefaultValue = (): any => {
         let value = ReactNative
            ? // apenas resgatar valor, se existir nas options
              props.options?.find((i) => isEqual(i.value, defaultValue))?.value ?? null
            : convertToJSON(defaultValue);

         if (value == '' || value == null) {
            value = props.options?.find((i) => isEqual(i.value, value))?.value ?? value;

            if ((value == '' || value == null) && props.options?.length) {
               value = props.options[0]?.value ?? value;
            }
         }

         return value;
      };

      const defaultOption = useMemo(getDefaultValue, []);

      const inputValueRef = useRef(defaultOption);
      const [value, setValue] = useState<any>(ReactNative ? defaultValue : undefined);

      useEffect(() => {
         registerField({
            name: fieldName,
            ref: inputValueRef,
            path: 'current' as any,
            clearValue(_ref) {
               _ref.current = '';

               if (ReactNative) {
                  setValue(null);
               }
            },
            setValue(_ref, v) {
               _ref.current = convertToJSON(v);

               if (ReactNative) {
                  _ref.current = v;
                  setValue(v);
               } else if (v == undefined) {
                  ref.current.value = convertToJSON(null);
               } else {
                  ref.current.value = convertToJSON(v);
               }
            },
            getValue(_ref) {
               if (ReactNative) {
                  return _ref.current;
               }

               return convertToString(_ref.current);
            },
         });
      }, [fieldName, registerField, props.options]);

      // Em alguns casos, os dados são recuperados assincronamente do servidor
      // nesses casos deve-se verificar se o valor está na lista recuperada
      useDidUpdateEffect(() => {
         const v = inputValueRef.current;

         if (v) {
            ref.current.value = convertToJSON(v);
         }

         if (v == '' || v == undefined || v == null) {
            ref.current.value = getDefaultValue();
            inputValueRef.current = getDefaultValue();
         }
      }, [props.options]);

      const onChange = useCallback(
         function onSelectItem(newValue: any) {
            if (error) clearError();
            inputValueRef.current = convertToJSON(newValue);

            if (ReactNative) {
               setValue(newValue);
            }

            if (props.onChange) props.onChange(newValue);
         },
         [error],
      );

      return (
         <SelectCoreWithoutUnform
            {...props}
            ref={ref}
            onChange={onChange}
            value={value}
            error={error}
            defaultValue={defaultValue}
         />
      );
   },
);

export const SelectCoreWithoutUnform = React.forwardRef<InputHTMLAttributes<any>, ISelectProps>(
   function SelectCoreWithoutUnform(props, inputRef: any) {
      const ref = useRef(null);
      useImperativeHandle(inputRef, () => ref.current, []);

      const PureComponent = useMemo(() => props.as, [props.as]) as any;

      return <PureComponent {...props} ref={ref} />;
   },
);
