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

import NevisMobileAuthentication

struct TypedAuthorizationProvider: Codable {
	// MARK: Properties

	let wrapped: AuthorizationProvider

	// MARK: Initialization

	init(wrapped: AuthorizationProvider) {
		self.wrapped = wrapped
	}

	// MARK: Coding Keys

	enum AuthorizationProviderType: String, Encodable {
		case cookie = "CookieAuthorizationProvider"
		case jws = "JwsAuthorizationProvider"
		case jwt = "JwtAuthorizationProvider"
	}

	enum CodingKeys: String, CodingKey {
		case cookies
		case jwt
	}

	// 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 AuthorizationProviderType.cookie.rawValue:
				self.wrapped = try container.decode(CookieAuthorizationProvider.self, forKey: .data)
			case AuthorizationProviderType.jws.rawValue:
				self.wrapped = try container.decode(JwsAuthorizationProvider.self, forKey: .data)
			case AuthorizationProviderType.jwt.rawValue:
				self.wrapped = try container.decode(JwtAuthorizationProvider.self, forKey: .data)
			default:
				throw DecodingError.dataCorrupted(
					DecodingError.Context(
						codingPath: container.codingPath,
						debugDescription: "Failed to decode authorization provider!"
					)
				)
		}
	}

	// MARK: Encodable

	func encode(to encoder: Encoder) throws {
		var container = encoder.container(keyedBy: TypedCodingKeys.self)
		switch wrapped {
			case is CookieAuthorizationProvider:
				try container.encode(AuthorizationProviderType.cookie, forKey: .type)
			case is JwsAuthorizationProvider:
				try container.encode(AuthorizationProviderType.jws, forKey: .type)
			case is JwtAuthorizationProvider:
				try container.encode(AuthorizationProviderType.jwt, forKey: .type)
			default:
				throw EncodingError.invalidValue(
					wrapped,
					EncodingError.Context(
						codingPath: container.codingPath,
						debugDescription: "Failed to encode authorization provider!"
					)
				)
		}

		try wrapped.encode(to: container.superEncoder(forKey: .data))
	}
}
