/*
 * Zernio API
 *
 * API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api
 *
 * The version of the OpenAPI document: 1.0.1
 * Contact: support@zernio.com
 * Generated by: https://openapi-generator.tech
 */

use super::{configuration, ContentType, Error};
use crate::{apis::ResponseContent, models};
use reqwest;
use serde::{de::Error as _, Deserialize, Serialize};
use tokio::fs::File as TokioFile;
use tokio_util::codec::{BytesCodec, FramedRead};

/// struct for typed errors of method [`bulk_upload_posts`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum BulkUploadPostsError {
    Status400(),
    Status401(models::InlineObject),
    Status429(models::BulkUploadPosts429Response),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`create_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum CreatePostError {
    Status400(models::GetYouTubeDailyViews400Response),
    Status401(models::InlineObject),
    Status403(models::GetYouTubeDailyViews400Response),
    Status409(models::CreatePost409Response),
    Status429(models::CreatePost429Response),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`delete_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DeletePostError {
    Status400(),
    Status401(models::InlineObject),
    Status403(),
    Status404(models::InlineObject1),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`get_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetPostError {
    Status401(models::InlineObject),
    Status403(),
    Status404(models::InlineObject1),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`list_posts`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListPostsError {
    Status401(models::InlineObject),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`retry_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RetryPostError {
    Status400(),
    Status401(models::InlineObject),
    Status403(),
    Status404(models::InlineObject1),
    Status409(),
    Status429(models::BulkUploadPosts429Response),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`unpublish_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UnpublishPostError {
    Status400(),
    Status401(models::InlineObject),
    Status403(),
    Status404(models::InlineObject1),
    Status500(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`update_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UpdatePostError {
    Status400(),
    Status401(models::InlineObject),
    Status403(),
    Status404(models::InlineObject1),
    UnknownValue(serde_json::Value),
}

/// Create multiple posts by uploading a CSV file. Use dryRun=true to validate without creating posts.
pub async fn bulk_upload_posts(
    configuration: &configuration::Configuration,
    dry_run: Option<bool>,
    file: Option<std::path::PathBuf>,
) -> Result<models::BulkUploadPosts200Response, Error<BulkUploadPostsError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_query_dry_run = dry_run;
    let p_form_file = file;

    let uri_str = format!("{}/v1/posts/bulk-upload", configuration.base_path);
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::POST, &uri_str);

    if let Some(ref param_value) = p_query_dry_run {
        req_builder = req_builder.query(&[("dryRun", &param_value.to_string())]);
    }
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };
    let mut multipart_form = reqwest::multipart::Form::new();
    if let Some(ref param_value) = p_form_file {
        let file = TokioFile::open(param_value).await?;
        let stream = FramedRead::new(file, BytesCodec::new());
        let file_name = param_value
            .file_name()
            .map(|n| n.to_string_lossy().to_string())
            .unwrap_or_default();
        let file_part = reqwest::multipart::Part::stream(reqwest::Body::wrap_stream(stream))
            .file_name(file_name);
        multipart_form = multipart_form.part("file", file_part);
    }
    req_builder = req_builder.multipart(multipart_form);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::BulkUploadPosts200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::BulkUploadPosts200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<BulkUploadPostsError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Create and optionally publish a post. Immediate posts (publishNow: true) include platformPostUrl in the response. Content is optional when media is attached or all platforms have customContent. See each platform's schema for media constraints.
pub async fn create_post(
    configuration: &configuration::Configuration,
    create_post_request: models::CreatePostRequest,
) -> Result<models::PostCreateResponse, Error<CreatePostError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_body_create_post_request = create_post_request;

    let uri_str = format!("{}/v1/posts", configuration.base_path);
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::POST, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };
    req_builder = req_builder.json(&p_body_create_post_request);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PostCreateResponse`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PostCreateResponse`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<CreatePostError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Delete a draft or scheduled post from Zernio. Published posts cannot be deleted; use the Unpublish endpoint instead. Upload quota is automatically refunded.
