// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.

import Foundation

public protocol BatchRequestChildRetrievable {
    var requests: [Network.BatchRequest.Child] { get }
}

public extension Network {
    struct BatchRequest: Encodable, BatchRequestChildRetrievable {
        /// The servers currently have a limit for the number of requests a `BatchRequest` can have, when using this we should avoid
        /// trying to make calls that exceed this limit as they will fail
        public static let childRequestLimit: Int = 20
        
        public enum CodingKeys: String, CodingKey {
            // Storage Server keys
            case requests
        }
        
        let requestsKey: CodingKeys?
        public let requests: [Child]
        
        public init(requestsKey: CodingKeys? = nil, requests: [any ErasedPreparedRequest]) {
            self.requestsKey = requestsKey
            self.requests = requests.map { Child(request: $0) }
            
            if requests.count > BatchRequest.childRequestLimit {
                Log.warn("[BatchRequest] Constructed request with \(requests.count) subrequests when the limit is \(BatchRequest.childRequestLimit)")
            }
        }
        
        // MARK: - Encodable
        
        public func encode(to encoder: Encoder) throws {
            switch requestsKey {
                case .requests:
                    var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)

                    try container.encode(requests, forKey: .requests)
                    
                case .none:
                    var container: SingleValueEncodingContainer = encoder.singleValueContainer()

                    try container.encode(requests)
            }
        }
        
        // MARK: - BatchRequest.Child
        
        public struct Child: Encodable {
            public enum Variant {
                case unsupported
                case sogs
                case storageServer
            }
            
            enum CodingKeys: String, CodingKey {
                case method
                
                // SOGS keys
                case path
                case headers
                case json
                case b64
                case bytes
                
                // Storage Server keys
                case params
            }
            
            let request: any ErasedPreparedRequest
            
            public func encode(to encoder: Encoder) throws {
                try request.encodeForBatchRequest(to: encoder)
            }
        }
    }
}
