//
// Generated by SwagGen
// https://github.com/yonaskolb/SwagGen
//

import Foundation

extension IBPortalApi.Contract {

    /**
    Search by symbol or name

    Specify an underlying to search what derivative contract(s) it has. This endpoint must be called before using /secdef/info
    */
    public enum PostIserverSecdefSearch {

        public static let service = APIService<Response>(id: "postIserverSecdefSearch", tag: "Contract", method: "POST", path: "/iserver/secdef/search", hasBody: true, securityRequirements: [])

        public final class Request: APIRequest<Response> {

            /** Specify an underlying to search what derivative contract(s) it has. This endpoint must be called before using /secdef/info */
            public struct Body: APIModel {

                /** symbol or name to be searched */
                public let symbol: String

                /** should be true if the search is to be performed by name. false by default. */
                public let name: Bool?

                /** If search is done by name, only the assets provided in this field will be returned. Currently, only STK is supported. */
                public let secType: String?

                public init(symbol: String, name: Bool? = nil, secType: String? = nil) {
                    self.symbol = symbol
                    self.name = name
                    self.secType = secType
                }

                public init(from decoder: Decoder) throws {
                    let container = try decoder.container(keyedBy: StringCodingKey.self)

                    symbol = try container.decode("symbol")
                    name = try container.decodeIfPresent("name")
                    secType = try container.decodeIfPresent("secType")
                }

                public func encode(to encoder: Encoder) throws {
                    var container = encoder.container(keyedBy: StringCodingKey.self)

                    try container.encode(symbol, forKey: "symbol")
                    try container.encodeIfPresent(name, forKey: "name")
                    try container.encodeIfPresent(secType, forKey: "secType")
                }

            }

            public struct Options {

                /** symbol or name to be searched */
                public var symbol: String

                /** should be true if the search is to be performed by name. false by default. */
                public var name: Bool?

                /** If search is done by name, only the assets provided in this field will be returned. Currently, only STK is supported. */
                public var secType: String?

                public init(symbol: String, name: Bool? = nil, secType: String? = nil) {
                    self.symbol = symbol
                    self.name = name
                    self.secType = secType
                }
            }

            public var options: Options

            public var body: Body

            public init(body: Body, options: Options, encoder: RequestEncoder? = nil) {
                self.body = body
                self.options = options
                super.init(service: PostIserverSecdefSearch.service) { defaultEncoder in
                    return try (encoder ?? defaultEncoder).encode(body)
                }
            }

            /// convenience initialiser so an Option doesn't have to be created
            public convenience init(symbol: String, name: Bool? = nil, secType: String? = nil, body: Body) {
                let options = Options(symbol: symbol, name: name, secType: secType)
                self.init(body: body, options: options)
            }

            public override var formParameters: [String: Any] {
                var params: [String: Any] = [:]
                params["symbol"] = options.symbol
                if let name = options.name {
                  params["name"] = name
                }
                if let secType = options.secType {
                  params["secType"] = secType
                }
                return params
            }
        }

        public enum Response: APIResponseValue, CustomStringConvertible, CustomDebugStringConvertible {

            /** Specify an underlying to search what derivative contract(s) it has. This endpoint must be called before using /secdef/info */
            public struct Status200: APIModel {

                /** Company Name - Exchange */
                public let companyHeader: String?

                public let companyName: String?

                /** Contract Identifier */
                public let conid: Int?

                /** Exchange */
                public let description: String?

                public let fop: String?

                public let opt: String?

                public let restricted: String?

                public let sections: [Sections]?

                public let symbol: String?

                public let war: String?

                /** Specify an underlying to search what derivative contract(s) it has. This endpoint must be called before using /secdef/info */
                public struct Sections: APIModel {

                    /** Listing Exchange */
                    public let exchange: String?

                    /** For combo's defines the asset class for each leg */
                    public let legSecType: String?

                    /** List of expiration month(s) and year(s) in MMMYY format separated by semicolon */
                    public let months: String?

                    /** Asset Class */
                    public let secType: String?

                    public let symbol: String?

                    public init(exchange: String? = nil, legSecType: String? = nil, months: String? = nil, secType: String? = nil, symbol: String? = nil) {
                        self.exchange = exchange
                        self.legSecType = legSecType
                        self.months = months
                        self.secType = secType
                        self.symbol = symbol
                    }

                    public init(from decoder: Decoder) throws {
                        let container = try decoder.container(keyedBy: StringCodingKey.self)

                        exchange = try container.decodeIfPresent("exchange")
                        legSecType = try container.decodeIfPresent("legSecType")
                        months = try container.decodeIfPresent("months")
                        secType = try container.decodeIfPresent("secType")
                        symbol = try container.decodeIfPresent("symbol")
                    }