pub async fn delete_post(
    configuration: &configuration::Configuration,
    post_id: &str,
) -> Result<models::PostDeleteResponse, Error<DeletePostError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_post_id = post_id;

    let uri_str = format!(
        "{}/v1/posts/{postId}",
        configuration.base_path,
        postId = crate::apis::urlencode(p_path_post_id)
    );
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::DELETE, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PostDeleteResponse`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PostDeleteResponse`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<DeletePostError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Fetch a single post by ID. For published posts, this returns platformPostUrl for each platform.
pub async fn get_post(
    configuration: &configuration::Configuration,
    post_id: &str,
) -> Result<models::PostGetResponse, Error<GetPostError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_post_id = post_id;

    let uri_str = format!(
        "{}/v1/posts/{postId}",
        configuration.base_path,
        postId = crate::apis::urlencode(p_path_post_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PostGetResponse`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PostGetResponse`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<GetPostError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Returns a paginated list of posts. Published posts include platformPostUrl with the public URL on each platform.
pub async fn list_posts(
    configuration: &configuration::Configuration,
    page: Option<i32>,
    limit: Option<i32>,
    status: Option<&str>,
    platform: Option<&str>,
    profile_id: Option<&str>,
    created_by: Option<&str>,
    date_from: Option<String>,
    date_to: Option<String>,
    include_hidden: Option<bool>,
    search: Option<&str>,
    sort_by: Option<&str>,
) -> Result<models::PostsListResponse, Error<ListPostsError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_query_page = page;
    let p_query_limit = limit;
    let p_query_status = status;
    let p_query_platform = platform;
    let p_query_profile_id = profile_id;
    let p_query_created_by = created_by;
    let p_query_date_from = date_from;
    let p_query_date_to = date_to;
    let p_query_include_hidden = include_hidden;
    let p_query_search = search;
    let p_query_sort_by = sort_by;

    let uri_str = format!("{}/v1/posts", configuration.base_path);
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    if let Some(ref param_value) = p_query_page {
        req_builder = req_builder.query(&[("page", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_limit {
        req_builder = req_builder.query(&[("limit", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_status {
        req_builder = req_builder.query(&[("status", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_platform {
        req_builder = req_builder.query(&[("platform", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_profile_id {
        req_builder = req_builder.query(&[("profileId", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_created_by {
        req_builder = req_builder.query(&[("createdBy", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_date_from {
        req_builder = req_builder.query(&[("dateFrom", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_date_to {
        req_builder = req_builder.query(&[("dateTo", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_include_hidden {
        req_builder = req_builder.query(&[("includeHidden", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_search {
        req_builder = req_builder.query(&[("search", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_sort_by {
        req_builder = req_builder.query(&[("sortBy", &param_value.to_string())]);
    }
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PostsListResponse`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PostsListResponse`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<ListPostsError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Immediately retries publishing a failed post. Returns the updated post with its new status.
pub async fn retry_post(
    configuration: &configuration::Configuration,
    post_id: &str,
) -> Result<models::PostRetryResponse, Error<RetryPostError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_post_id = post_id;

    let uri_str = format!(
        "{}/v1/posts/{postId}/retry",
        configuration.base_path,
        postId = crate::apis::urlencode(p_path_post_id)
    );
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::POST, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PostRetryResponse`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PostRetryResponse`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<RetryPostError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Deletes a published post from the specified platform. The post record in Zernio is kept but its status is updated to cancelled. Not supported on Instagram, TikTok, or Snapchat. Threaded posts delete all items. YouTube deletion is permanent.
pub async fn unpublish_post(
    configuration: &configuration::Configuration,
    post_id: &str,
    unpublish_post_request: models::UnpublishPostRequest,
) -> Result<models::UnpublishPost200Response, Error<UnpublishPostError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_post_id = post_id;
    let p_body_unpublish_post_request = unpublish_post_request;

    let uri_str = format!(
        "{}/v1/posts/{postId}/unpublish",
        configuration.base_path,
        postId = crate::apis::urlencode(p_path_post_id)
    );
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::POST, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };
    req_builder = req_builder.json(&p_body_unpublish_post_request);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UnpublishPost200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UnpublishPost200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<UnpublishPostError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Update an existing post. Only draft, scheduled, failed, and partial posts can be edited. Published, publishing, and cancelled posts cannot be modified.
pub async fn update_post(
    configuration: &configuration::Configuration,
    post_id: &str,
    update_post_request: models::UpdatePostRequest,
) -> Result<models::PostUpdateResponse, Error<UpdatePostError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_post_id = post_id;
    let p_body_update_post_request = update_post_request;

    let uri_str = format!(
        "{}/v1/posts/{postId}",
        configuration.base_path,
        postId = crate::apis::urlencode(p_path_post_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };
    req_builder = req_builder.json(&p_body_update_post_request);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PostUpdateResponse`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PostUpdateResponse`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<UpdatePostError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}
