use anyhow::Result;

use crate::Client;

pub struct Disputes {
    pub client: Client,
}

impl Disputes {
    #[doc(hidden)]
    pub fn new(client: Client) -> Self {
        Disputes { client }
    }

    /**
    * This function performs a `GET` to the `/v1/disputes` endpoint.
    *
    * <p>Returns a list of your disputes.</p>
    *
    * **Parameters:**
    *
    * * `charge: &str` -- Only return disputes associated to the charge specified by this charge ID.
    * * `created: &str`
    * * `ending_before: &str` -- A cursor for use in pagination. `ending_before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with `obj_bar`, your subsequent call can include `ending_before=obj_bar` in order to fetch the previous page of the list.
    * * `expand: &[String]` -- Fields that need to be collected to keep the capability enabled. If not collected by `future_requirements[current_deadline]`, these fields will transition to the main `requirements` hash.
    * * `limit: i64` -- A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.
    * * `payment_intent: &str` -- Only return disputes associated to the PaymentIntent specified by this PaymentIntent ID.
    * * `starting_after: &str` -- A cursor for use in pagination. `starting_after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with `obj_foo`, your subsequent call can include `starting_after=obj_foo` in order to fetch the next page of the list.
    */
    pub async fn get_page(
        &self,
        charge: &str,
        _created: &str,
        ending_before: &str,
        limit: i64,
        payment_intent: &str,
        starting_after: &str,
    ) -> Result<Vec<crate::types::Dispute>> {
        let mut query_args: Vec<(String, String)> = Default::default();
        if !charge.is_empty() {
            query_args.push(("charge".to_string(), charge.to_string()));
        }
        if !ending_before.is_empty() {
            query_args.push(("ending_before".to_string(), ending_before.to_string()));
        }
        if limit > 0 {
            query_args.push(("limit".to_string(), limit.to_string()));
        }
        if !payment_intent.is_empty() {
            query_args.push(("payment_intent".to_string(), payment_intent.to_string()));
        }
        if !starting_after.is_empty() {
            query_args.push(("starting_after".to_string(), starting_after.to_string()));
        }
        let query_ = serde_urlencoded::to_string(&query_args).unwrap();
        let url = format!("/v1/disputes?{}", query_);

        let resp: crate::types::GetDisputesResponse = self.client.get(&url, None).await?;

        // Return our response data.
        Ok(resp.data.to_vec())
    }

    /**
    * This function performs a `GET` to the `/v1/disputes` endpoint.
    *
    * As opposed to `get`, this function returns all the pages of the request at once.
    *
    * <p>Returns a list of your disputes.</p>
    */
    pub async fn get_all(
        &self,
        charge: &str,
        _created: &str,
        payment_intent: &str,
    ) -> Result<Vec<crate::types::Dispute>> {
        let mut query_args: Vec<(String, String)> = Default::default();
        if !charge.is_empty() {
            query_args.push(("charge".to_string(), charge.to_string()));
        }
        if !payment_intent.is_empty() {
            query_args.push(("payment_intent".to_string(), payment_intent.to_string()));
        }
        let query_ = serde_urlencoded::to_string(&query_args).unwrap();
        let url = format!("/v1/disputes?{}", query_);

        let mut resp: crate::types::GetDisputesResponse = self.client.get(&url, None).await?;

        let mut data = resp.data;
        let mut has_more = resp.has_more;
        let mut page = "".to_string();

        // Paginate if we should.
        while has_more {
            if !data.is_empty() {
                let last = data.last().unwrap();
                let j = serde_json::json!(last);
                if let serde_json::Value::Object(o) = j {
                    if let Some(serde_json::Value::String(s)) = o.get("id") {
                        page = s.to_string();
                    }
                }
            }

            if !url.contains('?') {
                resp = self
                    .client
                    .get(&format!("{}?startng_after={}", url, page), None)
                    .await?;
            } else {
                resp = self
                    .client
                    .get(&format!("{}&starting_after={}", url, page), None)
                    .await?;
            }

            data.append(&mut resp.data);

            has_more = resp.has_more;
        }

        // Return our response data.
        Ok(data.to_vec())
    }

    /**
    * This function performs a `GET` to the `/v1/disputes/{dispute}` endpoint.
    *
    * <p>Retrieves the dispute with the given ID.</p>
    *
    * **Parameters:**
    *
    * * `dispute: &str` -- The account's country.
    * * `expand: &[String]` -- Fields that need to be collected to keep the capability enabled. If not collected by `future_requirements[current_deadline]`, these fields will transition to the main `requirements` hash.
    */
    pub async fn get(&self, dispute: &str) -> Result<crate::types::Dispute> {
        let url = format!(
            "/v1/disputes/{}",
            crate::progenitor_support::encode_path(dispute),
        );

        self.client.get(&url, None).await
    }

    /**
    * This function performs a `POST` to the `/v1/disputes/{dispute}` endpoint.
    *
    * <p>When you get a dispute, contacting your customer is always the best first step. If that doesn’t work, you can submit evidence to help us resolve the dispute in your favor. You can do this in your <a href="https://dashboard.stripe.com/disputes">dashboard</a>, but if you prefer, you can use the API to submit evidence programmatically.</p>
    *
    * <p>Depending on your dispute type, different evidence fields will give you a better chance of winning your dispute. To figure out which evidence fields to provide, see our <a href="/docs/disputes/categories">guide to dispute types</a>.</p>
    *
    * **Parameters:**
    *
    * * `dispute: &str` -- The account's country.
    */
    pub async fn post(&self, dispute: &str) -> Result<crate::types::Dispute> {
        let url = format!(
            "/v1/disputes/{}",
            crate::progenitor_support::encode_path(dispute),
        );

        self.client.post(&url, None).await
    }

    /**
    * This function performs a `POST` to the `/v1/disputes/{dispute}/close` endpoint.
    *
    * <p>Closing the dispute for a charge indicates that you do not have any evidence to submit and are essentially dismissing the dispute, acknowledging it as lost.</p>
    *
    * <p>The status of the dispute will change from <code>needs_response</code> to <code>lost</code>. <em>Closing a dispute is irreversible</em>.</p>
    *
    * **Parameters:**
    *
    * * `dispute: &str` -- The account's country.
    */
    pub async fn post_close(&self, dispute: &str) -> Result<crate::types::Dispute> {
        let url = format!(
            "/v1/disputes/{}/close",
            crate::progenitor_support::encode_path(dispute),
        );

        self.client.post(&url, None).await
    }
}