                    public func encode(to encoder: Encoder) throws {
                        var container = encoder.container(keyedBy: StringCodingKey.self)

                        try container.encodeIfPresent(exchange, forKey: "exchange")
                        try container.encodeIfPresent(legSecType, forKey: "legSecType")
                        try container.encodeIfPresent(months, forKey: "months")
                        try container.encodeIfPresent(secType, forKey: "secType")
                        try container.encodeIfPresent(symbol, forKey: "symbol")
                    }

                }

                public init(companyHeader: String? = nil, companyName: String? = nil, conid: Int? = nil, description: String? = nil, fop: String? = nil, opt: String? = nil, restricted: String? = nil, sections: [Sections]? = nil, symbol: String? = nil, war: String? = nil) {
                    self.companyHeader = companyHeader
                    self.companyName = companyName
                    self.conid = conid
                    self.description = description
                    self.fop = fop
                    self.opt = opt
                    self.restricted = restricted
                    self.sections = sections
                    self.symbol = symbol
                    self.war = war
                }

                public init(from decoder: Decoder) throws {
                    let container = try decoder.container(keyedBy: StringCodingKey.self)

                    companyHeader = try container.decodeIfPresent("companyHeader")
                    companyName = try container.decodeIfPresent("companyName")
                    conid = try container.decodeIfPresent("conid")
                    description = try container.decodeIfPresent("description")
                    fop = try container.decodeIfPresent("fop")
                    opt = try container.decodeIfPresent("opt")
                    restricted = try container.decodeIfPresent("restricted")
                    sections = try container.decodeArrayIfPresent("sections")
                    symbol = try container.decodeIfPresent("symbol")
                    war = try container.decodeIfPresent("war")
                }

                public func encode(to encoder: Encoder) throws {
                    var container = encoder.container(keyedBy: StringCodingKey.self)

                    try container.encodeIfPresent(companyHeader, forKey: "companyHeader")
                    try container.encodeIfPresent(companyName, forKey: "companyName")
                    try container.encodeIfPresent(conid, forKey: "conid")
                    try container.encodeIfPresent(description, forKey: "description")
                    try container.encodeIfPresent(fop, forKey: "fop")
                    try container.encodeIfPresent(opt, forKey: "opt")
                    try container.encodeIfPresent(restricted, forKey: "restricted")
                    try container.encodeIfPresent(sections, forKey: "sections")
                    try container.encodeIfPresent(symbol, forKey: "symbol")
                    try container.encodeIfPresent(war, forKey: "war")
                }

            }

            /** Specify an underlying to search what derivative contract(s) it has. This endpoint must be called before using /secdef/info */
            public struct Status500: APIModel {

                public let error: String?

                public init(error: String? = nil) {
                    self.error = error
                }

                public init(from decoder: Decoder) throws {
                    let container = try decoder.container(keyedBy: StringCodingKey.self)

                    error = try container.decodeIfPresent("error")
                }

                public func encode(to encoder: Encoder) throws {
                    var container = encoder.container(keyedBy: StringCodingKey.self)

                    try container.encodeIfPresent(error, forKey: "error")
                }

            }
            public typealias SuccessType = [Status200]

            /** returns an array of results */
            case status200([Status200])

            /** error while processing the request */
            case status500(Status500)

            public var success: [Status200]? {
                switch self {
                case .status200(let response): return response
                default: return nil
                }
            }

            public var failure: Status500? {
                switch self {
                case .status500(let response): return response
                default: return nil
                }
            }

            /// either success or failure value. Success is anything in the 200..<300 status code range
            public var responseResult: APIResponseResult<[Status200], Status500> {
                if let successValue = success {
                    return .success(successValue)
                } else if let failureValue = failure {
                    return .failure(failureValue)
                } else {
                    fatalError("Response does not have success or failure response")
                }
            }

            public var response: Any {
                switch self {
                case .status200(let response): return response
                case .status500(let response): return response
                }
            }

            public var statusCode: Int {
                switch self {
                case .status200: return 200
                case .status500: return 500
                }
            }

            public var successful: Bool {
                switch self {
                case .status200: return true
                case .status500: return false
                }
            }

            public init(statusCode: Int, data: Data, decoder: ResponseDecoder) throws {
                switch statusCode {
                case 200: self = try .status200(decoder.decode([Status200].self, from: data))
                case 500: self = try .status500(decoder.decode(Status500.self, from: data))
                default: throw APIClientError.unexpectedStatusCode(statusCode: statusCode, data: data)
                }
            }

            public var description: String {
                return "\(statusCode) \(successful ? "success" : "failure")"
            }

            public var debugDescription: String {
                var string = description
                let responseString = "\(response)"
                if responseString != "()" {
                    string += "\n\(responseString)"
                }
                return string
            }
        }
    }
}
