{{#operations}}//
// {{classname}}.swift
//
// Generated by openapi-generator
// https://openapi-generator.tech

import Foundation
import Combine
import OpenAPITransport

{{#description}}
/// {{{.}}} {{/description}}
open class {{classname}} {
    private let transport: OpenAPITransport
    public var encoder: JSONEncoder = {
        let encoder = JSONEncoder()
        encoder.dateEncodingStrategy = .formatted(OpenISO8601DateFormatter())
        return encoder
    }()
    public var decoder: JSONDecoder = {
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .formatted(OpenISO8601DateFormatter())
        return decoder
    }()
    public var baseURL = URL(string: "{{{basePath}}}")

    public init(_ transport: OpenAPITransport) {
        self.transport = transport
    }
{{#operation}}

    {{#allParams}}
    {{#isEnum}}
    ///
    /// Enum for parameter {{paramName}}
    ///
    public enum {{{vendorExtensions.x-swift-nested-enum-type}}}: {{{baseType}}}, Codable, CaseIterable {
        {{#allowableValues}}
        {{#enumVars}}
        case {{name}} = {{{value}}}
        {{/enumVars}}
        {{/allowableValues}}
    }
    {{/isEnum}}
    {{/allParams}}
    {{#vendorExtensions.x-swift-custom-error-type}}
    public enum {{{vendorExtensions.x-swift-custom-error-type}}}: Error, CustomStringConvertible {
        {{#responses}}
        {{#vendorExtensions.x-swift-has-custom-error-type}}
        // {{{message}}}
        case code{{{code}}}Error{{#dataType}}({{{dataType}}}){{/dataType}}
        {{/vendorExtensions.x-swift-has-custom-error-type}}
        {{/responses}}

        public var description: String {
            switch self {
            {{#responses}}
            {{#vendorExtensions.x-swift-has-custom-error-type}}
            case .code{{{code}}}Error{{#dataType}}(let object){{/dataType}}:
                return "{{{vendorExtensions.x-swift-custom-error-type}}}: {{{message}}}{{#dataType}}: \(object){{/dataType}}"
            {{/vendorExtensions.x-swift-has-custom-error-type}}
            {{/responses}}
            }
        }
    }
    {{/vendorExtensions.x-swift-custom-error-type}}

    {{#summary}}
    /// {{{.}}}
    {{/summary}}
    /// - {{httpMethod}} {{{path}}}{{#notes}}
    /// - {{{.}}}{{/notes}}{{#subresourceOperation}}
    /// - subresourceOperation: {{.}}{{/subresourceOperation}}{{#defaultResponse}}
    /// - defaultResponse: {{.}}{{/defaultResponse}}
    {{#authMethods}}
    /// - {{#isBasicBasic}}BASIC{{/isBasicBasic}}{{#isOAuth}}OAuth{{/isOAuth}}{{#isApiKey}}API Key{{/isApiKey}}:
    /// - type: {{type}}{{#keyParamName}} {{keyParamName}} {{#isKeyInQuery}}(QUERY){{/isKeyInQuery}}{{#isKeyInHeaer}}(HEADER){{/isKeyInHeaer}}{{/keyParamName}}
    /// - name: {{name}}
    {{/authMethods}}
    {{#hasResponseHeaders}}
    /// - responseHeaders: [{{#responseHeaders}}{{{baseName}}}({{{dataType}}}){{^-last}}, {{/-last}}{{/responseHeaders}}]
    {{/hasResponseHeaders}}
    {{#externalDocs}}
    /// - externalDocs:
    {{#url}}
    ///     url: {{.}} 
    {{/url}}
    {{#description}}
    ///     description: {{.}} 
    {{/description}}
    {{/externalDocs}}
    {{#allParams}}
    /// - parameter {{paramName}}: ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}
    {{/allParams}}
    /// - returns: AnyPublisher<{{{returnType}}}{{^returnType}}Void{{/returnType}}, Error> {{description}}
    {{#isDeprecated}}
    @available(*, deprecated, message: "Deprecated API operation")
    {{/isDeprecated}}
    open func {{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}{{^required}}? = nil{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) -> AnyPublisher<{{{returnType}}}{{^returnType}}Void{{/returnType}}, Error> {
        Deferred {
            Result<URLRequest, Error> {
                guard let baseURL = self.transport.baseURL ?? self.baseURL else {
                    throw OpenAPITransportError.badURLError()
                }
                {{#pathParams}}{{#-last}}var{{/-last}}{{/pathParams}}{{^pathParams}}let{{/pathParams}} path = "{{path}}"
                {{#pathParams}}
                {{#required}}path = path.replacingOccurrences(of: "{{=<% %>=}}{<%baseName%>}<%={{ }}=%>", with: {{> toString}}){{/required}}{{^required}}if let {{paramName}} = {{paramName}} { path = path.replacingOccurrences(of: "{{=<% %>=}}{<%baseName%>}<%={{ }}=%>", with: {{> toString}}) } {{/required}}
                {{/pathParams}}
                let url = baseURL.appendingPathComponent(path)
                {{#hasQueryParams}}var{{/hasQueryParams}}{{^hasQueryParams}}let{{/hasQueryParams}} components = URLComponents(url: url, resolvingAgainstBaseURL: false)
                {{#hasQueryParams}}
                var queryItems: [URLQueryItem] = []
                {{#queryParams}}
                {{#required}}queryItems.append(URLQueryItem(name: "{{baseName}}", value: {{> toString}})){{/required}}{{^required}}if let {{paramName}} = {{paramName}} { queryItems.append(URLQueryItem(name: "{{baseName}}", value: {{> toString}})) } {{/required}}
                {{/queryParams}}
                components?.queryItems = queryItems
                {{/hasQueryParams}}
                guard let requestURL = components?.url else {
                    throw OpenAPITransportError.badURLError()
                }
                var request = URLRequest(url: requestURL)
                request.httpMethod = "{{httpMethod}}"
                {{! Begin headers }}
                {{#headerParams}}
                {{#-last}}
                var headers = [String: String]()
                {{/-last}}
                {{/headerParams}}
                {{#headerParams}}
                {{#required}}headers["{{baseName}}"] = {{> toString}}{{/required}}{{^required}}if let {{paramName}} = {{paramName}} { headers["{{baseName}}"] = {{> toString}} }{{/required}}
                {{/headerParams}}
                {{#headerParams}}
                {{#-last}}
                request.allHTTPHeaderFields = headers
                {{/-last}}
                {{/headerParams}}
                {{! Begin body }}
                {{#hasBodyParam}}
                {{#bodyParam}}
                request.httpBody = try self.encoder.encode({{paramName}})
                request.setValue("application/json", forHTTPHeaderField: "Content-Type")
                {{/bodyParam}}
                {{/hasBodyParam}}
                {{! Begin multipart form }}
                {{#hasFormParams}}
                {{#isMultipart}}
                let multipartBoundary = String(format: "Boundary+%08X%08X", arc4random(), arc4random())
                var multipartData = Data()
                {{#formParams}}
{{> toMultipartFormDataAppend}}
                {{/formParams}}
                multipartData.append("\r\n--\(multipartBoundary)--\r\n".data(using: .utf8) ?? Data())
                request.httpBody = multipartData
                request.setValue("\(multipartData.count)", forHTTPHeaderField: "Content-Length")
                request.setValue("multipart/form-data; boundary=\(multipartBoundary)", forHTTPHeaderField: "Content-Type")
                {{/isMultipart}}
                {{^isMultipart}}
                {{! Begin form urlencoded }}
                var formEncodedItems: [String] = []
                {{#formParams}}
                {{#required}}formEncodedItems.append("{{baseName}}=\({{> toString}})"){{/required}}{{^required}}if let {{paramName}} = {{paramName}} { formEncodedItems.append("{{baseName}}=\({{> toString}})") } {{/required}}
                {{/formParams}}
                request.httpBody = formEncodedItems.joined(separator: "&").data(using: .utf8)
                request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
                {{/isMultipart}}
                {{/hasFormParams}}
                return request
            }.publisher
        }.flatMap { request -> AnyPublisher<{{{returnType}}}{{^returnType}}Void{{/returnType}}, Error> in 
            return self.transport.send(request: request)
            {{#vendorExtensions.x-swift-custom-error-type}}
                .mapError { transportError -> Error in 
                    {{#responses}}
                    {{#vendorExtensions.x-swift-has-custom-error-type}}
                    if transportError.statusCode == {{{code}}} {
                        {{#dataType}}
                        do {
                            let error = try self.decoder.decode({{{dataType}}}.self, from: transportError.data)
                            return {{{vendorExtensions.x-swift-custom-error-type}}}.code{{{code}}}Error(error)
                        } catch {
                            return error
                        }
                        {{/dataType}}
                        {{^dataType}}
                        return {{{vendorExtensions.x-swift-custom-error-type}}}.code{{{code}}}Error
                        {{/dataType}}
                    }
                    {{/vendorExtensions.x-swift-has-custom-error-type}}
                    {{/responses}}
                    return transportError
                }
            {{/vendorExtensions.x-swift-custom-error-type}}
                .tryMap { response in
                    {{#returnType}}
                    {{#vendorExtensions.x-swift-is-not-codable}}
                    if let object = try JSONSerialization.jsonObject(with: response.data, options: []) as? {{{returnType}}} {
                        return object
                    } else {
                        throw OpenAPITransportError.invalidResponseMappingError(data: response.data)
                    }
                    {{/vendorExtensions.x-swift-is-not-codable}} 
                    {{^vendorExtensions.x-swift-is-not-codable}} 
                    try self.decoder.decode({{{returnType}}}.self, from: response.data)
                    {{/vendorExtensions.x-swift-is-not-codable}} 
                    {{/returnType}}
                    {{^returnType}}
                    return ()
                    {{/returnType}}
                }
                .eraseToAnyPublisher()
        }.eraseToAnyPublisher()
    }
{{/operation}}
}
{{/operations}}