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

import NevisMobileAuthentication

struct TypedRetryPolicy: Decodable {
	// MARK: Properties

	let wrapped: RetryPolicy

	// MARK: Decodable

	init(from decoder: Decoder) throws {
		let container = try decoder.container(keyedBy: TypedCodingKeys.self)
		let type = try container.decode(String.self, forKey: .type)
		switch type {
			case RetryPolicy.CodingKeys.Case.noRetry.stringValue:
				self.wrapped = .noRetry
			case RetryPolicy.CodingKeys.Case.constant.stringValue:
				let dataContainer = try container.nestedContainer(
					keyedBy: RetryPolicy.CodingKeys.Constant.self,
					forKey: .data
				)
				let maxRetries = try dataContainer.decode(Int.self, forKey: .maxRetries)
				let delayInSeconds = try dataContainer.decode(TimeInterval.self, forKey: .delayInSeconds)
				self.wrapped = .constant(
					maxRetries: maxRetries,
					delayInSeconds: delayInSeconds
				)
			case RetryPolicy.CodingKeys.Case.exponential.stringValue:
				let dataContainer = try container.nestedContainer(
					keyedBy: RetryPolicy.CodingKeys.Exponential.self,
					forKey: .data
				)
				let maxRetries = try dataContainer.decode(Int.self, forKey: .maxRetries)
				let initialDelayInSeconds = try dataContainer.decode(TimeInterval.self, forKey: .initialDelayInSeconds)
				let multiplier = try dataContainer.decode(Double.self, forKey: .multiplier)
				let maximumDelayInSeconds = try dataContainer.decode(TimeInterval.self, forKey: .maximumDelayInSeconds)
				self.wrapped = .exponential(
					maxRetries: maxRetries,
					initialDelayInSeconds: initialDelayInSeconds,
					multiplier: multiplier,
					maximumDelayInSeconds: maximumDelayInSeconds
				)
			default:
				throw DecodingError.dataCorrupted(
					DecodingError.Context(
						codingPath: container.codingPath,
						debugDescription: "Failed to decode retry policy!"
					)
				)
		}
	}
}

extension RetryPolicy {
	enum CodingKeys {
		enum Case: CodingKey {
			case noRetry
			case constant
			case exponential
		}

		enum Constant: CodingKey {
			case maxRetries
			case delayInSeconds
		}

		enum Exponential: CodingKey {
			case maxRetries
			case initialDelayInSeconds
			case multiplier
			case maximumDelayInSeconds
		}
	}

	var caseName: String {
		Mirror(reflecting: self).children.first?.label ?? String(describing: self)
	}
}
