//
// Copyright © 2023 Nevis Security AG. All rights reserved.
//

import NevisMobileAuthentication

typealias OOBRegistrationHandler = @Sendable (OutOfBandRegistration) -> ()
typealias OOBAuthenticationHandler = @Sendable (OutOfBandAuthentication) -> ()

struct OutOfBandOperationMethodHandler: MethodHandler {
	func execute(using client: MobileAuthenticationClient, method: ReactMethod, with message: ChannelInMessage) async {
		let message: OutOfBandOperationMessage = validate(message: message)
		let operation = OutOfBandOperation(operationId: message.operationId, method: method)
		await OperationCache.shared.put(operation, using: message.operationId)

		client.operations.outOfBandOperation
			.configure(
				by: message,
				operation: operation,
				onRegistration: register(
					using: message.operationId,
					subOperationId: message.subOperationId,
					method: method
				),
				onAuthentication: authenticate(
					using: message.operationId,
					subOperationId: message.subOperationId,
					method: method
				)
			)
			.execute()
	}
}

private extension OutOfBandOperationMethodHandler {
	func register(using operationId: String, subOperationId: String, method: ReactMethod) -> OOBRegistrationHandler {
		{ registration in
			Task {
				await OperationCache.shared.delete(by: operationId)

				let operation = OutOfBandRegistrationOperation(
					operationId: subOperationId,
					method: ReactMethod(name: .oobRegister),
					registration: registration
				)
				await OperationCache.shared.put(operation, using: subOperationId)

				let message = OperationTypeMessage(operationId: operationId, operationType: .registration)
				MethodChannelHandler.shared.resolve(method: method, message: message)
			}
		}
	}

	func authenticate(using operationId: String, subOperationId: String, method: ReactMethod) -> OOBAuthenticationHandler {
		{ authentication in
			Task {
				await OperationCache.shared.delete(by: operationId)

				let operation = OutOfBandAuthenticationOperation(
					operationId: subOperationId,
					method: ReactMethod(name: .oobAuthenticate),
					authentication: authentication
				)
				await OperationCache.shared.put(operation, using: subOperationId)

				let message = OperationTypeMessage(operationId: operationId, operationType: .authentication)
				MethodChannelHandler.shared.resolve(method: method, message: message)
			}
		}
	}
}
