import tuvali from '@mosip/tuvali';
import BluetoothStateManager from 'react-native-bluetooth-state-manager';
import {EmitterSubscription, Linking} from 'react-native';
import {
  checkMultiple,
  PERMISSIONS,
  requestMultiple,
  RESULTS,
} from 'react-native-permissions';
import {assign, EventFrom, send, StateFrom} from 'xstate';
import {createModel} from 'xstate/lib/model';
import {DeviceInfo} from '../../../components/DeviceInfoList';
import {getDeviceNameSync} from 'react-native-device-info';
import {StoreEvents} from '../../store';
import {VC} from '../../../types/VC/ExistingMosipVC/vc';
import {AppServices} from '../../../shared/GlobalContext';
import {
  androidVersion,
  isAndroid,
  RECEIVED_VCS_STORE_KEY,
} from '../../../shared/constants';
import {ActivityLogEvents, ActivityLogType} from '../../activityLog';
import {VcEvents} from '../../vc';
import {subscribe} from '../../../shared/openIdBLE/verifierEventHandler';
import {log} from 'xstate/lib/actions';
import {VerifierDataEvent} from '@mosip/tuvali/src/types/events';
import {BLEError} from '../types';
import Storage from '../../../shared/storage';
import {VCMetadata} from '../../../shared/VCMetadata';
import {
  getEndEventData,
  getErrorEventData,
  getImpressionEventData,
  getStartEventData,
  sendEndEvent,
  sendErrorEvent,
  sendImpressionEvent,
  sendStartEvent,
} from '../../../shared/telemetry/TelemetryUtils';
import {TelemetryConstants} from '../../../shared/telemetry/TelemetryConstants';
// import { verifyPresentation } from '../shared/vcjs/verifyPresentation';

const {verifier, EventTypes, VerificationStatus} = tuvali;

const model = createModel(
  {
    serviceRefs: {} as AppServices,
    senderInfo: {} as DeviceInfo,
    receiverInfo: {} as DeviceInfo,
    incomingVc: {} as VC,
    openId4VpUri: '',
    bleError: {} as BLEError,
    loggers: [] as EmitterSubscription[],
    receiveLogType: '' as ActivityLogType,
    readyForBluetoothStateCheck: false,
  },
  {
    events: {
      ACCEPT: () => ({}),
      ACCEPT_AND_VERIFY: () => ({}),
      GO_TO_RECEIVED_VC_TAB: () => ({}),
      REJECT: () => ({}),
      CANCEL: () => ({}),
      RESET: () => ({}),
      DISMISS: () => ({}),
      VC_RECEIVED: (vc: VC) => ({vc}),
      ADV_STARTED: (openId4VpUri: string) => ({openId4VpUri}),
      CONNECTED: () => ({}),
      DISCONNECT: () => ({}),
      BLE_ERROR: (bleError: BLEError) => ({bleError}),
      EXCHANGE_DONE: (senderInfo: DeviceInfo) => ({senderInfo}),
      SCREEN_FOCUS: () => ({}),
      SCREEN_BLUR: () => ({}),
      BLUETOOTH_STATE_ENABLED: () => ({}),
      BLUETOOTH_STATE_DISABLED: () => ({}),
      NEARBY_ENABLED: () => ({}),
      NEARBY_DISABLED: () => ({}),
      STORE_READY: () => ({}),
      STORE_RESPONSE: (response: unknown) => ({response}),
      STORE_ERROR: (error: Error) => ({error}),
      RECEIVE_DEVICE_INFO: (info: DeviceInfo) => ({info}),
      RECEIVED_VCS_UPDATED: () => ({}),
      VC_RESPONSE: (vcMetadatas: VCMetadata[]) => ({vcMetadatas}),
      GOTO_SETTINGS: () => ({}),
      APP_ACTIVE: () => ({}),
      FACE_VALID: () => ({}),
      FACE_INVALID: () => ({}),
      RETRY_VERIFICATION: () => ({}),
    },
  },
);
export const RequestEvents = model.events;

