{{>partial_header}}

use reqwest;
use serde::{Deserialize, Serialize};
use crate::{apis::ResponseContent, models};
use super::{Error, configuration};

{{#operations}}
{{#operation}}
{{#vendorExtensions.x-group-parameters}}
{{#allParams}}
{{#-first}}
/// struct for passing parameters to the method [`{{operationId}}`]
#[derive(Clone, Debug, Default)]
pub struct {{{operationIdCamelCase}}}Params {
{{/-first}}
    {{#description}}
    /// {{{.}}}
    {{/description}}
    pub {{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{^isUuid}}{{#isString}}{{#isArray}}Vec<{{/isArray}}String{{#isArray}}>{{/isArray}}{{/isString}}{{/isUuid}}{{#isUuid}}{{#isArray}}Vec<{{/isArray}}String{{#isArray}}>{{/isArray}}{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}},{{/-last}}
{{#-last}}
}

{{/-last}}
{{/allParams}}
{{/vendorExtensions.x-group-parameters}}
{{/operation}}
{{/operations}}

{{#supportMultipleResponses}}
{{#operations}}
{{#operation}}
/// struct for typed successes of method [`{{operationId}}`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum {{{operationIdCamelCase}}}Success {
    {{#responses}}
    {{#is2xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is2xx}}
    {{#is3xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is3xx}}
    {{/responses}}
    UnknownValue(serde_json::Value),
}

{{/operation}}
{{/operations}}
{{/supportMultipleResponses}}
{{#operations}}
{{#operation}}
/// struct for typed errors of method [`{{operationId}}`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum {{{operationIdCamelCase}}}Error {
    {{#responses}}
    {{#is4xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is4xx}}
    {{#is5xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is5xx}}
    {{#isDefault}}
    DefaultResponse({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/isDefault}}
    {{/responses}}
    UnknownValue(serde_json::Value),
}

{{/operation}}
{{/operations}}

{{#operations}}
{{#operation}}
{{#description}}
/// {{{.}}}
{{/description}}
{{#notes}}
/// {{{.}}}
{{/notes}}
{{#vendorExtensions.x-group-parameters}}
pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: &configuration::Configuration{{#allParams}}{{#-first}}, params: {{{operationIdCamelCase}}}Params{{/-first}}{{/allParams}}) -> Result<{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{{returnType}}}{{/supportMultipleResponses}}, Error<{{{operationIdCamelCase}}}Error>> {
    let local_var_configuration = configuration;

    // unbox the parameters
    {{#allParams}}
    let {{paramName}} = params.{{paramName}};
    {{/allParams}}

{{/vendorExtensions.x-group-parameters}}
{{^vendorExtensions.x-group-parameters}}
pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: &configuration::Configuration, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}{{#isArray}}Vec<{{/isArray}}{{^isUuid}}&str{{/isUuid}}{{#isArray}}>{{/isArray}}{{/isString}}{{#isUuid}}{{#isArray}}Vec<{{/isArray}}&str{{#isArray}}>{{/isArray}}{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) -> Result<{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{{returnType}}}{{/supportMultipleResponses}}, Error<{{{operationIdCamelCase}}}Error>> {
    let local_var_configuration = configuration;
{{/vendorExtensions.x-group-parameters}}

    let local_var_client = &local_var_configuration.client;

    {{#servers}}
    {{#-first}}
    let local_base_path = local_var_configuration.get_base_path("{{{url}}}");
    {{/-first}}
    {{/servers}}
    {{^servers}}
    let local_base_path = local_var_configuration.get_default_base_path();
    {{/servers}}
    let local_var_uri_str = format!("{}{{{path}}}", local_base_path{{#pathParams}}, {{{baseName}}}={{#isString}}crate::apis::urlencode({{/isString}}{{{paramName}}}{{^required}}.unwrap(){{/required}}{{#required}}{{#isNullable}}.unwrap(){{/isNullable}}{{/required}}{{#isArray}}.join(",").as_ref(){{/isArray}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}.to_string(){{/isContainer}}{{/isPrimitiveType}}{{/isUuid}}{{/isString}}{{#isString}}){{/isString}}{{/pathParams}});
    let mut local_var_req_builder = local_var_client.request(reqwest::Method::{{{httpMethod}}}, local_var_uri_str.as_str());

    {{#queryParams}}
    {{#required}}
    {{#isArray}}
    local_var_req_builder = match "{{collectionFormat}}" {
        "multi" => local_var_req_builder.query(&{{{paramName}}}.iter().map(|p| ("{{{baseName}}}".to_owned(), p.to_string())).collect::<Vec<(std::string::String, std::string::String)>>()),
        _ => local_var_req_builder.query(&[("{{{baseName}}}", &{{{paramName}}}.iter().map(|p| p.to_string()).collect::<Vec<String>>().join(",").to_string())]),
    };
    {{/isArray}}
    {{^isArray}}
    {{^isNullable}}
    local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &{{{paramName}}}.to_string())]);
    {{/isNullable}}
    {{#isNullable}}
    {{#isDeepObject}}
    if let Some(ref local_var_str) = {{{paramName}}} {
        let params = crate::apis::parse_deep_object("{{{baseName}}}", local_var_str);
        local_var_req_builder = local_var_req_builder.query(&params);
    };
    {{/isDeepObject}}
    {{^isDeepObject}}
    if let Some(ref local_var_str) = {{{paramName}}} {
        local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &local_var_str.to_string())]);
    };
    {{/isDeepObject}}
    {{/isNullable}}
    {{/isArray}}
    {{/required}}
    {{^required}}
    if let Some(ref local_var_str) = {{{paramName}}} {
        {{#isArray}}
        local_var_req_builder = match "{{collectionFormat}}" {
            "multi" => local_var_req_builder.query(&local_var_str.iter().map(|p| ("{{{baseName}}}".to_owned(), p.to_string())).collect::<Vec<(std::string::String, std::string::String)>>()),
            _ => local_var_req_builder.query(&[("{{{baseName}}}", &local_var_str.iter().map(|p| p.to_string()).collect::<Vec<String>>().join(",").to_string())]),
        };
        {{/isArray}}
        {{^isArray}}
        {{#isDeepObject}}
        let params = crate::apis::parse_deep_object("{{{baseName}}}", local_var_str);
        local_var_req_builder = local_var_req_builder.query(&params);
        {{/isDeepObject}}
        {{^isDeepObject}}
        local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &local_var_str.to_string())]);
        {{/isDeepObject}}
        {{/isArray}}
    }
    {{/required}}
    {{/queryParams}}
    {{#hasAuthMethods}}
    {{#authMethods}}
    {{#isApiKey}}
    {{#isKeyInQuery}}
    if let Some(ref local_var_apikey) = local_var_configuration.api_key {
        let local_var_key = local_var_apikey.key.clone();
        let local_var_value = match local_var_apikey.prefix {
            Some(ref local_var_prefix) => format!("{} {}", local_var_prefix, local_var_key),
            None => local_var_key,
        };
        local_var_req_builder = local_var_req_builder.query(&[("{{{keyParamName}}}", local_var_value)]);
    }
    {{/isKeyInQuery}}
    {{/isApiKey}}
    {{/authMethods}}
    {{/hasAuthMethods}}
    {{#hasAuthMethods}}
    {{#withAWSV4Signature}}
    if let Some(ref local_var_aws_v4_key) = local_var_configuration.aws_v4_key {
        let local_var_new_headers = match local_var_aws_v4_key.sign(
	    &local_var_uri_str,
	    "{{{httpMethod}}}",
	    {{#hasBodyParam}}
	    {{#bodyParams}}
	    &serde_json::to_string(&{{{paramName}}}).expect("param should serialize to string"),
	    {{/bodyParams}}
	    {{/hasBodyParam}}
	    {{^hasBodyParam}}
	    "",
	    {{/hasBodyParam}}
	    ) {
	      Ok(new_headers) => new_headers,
	      Err(err) => return Err(Error::AWSV4SignatureError(err)),
	    };
	for (local_var_name, local_var_value) in local_var_new_headers.iter() {
	    local_var_req_builder = local_var_req_builder.header(local_var_name.as_str(), local_var_value.as_str());
	}
    }
    {{/withAWSV4Signature}}
    {{/hasAuthMethods}}
    if let Some(ref local_var_user_agent) = local_var_configuration.user_agent {
        local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone());
    }
    {{#hasHeaderParams}}
    {{#headerParams}}
    {{#required}}
    {{^isNullable}}
    local_var_req_builder = local_var_req_builder.header("{{{baseName}}}", {{{paramName}}}{{#isArray}}.join(","){{/isArray}}.to_string());
    {{/isNullable}}
    {{#isNullable}}
    match {{{paramName}}} {
        Some(local_var_param_value) => { local_var_req_builder = local_var_req_builder.header("{{{baseName}}}", local_var_param_value{{#isArray}}.join(","){{/isArray}}.to_string()); },
        None => { local_var_req_builder = local_var_req_builder.header("{{{baseName}}}", ""); },
    }
    {{/isNullable}}
    {{/required}}
    {{^required}}
    if let Some(local_var_param_value) = {{{paramName}}} {
        local_var_req_builder = local_var_req_builder.header("{{{baseName}}}", local_var_param_value{{#isArray}}.join(","){{/isArray}}.to_string());
    }
    {{/required}}
    {{/headerParams}}
    {{/hasHeaderParams}}
    {{#hasAuthMethods}}
    {{#authMethods}}
    {{#isApiKey}}
    {{#isKeyInHeader}}
    if let Some(ref local_var_apikey) = local_var_configuration.api_key {
        let local_var_key = local_var_apikey.key.clone();
        let local_var_value = match local_var_apikey.prefix {
            Some(ref local_var_prefix) => format!("{} {}", local_var_prefix, local_var_key),
            None => local_var_key,
        };
        local_var_req_builder = local_var_req_builder.header("{{{keyParamName}}}", local_var_value);
    };
    {{/isKeyInHeader}}
    {{/isApiKey}}
    {{#isBasic}}
    {{#isBasicBasic}}
    if let Some(ref local_var_auth_conf) = local_var_configuration.basic_auth {
        local_var_req_builder = local_var_req_builder.basic_auth(local_var_auth_conf.0.to_owned(), local_var_auth_conf.1.to_owned());
    };
    {{/isBasicBasic}}
    {{#isBasicBearer}}
    if let Some(ref local_var_token) = local_var_configuration.bearer_access_token {
        local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned());
    };
    {{/isBasicBearer}}
    {{/isBasic}}
    {{#isOAuth}}
    if let Some(ref local_var_token) = local_var_configuration.oauth_access_token {
        local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned());
    };
    {{/isOAuth}}
    {{/authMethods}}
    {{/hasAuthMethods}}
    {{#isMultipart}}
    {{#hasFormParams}}
    let mut local_var_form = reqwest{{^supportAsync}}::blocking{{/supportAsync}}::multipart::Form::new();
    {{#formParams}}
    {{#isFile}}
    {{^supportAsync}}
    {{#required}}
    {{^isNullable}}
    local_var_form = local_var_form.file("{{{baseName}}}", {{{paramName}}})?;
    {{/isNullable}}
    {{#isNullable}}
    match {{{paramName}}} {
        Some(local_var_param_value) => { local_var_form = local_var_form.file("{{{baseName}}}", local_var_param_value)?; },
        None => { unimplemented!("Required nullable form file param not supported"); },
    }
    {{/isNullable}}
    {{/required}}
    {{^required}}
    if let Some(local_var_param_value) = {{{paramName}}} {
        local_var_form = local_var_form.file("{{{baseName}}}", local_var_param_value)?;
    }
    {{/required}}
    {{/supportAsync}}
    {{#supportAsync}}
    // TODO: support file upload for '{{{baseName}}}' parameter
    {{/supportAsync}}
    {{/isFile}}
    {{^isFile}}
    {{#required}}
    {{^isNullable}}
    local_var_form = local_var_form.text("{{{baseName}}}", {{{paramName}}}{{#isArray}}.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","){{/isArray}}.to_string());
    {{/isNullable}}
    {{#isNullable}}
    match {{{paramName}}} {
        Some(local_var_param_value) => { local_var_form = local_var_form.text("{{{baseName}}}", local_var_param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","){{/isArray}}.to_string()); },
        None => { local_var_form = local_var_form.text("{{{baseName}}}", ""); },
    }
    {{/isNullable}}
    {{/required}}
    {{^required}}
    if let Some(local_var_param_value) = {{{paramName}}} {
        local_var_form = local_var_form.text("{{{baseName}}}", local_var_param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","){{/isArray}}.to_string());
    }
    {{/required}}
    {{/isFile}}
    {{/formParams}}
    local_var_req_builder = local_var_req_builder.multipart(local_var_form);
    {{/hasFormParams}}
    {{/isMultipart}}
    {{^isMultipart}}
    {{#hasFormParams}}
    let mut local_var_form_params = std::collections::HashMap::new();
    {{#formParams}}
    {{#isFile}}
    {{#required}}
    {{^isNullable}}
    local_var_form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content"));
    {{/isNullable}}
    {{#isNullable}}
    match {{{paramName}}} {
        Some(local_var_param_value) => { local_var_form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content")); },
        None => { unimplemented!("Required nullable file form param not supported with x-www-form-urlencoded content"); },
    }
    {{/isNullable}}
    {{/required}}
    {{^required}}
    if let Some(local_var_param_value) = {{{paramName}}} {
        local_var_form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content"));
    }
    {{/required}}
    {{/isFile}}
    {{^isFile}}
    {{#required}}
    {{^isNullable}}
    local_var_form_params.insert("{{{baseName}}}", {{{paramName}}}{{#isArray}}.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","){{/isArray}}.to_string());
    {{/isNullable}}
    {{#isNullable}}
    match {{{paramName}}} {
        Some(local_var_param_value) => { local_var_form_params.insert("{{{baseName}}}", local_var_param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","){{/isArray}}.to_string()); },
        None => { local_var_form_params.insert("{{{baseName}}}", ""); },
    }
    {{/isNullable}}
    {{/required}}
    {{^required}}
    if let Some(local_var_param_value) = {{{paramName}}} {
        local_var_form_params.insert("{{{baseName}}}", local_var_param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","){{/isArray}}.to_string());
    }
    {{/required}}
    {{/isFile}}
    {{/formParams}}
    local_var_req_builder = local_var_req_builder.form(&local_var_form_params);
    {{/hasFormParams}}
    {{/isMultipart}}
    {{#hasBodyParam}}
    {{#bodyParams}}
    local_var_req_builder = local_var_req_builder.json(&{{{paramName}}});
    {{/bodyParams}}
    {{/hasBodyParam}}

    let local_var_req = local_var_req_builder.build()?;
    let local_var_resp = local_var_client.execute(local_var_req){{#supportAsync}}.await{{/supportAsync}}?;

    let local_var_status = local_var_resp.status();
    let local_var_content = local_var_resp.text(){{#supportAsync}}.await{{/supportAsync}}?;

    if !local_var_status.is_client_error() && !local_var_status.is_server_error() {
        {{^supportMultipleResponses}}
        {{^returnType}}
        Ok(())
        {{/returnType}}
        {{#returnType}}
        serde_json::from_str(&local_var_content).map_err(Error::from)
        {{/returnType}}
        {{/supportMultipleResponses}}
        {{#supportMultipleResponses}}
        let local_var_entity: Option<{{{operationIdCamelCase}}}Success> = serde_json::from_str(&local_var_content).ok();
        let local_var_result = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity };
        Ok(local_var_result)
        {{/supportMultipleResponses}}
    } else {
        let local_var_entity: Option<{{{operationIdCamelCase}}}Error> = serde_json::from_str(&local_var_content).ok();
        let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity };
        Err(Error::ResponseError(local_var_error))
    }
}

{{/operation}}
{{/operations}}
