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

struct MethodChannelData {
	let resolver: RCTPromiseResolveBlock
	let rejecter: RCTPromiseRejectBlock
}

final class MethodChannelHandler: @unchecked Sendable {
	enum Constants: String {
		case errorCode = "NMA_SDK"
	}

	// MARK: Properties

	static let shared = MethodChannelHandler()
	private var channelData: [ReactMethod: MethodChannelData] = [:]
	private let queue = DispatchQueue(withLabel: "ch.nevis.mobile.authentication.sdk.react.method-channel")

	// MARK: Initialization

	private init() { /* . */ }

	// MARK: Public Interface

	func add(method: ReactMethod, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
		queue.sync {
			channelData[method] = MethodChannelData(resolver: resolve, rejecter: reject)
		}
	}

	func resolve(method: ReactMethod, message: ChannelOutMessage? = nil) {
		queue.sync {
			do {
				if let data = channelData.removeValue(forKey: method) {
					if let message {
						let arguments = try JsonMapper.encode(message)
						data.resolver(arguments)
					}
					else {
						data.resolver(nil)
					}
				}
			}
			catch {
				if let message {
					Task {
						await OperationCache.shared.delete(by: message.operationId)
					}
				}
				print(error.localizedDescription)
			}
		}
	}

	func reject(method: ReactMethod, message: ChannelOutMessage) {
		queue.sync {
			do {
				if let data = channelData.removeValue(forKey: method) {
					let arguments = try JsonMapper.encode(message)
					let error = NSError(domain: Constants.errorCode.rawValue, code: 0, userInfo: arguments)
					data.rejecter(Constants.errorCode.rawValue, message.operationId, error)
				}
			}
			catch {
				print(error.localizedDescription)
			}

			Task {
				await OperationCache.shared.delete(by: message.operationId)
			}
		}
	}
}
