//
//  Body.swift
//  Pachyderm
//
//  Created by Shadowfacts on 9/8/18.
//  Copyright © 2018 Shadowfacts. All rights reserved.
//

import Foundation

protocol Body: Sendable {
    var mimeType: String? { get }
    var data: Data? { get }
}

struct EmptyBody: Body {
    var mimeType: String? { nil }
    var data: Data? { nil }
}

struct ParametersBody: Body {
    let parameters: [Parameter]?
    
    init(_ parmaeters: [Parameter]?) {
        self.parameters = parmaeters
    }
    
    var mimeType: String? {
        if parameters == nil || parameters!.isEmpty {
            return nil
        }
        return "application/x-www-form-urlencoded; charset=utf-8"
    }
    
    var data: Data? {
        return parameters?.urlEncoded.data(using: .utf8)
    }
}

struct FormDataBody: Body {
    private static let boundary = "PachydermBoundary"
    
    let parameters: [Parameter]?
    let attachment: FormAttachment?
    
    init(_ parameters: [Parameter]?, _ attachment: FormAttachment?) {
        self.parameters = parameters
        self.attachment = attachment
    }
    
    var mimeType: String? {
        if parameters == nil && attachment == nil {
            return nil
        }
        return "multipart/form-data; boundary=\(FormDataBody.boundary)"
    }
    
    var data: Data? {
        var data = Data()
        parameters?.forEach { param in
            guard let value = param.value else { return }
            data.append("--\(FormDataBody.boundary)\r\n")
            data.append("Content-Disposition: form-data; name=\"\(param.name)\"\r\n\r\n")
            data.append("\(value)\r\n")
        }
        if let attachment = attachment {
            data.append("--\(FormDataBody.boundary)\r\n")
            data.append("Content-Disposition: form-data; name=\"file\"; filename=\"\(attachment.fileName)\"\r\n")
            data.append("Content-Type: \(attachment.mimeType)\r\n\r\n")
            data.append(attachment.data)
            data.append("\r\n")
        }

        data.append("--\(FormDataBody.boundary)--\r\n")
        return data
    }
}

struct JsonBody<T: Encodable & Sendable>: Body {
    let value: T
    
    init(_ value: T) {
        self.value = value
    }
    
    var mimeType: String? { "application/json" }
    
    var data: Data? { try? Client.encoder.encode(value) }
}
