import Foundation

// Calls your application's backend using your signed-in customer's token.
// Keep this instance at application/coordinator scope while checkout is active.
final class CheckoutAPI {
    let origin: URL
    let customerToken: () -> String

    init(origin: URL, customerToken: @escaping () -> String) {
        precondition(origin.scheme == "https")
        self.origin = origin
        self.customerToken = customerToken
    }

    func request(path: String, body: [String: Any]? = nil,
                 completion: @escaping (Result<[String: Any], Error>) -> Void) {
        precondition(path.hasPrefix("/api/"))
        let url = URL(string: path, relativeTo: origin)!.absoluteURL
        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 15)
        request.httpMethod = body == nil ? "GET" : "POST"
        request.setValue("Bearer \(customerToken())", forHTTPHeaderField: "Authorization")
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        do {
            if let body = body {
                request.setValue("application/json", forHTTPHeaderField: "Content-Type")
                request.httpBody = try JSONSerialization.data(withJSONObject: body)
            }
        } catch {
            DispatchQueue.main.async { completion(.failure(error)) }
            return
        }
        URLSession.shared.dataTask(with: request) { data, response, error in
            let result = Result<[String: Any], Error> {
                if let error = error { throw error }
                guard let response = response as? HTTPURLResponse,
                      (200...299).contains(response.statusCode), let data = data,
                      let object = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
                    throw NSError(domain: "CheckoutAPI", code: 1,
                                  userInfo: [NSLocalizedDescriptionKey: "Cannot read the order result. Keep the original order."])
                }
                return object
            }
            DispatchQueue.main.async { completion(result) }
        }.resume()
    }
}
