import { useEffect, useState, useRef } from 'react';
import { UNWalletCode, UNWalletName } from '../../../types/shared/wallet.types';
import type { UNWallet } from '../../../types/shared/wallet.types';
import { UnitSDK } from '../../../unitSdkManager/UnitSdkManager';
import { getMobileWalletPayload } from '../../../networking/requests/UNWalletPayloadRequest';
import { useSelector, useDispatch } from 'react-redux';
import { selectWallet, setSignedNonce } from '../../../slices/pushProvisioningSlice';
import { useLaunchInitialize } from './useLaunchInitialize';
import { UNVPErrorType } from '../types';
import { promiseRejectToUNVPErrorType } from '../helpers';
import { UNErrorCodes, UNErrorData } from '../../../types/shared/error.types';
import { isUNError } from '../../../types/internal/errorHelpers';

export type CardToEncryptedPayload = {
  [cardId: string]: string;
};

export const useCardWallet = (cardId: string) => {
  const [currentUNWallet, setCurrentUNWallet] = useState<UNWallet>();
  const { signedNonce } = useSelector(selectWallet);
  const { initializePushProvisioning } = useLaunchInitialize();
  const dispatch = useDispatch();
  const isRecoveringSignedNonceRef = useRef<boolean>(false);

  const shouldRecoverVPSDKForError = (errorType: UNVPErrorType) => {
    const recoveringTypes = [UNVPErrorType.PayloadDecryptionFailed, UNVPErrorType.SessionExpired, UNVPErrorType.InvalidNonce];
    return recoveringTypes.includes(errorType);
  };

  useEffect(() => {
    const getEncryptedPayload = async () => {
      const env = UnitSDK.getEnv();
      const customerToken = UnitSDK.getCustomerToken();
      if (!env || !customerToken) return;
      // In case we use flow
      if (!signedNonce) {
        await initializePushProvisioning();
        return;
      }
      const encryptedPayload = await getMobileWalletPayload(customerToken, cardId, env, signedNonce);
      return encryptedPayload;
    };

    const getCardWalletData = async () => {
      const currentProvisioningModule = UnitSDK.getPushProvisionModule();
      if (!currentProvisioningModule) return;
      try {
        const encryptedPayload = await getEncryptedPayload();
        if (!encryptedPayload) return;
        const walletsResponse = await currentProvisioningModule.launchGetWallets(JSON.stringify({ encPayload: encryptedPayload }));
        const unWallet: UNWallet | null = parseWalletsResponse(walletsResponse);
        if (!unWallet) return;
        setCurrentUNWallet(unWallet);
        return;
      } catch (error) {
        console.error('Coudln\'t get card wallet data:', error);
        if (isRecoveringSignedNonceRef.current) return;
        isRecoveringSignedNonceRef.current = true;

        // handle unit network api errors
        if (isUNError(error)) {
          const errors: UNErrorData[] = error.errors;
          // errors will return in an array but hold only a single error element
          if (errors[0]?.code === UNErrorCodes.INVALID_NONCE) {
            dispatch(setSignedNonce(null));
          }
          return;
        }

        // handle VDE SDK errors
        const errorType = promiseRejectToUNVPErrorType(error);
        if (!errorType) return;
        if (shouldRecoverVPSDKForError(errorType)) {
          dispatch(setSignedNonce(null));
          return;
        }
      }
    };

    getCardWalletData();
  }, [signedNonce, cardId]);

  return {
    currentUNWallet,
  };

};

const parseWalletsResponse = (walletsResponse: string): UNWallet | null => {
  try {
    const parsedWalletsResponse = JSON.parse(walletsResponse);
    const wallets = parsedWalletsResponse.wallets;

    if (!wallets) return null;
    if (wallets[0].code === UNWalletCode.Apple) {
      return ({ name: UNWalletName.Apple, code: UNWalletCode.Apple, status: wallets[0].status });
    } else {
      const googleWallet = wallets.filter((wallet: { name: UNWalletName }) => {
        return wallet.name === UNWalletName.Google;
      });
      if (!googleWallet) return null;
      return ({ name: UNWalletName.Google, code: UNWalletCode.Google, status: googleWallet[0].status });
    }
  } catch (error) {
    throw new Error('Error parsing wallet response');
  }
};
