package com.stripe.model;

import com.stripe.exception.StripeException;
import com.stripe.net.ApiResource;
import com.stripe.net.RequestOptions;

import java.math.BigDecimal;
import java.util.Map;

import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@EqualsAndHashCode(callSuper = false)
public class Subscription extends ApiResource implements MetadataStore<Subscription>, HasId {
  @Getter(onMethod = @__({@Override})) String id;
  String object;
  BigDecimal applicationFeePercent;
  String billing;
  Long billingCycleAnchor;
  BillingThresholds billingThresholds;
  Boolean cancelAtPeriodEnd;
  Long canceledAt;
  Long created;
  Long currentPeriodEnd;
  Long currentPeriodStart;
  @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) ExpandableField<Customer> customer;
  Long daysUntilDue;
  @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE)
      ExpandableField<ExternalAccount> defaultSource;
  Discount discount;
  Long endedAt;
  @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) SubscriptionItemCollection items;
  @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) ExpandableField<Invoice> latestInvoice;
  Boolean livemode;
  @Getter(onMethod = @__({@Override})) Map<String, String> metadata;
  Plan plan;
  Long quantity;
  Long start;
  String status;
  BigDecimal taxPercent;
  TransferData transferData;
  Long trialEnd;
  Long trialStart;

  // <editor-fold desc="customer">
  public String getCustomer() {
    return (this.customer != null) ? this.customer.getId() : null;
  }

  public void setCustomer(String customerId) {
    this.customer = setExpandableFieldId(customerId, this.customer);
  }

  public Customer getCustomerObject() {
    return (this.customer != null) ? this.customer.getExpanded() : null;
  }

  public void setCustomerObject(Customer c) {
    this.customer = new ExpandableField<>(c.getId(), c);
  }
  // </editor-fold>

  // <editor-fold desc="defaultSource">
  public String getDefaultSource() {
    return (this.defaultSource != null) ? this.defaultSource.getId() : null;
  }

  public void setDefaultSource(String defaultSourceId) {
    this.defaultSource = setExpandableFieldId(defaultSourceId, this.defaultSource);
  }

  public ExternalAccount getDefaultSourceObject() {
    return (this.defaultSource != null) ? this.defaultSource.getExpanded() : null;
  }

  public void setDefaultSourceObject(ExternalAccount c) {
    this.defaultSource = new ExpandableField<>(c.getId(), c);
  }
  // </editor-fold>

  // <editor-fold desc="latestInvoice">
  public String getLatestInvoice() {
    return (this.latestInvoice != null) ? this.latestInvoice.getId() : null;
  }

  public void setLatestInvoice(String latestInvoiceId) {
    this.latestInvoice = setExpandableFieldId(latestInvoiceId, this.latestInvoice);
  }

  public Invoice getLatestInvoiceObject() {
    return (this.latestInvoice != null) ? this.latestInvoice.getExpanded() : null;
  }

  public void setLatestInvoiceObject(Invoice c) {
    this.latestInvoice = new ExpandableField<>(c.getId(), c);
  }
  // </editor-fold>

  // <editor-fold desc="items">
  public SubscriptionItemCollection getSubscriptionItems() {
    return items;
  }

  public void setSubscriptionItems(SubscriptionItemCollection items) {
    this.items = items;
  }
  // </editor-fold>

  // <editor-fold desc="cancel">
  /**
   * Cancel a subscription.
   */
  public Subscription cancel(Map<String, Object> params) throws StripeException {
    return cancel(params, (RequestOptions) null);
  }

  /**
   * Cancel a subscription.
   */
  public Subscription cancel(Map<String, Object> params, RequestOptions options)
      throws StripeException {
    return request(RequestMethod.DELETE, instanceUrl(Subscription.class, id), params,
        Subscription.class, options);
  }
  // </editor-fold>

  // <editor-fold desc="create">
  /**
   * Create a subscription.
   */
  public static Subscription create(Map<String, Object> params) throws StripeException {
    return create(params, null);
  }

  /**
   * Create a subscription.
   */
  public static Subscription create(Map<String, Object> params, RequestOptions options)
      throws StripeException {
    return request(RequestMethod.POST, classUrl(Subscription.class), params, Subscription.class,
        options);
  }
  // </editor-fold>

  // <editor-fold desc="deleteDiscount">
  /**
   * Delete a subscription discount.
   */
  public void deleteDiscount() throws StripeException {
    deleteDiscount((RequestOptions) null);
  }

  /**
   * Delete a subscription discount.
   */
  public void deleteDiscount(RequestOptions options) throws StripeException {
    request(RequestMethod.DELETE, String.format("%s/discount", instanceUrl(Subscription.class, id)),
        null, Discount.class, options);
  }
  // </editor-fold>

  // <editor-fold desc="list">
  /**
   * List subscriptions.
   */
  public static SubscriptionCollection list(Map<String, Object> params) throws StripeException {
    return list(params, null);
  }

  /**
   * List subscriptions.
   */
  public static SubscriptionCollection list(Map<String, Object> params, RequestOptions options)
      throws StripeException {
    return requestCollection(classUrl(Subscription.class), params, SubscriptionCollection.class,
        options);
  }
  // </editor-fold>

  // <editor-fold desc="retrieve">
  /**
   * Retrieve a subscription.
   */
  public static Subscription retrieve(String id) throws StripeException {
    return retrieve(id, null);
  }

  /**
   * Retrieve a subscription.
   */
  public static Subscription retrieve(String id, RequestOptions options) throws StripeException {
    return retrieve(id, null, options);
  }

  /**
   * Retrieve a subscription.
   */
  public static Subscription retrieve(String id, Map<String, Object> params, RequestOptions options)
      throws StripeException {
    return request(RequestMethod.GET, instanceUrl(Subscription.class, id), params,
        Subscription.class, options);
  }
  // </editor-fold>

  // <editor-fold desc="update">
  /**
   * Update a subscription.
   */
  @Override
  public Subscription update(Map<String, Object> params) throws StripeException {
    return update(params, (RequestOptions) null);
  }

  /**
   * Update a subscription.
   */
  @Override
  public Subscription update(Map<String, Object> params, RequestOptions options)
      throws StripeException {
    return request(RequestMethod.POST, instanceUrl(Subscription.class, id), params,
        Subscription.class, options);
  }
  // </editor-fold>

  @Getter
  @Setter
  @EqualsAndHashCode(callSuper = false)
  public static class BillingThresholds extends StripeObject {
    Long amountGte;
    Boolean resetBillingCycleAnchor;
  }

  public static class TransferData extends StripeObject {
    @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) ExpandableField<Account> destination;

    // <editor-fold desc="destination">
    public String getDestination() {
      return (this.destination != null) ? this.destination.getId() : null;
    }

    public void setDestination(String destinationId) {
      this.destination = setExpandableFieldId(destinationId, this.destination);

    }

    public Account getDestinationObject() {
      return (this.destination != null) ? this.destination.getExpanded() : null;
    }

    public void setDestinationObject(Account c) {
      this.destination = new ExpandableField<>(c.getId(), c);
    }
    // </editor-fold>
  }
}

@@ -631,16 +631,33 @@
 sholds;%0A
+  Long cancelAt;%0A
   Boolea

Add support for `cancel_at` on `Subscription`