export const requestMachine =
  /** @xstate-layout   */
  model.createMachine(
    {
      predictableActionArguments: true,
      preserveActionOrder: true,
      tsTypes: {} as import('./requestMachine.typegen').Typegen0,
      schema: {
        context: model.initialContext,
        events: {} as EventFrom<typeof model>,
        services: {} as {
          verifyVp: {
            data: VC;
          };
        },
      },
      invoke: {
        src: 'monitorConnection',
      },
      id: 'request',
      initial: 'inactive',
      on: {
        SCREEN_BLUR: {
          target: '.inactive',
        },
        SCREEN_FOCUS: {
          // eslint-disable-next-line sonarjs/no-duplicate-string
          target: '.checkStorage',
        },
        BLE_ERROR: {
          target: '.handlingBleError',
          actions: ['sendBLEConnectionErrorEvent', 'setBleError'],
        },
        RESET: {
          target: '.checkNearbyDevicesPermission',
        },
      },
      states: {
        inactive: {
          entry: 'removeLoggers',
        },
        checkStorage: {
          invoke: {
            src: 'checkStorageAvailability',
            onDone: [
              {
                cond: 'isMinimumStorageLimitReached',
                target: 'storageLimitReached',
              },
              {
                target: 'checkNearbyDevicesPermission',
              },
            ],
          },
        },
        storageLimitReached: {},
        checkNearbyDevicesPermission: {
          initial: 'checking',
          states: {
            checking: {
              invoke: {
                src: 'checkNearByDevicesPermission',
              },
              on: {
                NEARBY_ENABLED: {
                  actions: 'setReadyForBluetoothStateCheck',
                  target: '#request.checkingBluetoothService',
                },
                NEARBY_DISABLED: {
                  target: 'requesting',
                },
              },
            },
            requesting: {
              invoke: {
                src: 'requestNearByDevicesPermission',
              },
              on: {
                NEARBY_ENABLED: {
                  target: '#request.checkingBluetoothService',
                },
                NEARBY_DISABLED: {
                  target: '#request.nearByDevicesPermissionDenied',
                },
              },
            },
          },
        },

        checkingBluetoothService: {
          initial: 'checking',
          states: {
            checking: {
              invoke: {
                src: 'checkBluetoothService',
              },
              on: {
                BLUETOOTH_STATE_ENABLED: {
                  target: 'enabled',
                },
                BLUETOOTH_STATE_DISABLED: {
                  target: 'requesting',
                },
              },
            },
            requesting: {
              invoke: {
                src: 'requestBluetooth',
              },
              on: {
                BLUETOOTH_STATE_ENABLED: {
                  target: 'enabled',
                },
                BLUETOOTH_STATE_DISABLED: {
                  target: '#request.bluetoothDenied',
                },
              },
            },
            enabled: {
              always: {
                // eslint-disable-next-line sonarjs/no-duplicate-string
                target: '#request.clearingConnection',
              },
            },
          },
        },
        bluetoothDenied: {},
        nearByDevicesPermissionDenied: {
          on: {
            APP_ACTIVE: '#request.checkNearbyDevicesPermission',
            GOTO_SETTINGS: {
              actions: 'openAppPermission',
            },
          },
        },
        clearingConnection: {
          invoke: {
            src: 'disconnect',
          },
          on: {
            DISCONNECT: {
              target: '#request.waitingForConnection',
              actions: [],
              internal: false,
            },
          },
          after: {
            DESTROY_TIMEOUT: {
              target: '#request.waitingForConnection',
              actions: [],
              internal: false,
            },
          },
        },
        waitingForConnection: {
          entry: ['removeLoggers', 'registerLoggers'],
          invoke: {
            src: 'advertiseDevice',
          },
          on: {
            ADV_STARTED: {
              actions: 'setOpenID4VpUri',
            },
            CONNECTED: {
              target: 'waitingForVc',
              actions: [
                'setSenderInfo',
                'setReceiverInfo',
                'sendVCReceivingStartEvent',
              ],
            },

            DISCONNECT: {
              target: 'disconnected',
            },
          },
        },
        waitingForVc: {
          invoke: {
            src: 'receiveVc',
          },
          initial: 'inProgress',
          states: {
            inProgress: {
              on: {
                CANCEL: {
                  target: '#request.cancelling',
                  actions: ['sendVCReceivingTerminatedEvent'],
                },
              },
              after: {
                SHARING_TIMEOUT: {
                  target: '#request.waitingForVc.timeout',
                  actions: [],
                  internal: false,
                },
              },
            },
            timeout: {
              on: {
                CANCEL: {
                  target: '#request.cancelling',
                  actions: 'sendVCReceiveFlowTimeoutEndEvent',
                },
              },
            },
          },
          on: {
            DISCONNECT: {
              target: 'disconnected',
              actions: ['sendVCReceivingDisconnectedEvent'],
            },
            VC_RECEIVED: {
              target: 'reviewing.accepting',
              actions: 'setIncomingVc',
            },
          },
        },
        cancelling: {
          always: {
            target: '#request.clearingConnection',
          },
        },
        reviewing: {
          initial: 'idle',
          states: {
            idle: {},
            verifyingIdentity: {
              exit: 'clearShouldVerifyPresence',
              on: {
                FACE_VALID: {
                  target: 'accepting',
                  actions: 'setReceiveLogTypeVerified',
                },
                FACE_INVALID: {
                  target: 'invalidIdentity',
                  actions: [
                    'setReceiveLogTypeUnverified',
                    'sendVCReceiveFailedEvent',
                  ],
                },
                CANCEL: {
                  target: 'idle',
                },
              },
            },
            invalidIdentity: {
              on: {
                DISMISS: {
                  target: 'accepting',
                },
                RETRY_VERIFICATION: {
                  target: 'verifyingIdentity',
                },
              },
            },
            verifyingVp: {
              invoke: {
                src: 'verifyVp',
                onDone: [
                  {
                    target: 'accepting',
                  },
                ],
                onError: [
                  {
                    target: 'idle',
                    actions: log('Failed to verify Verifiable Presentation'),
                  },
                ],
              },
            },
            accepting: {
              initial: 'requestingReceivedVcs',
              states: {
                requestingReceivedVcs: {
                  entry: 'requestReceivedVcs',
                  on: {
                    VC_RESPONSE: [
                      {
                        target: 'requestingExistingVc',
                        cond: 'hasExistingVc',
                      },
                      {
                        target: 'prependingReceivedVc',
                      },
                    ],
                  },
                },
                requestingExistingVc: {
                  entry: 'requestExistingVc',
                  on: {
                    STORE_RESPONSE: {
                      target: 'mergingIncomingVc',
                    },
                  },
                },
                mergingIncomingVc: {
                  entry: 'mergeIncomingVc',
                  on: {
                    STORE_RESPONSE: {
                      target: '#request.reviewing.accepted',
                    },
                  },
                },
                prependingReceivedVc: {
                  entry: 'prependReceivedVc',
                  on: {
                    STORE_RESPONSE: {
                      target: 'storingVc',
                    },
                  },
                },
                storingVc: {
                  entry: 'storeVc',
                  on: {
                    STORE_RESPONSE: {
                      target: '#request.reviewing.accepted',
                    },
                  },
                },
              },
              on: {
                STORE_ERROR: {
                  target: '#request.reviewing.savingFailed',
                },
              },
            },
            accepted: {
              entry: [
                'sendVcReceived',
                'setReceiveLogTypeRegular',
                'logReceived',
                'sendVCReceiveSuccessEvent',
              ],
              invoke: {
                src: 'sendVcResponse',
                data: {
                  status: VerificationStatus.ACCEPTED,
                },
              },
              on: {
                DISMISS: {
                  target: 'displayingIncomingVC',
                },
              },
            },
            rejected: {
              entry: [
                'setReceiveLogTypeDiscarded',
                'logReceived',
                'sendVCReceiveRejectedEvent',
              ],
              invoke: {
                src: 'sendVcResponse',
                data: {
                  status: VerificationStatus.REJECTED,
                },
              },
              on: {
                DISMISS: {
                  target: '#request.clearingConnection',
                },
              },
            },
            navigatingToHistory: {
              invoke: {
                src: 'disconnect',
              },
            },
            displayingIncomingVC: {
              on: {
                GO_TO_RECEIVED_VC_TAB: {
                  target: 'navigatingToReceivedCards',
                },
              },
            },
            navigatingToReceivedCards: {
              on: {
                DISMISS: {
                  target: 'navigatingToHome',
                },
              },
            },
            navigatingToHome: {
              invoke: {
                src: 'disconnect',
              },
              on: {
                DISCONNECT: {
                  target: '#request.inactive',
                },
              },
            },
            savingFailed: {
              initial: 'idle',
              entry: [
                'setReceiveLogTypeDiscarded',
                'logReceived',
                'sendVCReceiveRejectedEvent',
              ],
              invoke: {
                src: 'sendVcResponse',
                data: {
                  status: VerificationStatus.REJECTED,
                },
              },
              states: {
                idle: {},
                viewingVc: {},
              },
              on: {
                DISMISS: {
                  target: '.viewingVc',
                },
                GO_TO_RECEIVED_VC_TAB: {
                  target: 'navigatingToHistory',
                },
              },
            },
          },
          on: {
            ACCEPT: {
              target: '.accepting',
              actions: 'setReceiveLogTypeRegular',
            },
            ACCEPT_AND_VERIFY: {
              target: '.verifyingIdentity',
            },
            REJECT: {
              target: '.rejected',
            },
            CANCEL: {
              target: '.rejected',
            },
          },
        },
        disconnected: {
          on: {
            RESET: {
              target: 'waitingForConnection',
            },
            DISMISS: {
              target: '#request.reviewing.navigatingToHome',
            },
          },
        },
        handlingBleError: {
          on: {
            RESET: {
              target: 'checkNearbyDevicesPermission',
            },
            DISMISS: {
              target: '#request.reviewing.navigatingToHome',
            },
          },
        },
      },
    },
    {
      actions: {
        openAppPermission: () => {
          Linking.openSettings();
        },

        requestReceivedVcs: send(VcEvents.GET_RECEIVED_VCS(), {
          to: context => context.serviceRefs.vc,
        }),

        setReadyForBluetoothStateCheck: model.assign({
          readyForBluetoothStateCheck: () => true,
        }),

        setIncomingVc: assign({
          incomingVc: (_context, event) => {
            const vp = event.vc.verifiablePresentation;
            return vp != null
              ? {
                  ...event.vc,
                  verifiableCredential: vp.verifiableCredential[0],
                }
              : event.vc;
          },
        }),

        setOpenID4VpUri: assign({
          openId4VpUri: (_context, event) => {
            return event.openId4VpUri;
          },
        }),

        setSenderInfo: assign({
          senderInfo: () => {
            return {name: 'Wallet', deviceName: 'Wallet', deviceId: ''};
          },
        }),

        setReceiverInfo: assign({
          receiverInfo: () => {
            return {name: 'Verifier', deviceName: 'Verifier', deviceId: ''};
          },
        }),

        setBleError: assign({
          bleError: (_context, event) => event.bleError,
        }),

        registerLoggers: assign({
          loggers: () => {
            if (__DEV__) {
              return [
                verifier.handleDataEvents(event => {
                  console.log(
                    getDeviceNameSync(),
                    '<Receiver.Event>',
                    JSON.stringify(event).slice(0, 100),
                  );
                }),
              ];
            } else {
              return [];
            }
          },
        }),

        removeLoggers: assign({
          loggers: ({loggers}) => {
            loggers?.forEach(logger => logger.remove());
            return null;
          },
        }),

        prependReceivedVc: send(
          context => {
            return StoreEvents.PREPEND(
              RECEIVED_VCS_STORE_KEY,
              VCMetadata.fromVC(context.incomingVc?.vcMetadata),
            );
          },
          {to: context => context.serviceRefs.store},
        ),

        requestExistingVc: send(
          context =>
            StoreEvents.GET(VCMetadata.fromVC(context.incomingVc).getVcKey()),
          {to: context => context.serviceRefs.store},
        ),

        mergeIncomingVc: send(
          (context, event) => {
            const existing = event.response as VC;
            const updated: VC = {
              ...existing,
              reason: existing.reason.concat(context.incomingVc.reason),
            };
            return StoreEvents.SET(
              VCMetadata.fromVC(updated).getVcKey(),
              updated,
            );
          },
          {to: context => context.serviceRefs.store},
        ),

        storeVc: send(
          context =>
            StoreEvents.SET(
              VCMetadata.fromVC(context.incomingVc?.vcMetadata).getVcKey(),
              context.incomingVc,
            ),
          {to: context => context.serviceRefs.store},
        ),

        setReceiveLogTypeRegular: model.assign({
          receiveLogType: 'VC_RECEIVED',
        }),

        setReceiveLogTypeVerified: model.assign({
          receiveLogType: 'VC_RECEIVED_WITH_PRESENCE_VERIFIED',
        }),

        setReceiveLogTypeUnverified: model.assign({
          receiveLogType: 'VC_RECEIVED_BUT_PRESENCE_VERIFICATION_FAILED',
        }),

        setReceiveLogTypeDiscarded: model.assign({
          receiveLogType: 'VC_RECEIVED_NOT_SAVED',
        }),

        logReceived: send(
          context => {
            const vcMetadata = VCMetadata.fromVC(
              context.incomingVc?.vcMetadata,
            );
            return ActivityLogEvents.LOG_ACTIVITY({
              _vcKey: vcMetadata.getVcKey(),
              type: context.receiveLogType,
              timestamp: Date.now(),
              deviceName:
                context.senderInfo.name || context.senderInfo.deviceName,
              vcLabel: vcMetadata.id,
            });
          },
          {to: context => context.serviceRefs.activityLog},
        ),

        sendVcReceived: send(
          context => {
            return VcEvents.VC_RECEIVED(
              VCMetadata.fromVC(context.incomingVc?.vcMetadata),
            );
          },
          {to: context => context.serviceRefs.vc},
        ),

        clearShouldVerifyPresence: assign({
          incomingVc: context => ({
            ...context.incomingVc,
            shouldVerifyPresence: false,
          }),
        }),

        sendVCReceivingStartEvent: () => {
          sendStartEvent(
            getStartEventData(TelemetryConstants.FlowType.receiverVcShare),
          );
          sendImpressionEvent(
            getImpressionEventData(
              TelemetryConstants.FlowType.receiverVcShare,
              TelemetryConstants.Screens.sharingInProgressScreen,
            ),
          );
        },

        sendVCReceiveFlowTimeoutEndEvent: () => {
          sendEndEvent(
            getEndEventData(
              TelemetryConstants.FlowType.receiverVcShare,
              TelemetryConstants.EndEventStatus.failure,
              {comment: 'VC sharing timeout'},
            ),
          );
        },

        sendVCReceiveSuccessEvent: () => {
          sendImpressionEvent(
            getImpressionEventData(
              TelemetryConstants.FlowType.receiverVcShare,
              TelemetryConstants.Screens.vcReceivedSuccessPage,
            ),
          );
          sendEndEvent(
            getEndEventData(
              TelemetryConstants.FlowType.receiverVcShare,
              TelemetryConstants.EndEventStatus.success,
            ),
          );
        },

        sendVCReceiveFailedEvent: () => {
          sendEndEvent(
            getEndEventData(
              TelemetryConstants.FlowType.receiverVcShare,
              TelemetryConstants.EndEventStatus.failure,
            ),
          );
        },

        sendBLEConnectionErrorEvent: (_, event) => {
          sendErrorEvent(
            getErrorEventData(
              TelemetryConstants.FlowType.receiverVcShare,
              event.bleError.code,
              event.bleError.message,
            ),
          );
          sendEndEvent(
            getEndEventData(
              TelemetryConstants.FlowType.receiverVcShare,
              TelemetryConstants.EndEventStatus.failure,
            ),
          );
        },

        sendVCReceiveRejectedEvent: () => {
          sendEndEvent(
            getEndEventData(
              TelemetryConstants.FlowType.receiverVcShare,
              TelemetryConstants.EndEventStatus.failure,
              {comment: 'VC Rejected by the verifier'},
            ),
          );
        },

        sendVCReceivingTerminatedEvent: () => {
          sendEndEvent(
            getEndEventData(
              TelemetryConstants.FlowType.receiverVcShare,
              TelemetryConstants.EndEventStatus.failure,
              {comment: 'Verifier Disconnected'},
            ),
          );
        },

        sendVCReceivingDisconnectedEvent: () => {
          sendEndEvent(
            getEndEventData(
              TelemetryConstants.FlowType.receiverVcShare,
              TelemetryConstants.EndEventStatus.failure,
              {comment: 'VC Sharing cancelled by sender'},
            ),
          );
        },
      },

      services: {
        disconnect: () => () => {
          try {
            verifier.disconnect();
          } catch (e) {
            // pass
          }
        },

        checkBluetoothService: () => callback => {
          const subscription = BluetoothStateManager.onStateChange(state => {
            if (state === 'PoweredOn') {
              callback(model.events.BLUETOOTH_STATE_ENABLED());
            } else {
              callback(model.events.BLUETOOTH_STATE_DISABLED());
            }
          }, true);
          return () => subscription.remove();
        },

        requestBluetooth: () => callback => {
          BluetoothStateManager.requestToEnable()
            .then(() => callback(model.events.BLUETOOTH_STATE_ENABLED()))
            .catch(() => callback(model.events.BLUETOOTH_STATE_DISABLED()));
        },

        advertiseDevice: () => callback => {
          const openId4VpUri = verifier.startAdvertisement('OVPMOSIP');
          callback({type: 'ADV_STARTED', openId4VpUri});

          const statusCallback = (event: VerifierDataEvent) => {
            if (event.type === EventTypes.onSecureChannelEstablished) {
              callback({type: 'CONNECTED'});
            }
          };
          const subscription = subscribe(statusCallback);
          return () => subscription?.remove();
        },

        requestNearByDevicesPermission: () => callback => {
          requestMultiple([
            PERMISSIONS.ANDROID.BLUETOOTH_ADVERTISE,
            PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
          ])
            .then(response => {
              if (
                response[PERMISSIONS.ANDROID.BLUETOOTH_ADVERTISE] ===
                  RESULTS.GRANTED &&
                response[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] ===
                  RESULTS.GRANTED
              ) {
                callback(model.events.NEARBY_ENABLED());
              } else {
                callback(model.events.NEARBY_DISABLED());
              }
            })
            .catch(err => {
              callback(model.events.NEARBY_DISABLED());
            });
        },

        checkNearByDevicesPermission: () => callback => {
          if (isAndroid() && androidVersion >= 31) {
            const result = checkMultiple([
              PERMISSIONS.ANDROID.BLUETOOTH_ADVERTISE,
              PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
              PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
            ])
              .then(response => {
                if (
                  response[PERMISSIONS.ANDROID.BLUETOOTH_ADVERTISE] ===
                    RESULTS.GRANTED &&
                  response[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] ===
                    RESULTS.GRANTED
                ) {
                  callback(model.events.NEARBY_ENABLED());
                } else {
                  callback(model.events.NEARBY_DISABLED());
                }
              })
              .catch(err => {
                callback(model.events.NEARBY_DISABLED());
              });
          } else {
            callback(model.events.NEARBY_ENABLED());
          }
        },

        monitorConnection: () => callback => {
          const verifierErrorCodePrefix = 'TVV';
          const subscription = verifier.handleDataEvents(event => {
            if (event.type === EventTypes.onDisconnected) {
              callback({type: 'DISCONNECT'});
            }

            if (
              event.type === EventTypes.onError &&
              event.code.includes(verifierErrorCodePrefix)
            ) {
              callback({
                type: 'BLE_ERROR',
                bleError: {message: event.message, code: event.code},
              });
              console.log('BLE Exception: ' + event.message);
            }
          });

          return () => subscription.remove();
        },

        receiveVc: () => callback => {
          const statusCallback = (event: VerifierDataEvent) => {
            if (event.type === EventTypes.onDataReceived) {
              callback({type: 'VC_RECEIVED', vc: JSON.parse(event.data)});
            }
          };
          const subscription = subscribe(statusCallback);

          return () => subscription.remove();
        },

        sendVcResponse: (context, _event, meta) => async () => {
          verifier.sendVerificationStatus(meta.data.status);
        },

        verifyVp: context => async () => {
          const vp = context.incomingVc.verifiablePresentation;

          // TODO
          // const challenge = ?
          // await verifyPresentation(vp, challenge);

          const vc: VC = {
            ...context.incomingVc,
            verifiablePresentation: null,
            verifiableCredential: vp.verifiableCredential[0],
          };

          return Promise.resolve(vc);
        },

        checkStorageAvailability: () => async () => {
          return Promise.resolve(
            Storage.isMinimumLimitReached('minStorageRequired'),
          );
        },
      },

      guards: {
        hasExistingVc: (context, event) => {
          const receivedVcs = event.vcMetadatas;
          const incomingVcMetadata = VCMetadata.fromVC(context.incomingVc);
          return receivedVcs?.some(
            vcMetadata =>
              vcMetadata.getVcKey() == incomingVcMetadata.getVcKey(),
          );
        },

        isMinimumStorageLimitReached: (_context, event) => Boolean(event.data),
      },

      delays: {
        DESTROY_TIMEOUT: 500,
        SHARING_TIMEOUT: 15 * 1000,
      },
    },
  );

type State = StateFrom<typeof requestMachine>;

export function createRequestMachine(serviceRefs: AppServices) {
  return requestMachine.withContext({
    ...requestMachine.context,
    serviceRefs,
  });
}

export function selectIsMinimumStorageLimitReached(state: State) {
  return state.matches('storageLimitReached');
}
