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

import NevisMobileAuthentication

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

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

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

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

			let operation = OutOfBandRegistrationOperation(operationId: subOperationId,
			                                               registration: registration)
			OperationCache.shared.put(operation, using: subOperationId)

			self.selectOperation(using: operationId, type: .registration)

			let resultMessage = OnSuccessMessage(operationId: operationId)
			MethodChannelHandler.shared.resolve(method: .oobOperation, message: resultMessage)
		}
	}

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

			let operation = OutOfBandAuthenticationOperation(operationId: subOperationId,
			                                                 authentication: authentication)
			OperationCache.shared.put(operation, using: subOperationId)

			self.selectOperation(using: operationId, type: .authentication)

			let resultMessage = OnSuccessMessage(operationId: operationId)
			MethodChannelHandler.shared.resolve(method: .oobOperation, message: resultMessage)
		}
	}

	func selectOperation(using operationId: String, type: OperationType) {
		let message = OperationTypeMessage(operationId: operationId,
		                                   operationType: type)
		EventEmitter.shared.dispatch(event: .operationType, message: message)
	}
}
