"The cart holds selected items until order is placed."
type Cart implements Node {
  "The Cart ID"
  _id: ID!

  "The Cart version"
  fulfillmentCartVersion: Int

  """
  The account that owns the cart. Some carts are created for anonymous users. Anonymous carts have a null account.
  Every account has exactly one cart per shop.
  """
  account: Account

  "Holds all information collected for a cart during checkout"
  checkout: Checkout

  "The date and time at which the cart was created, which is when the first item was added to it."
  createdAt: DateTime!

  "An email address that has been associated with the cart"
  email: String

  # Read-only: Carts that don't have an account should be considered anonymous
  # and should be cleaned up after some time. This should not be set explicitly
  # on the cart but can be derived and add to the Cart as a read-only
  # attribute (read, value from a function.) The cart expiration ttl can then be
  # modified without having to modify cart records.
  # This value should not be stored in the database.
  # Cart expiry is: updated_at + cart_expiry_threshold < now
  "The date and time at which the cart will expire. Account carts usually do not expire, so they will have a null value here."
  expiresAt: DateTime

  "The items that have been added to the cart. A cart is not created until the first item is added. Items can be removed from a cart, and a cart is not deleted if all items are removed from it. Because all items may have been removed, this may be an empty array."
  items(
    "Return only results that come after this cursor. Use this with `first` to specify the number of results to return."
    after: ConnectionCursor,

    "Return only results that come before this cursor. Use this with `last` to specify the number of results to return."
    before: ConnectionCursor,

    "Return at most this many results. This parameter may be used with either `after` or `offset` parameters."
    first: ConnectionLimitInt,

    "Return at most this many results. This parameter may be used with the `before` parameter."
    last: ConnectionLimitInt,

    "Return only results that come after the Nth result. This parameter may be used with the `first` parameter."
    offset: Int,

    "Return results sorted in this order"
    sortOrder: SortOrder = desc,

    "By default, items are sorted by when they were added to the cart, newest first. Set this to sort by one of the other allowed fields"
    sortBy: CartItemsSortByField = addedAt
  ): CartItemConnection

  """
  If any products or variants become hidden or are deleted after they were added to this cart, they'll be
  automatically moved from `items` to `missingItems`. Clients may want to use this to show an
  "items that are no longer available" list to storefront users.

  If a product becomes visible again, the item will never be automatically moved from `missingItems`
  back to `items`, but clients may want to provide a way for users to manually do this.
  """
  missingItems: [CartItem]

  "The cart messages. These are messages that are returned from the server and displayed to the user."
  messages: [CartMessage]

  """
  If you integrate with third-party systems that require you to send the same ID for order
  calculations as for cart calculations, you may use this ID, which is the same on a `cart` as on
  the `order` placed from that cart. This ID can also be customized by plugins and is the best
  ID to use if it is necessary to show a cart ID in the user interface.
  """
  referenceId: String

  "The shop that owns the cart."
  shop: Shop!

  "Total quantity of all items in the cart"
  totalItemQuantity: Int!

  "The date and time at which this cart was last updated."
  updatedAt: DateTime!
}

"Allowed values for cart item sortBy parameter"
enum CartItemsSortByField {
  "Cart item ID"
  _id

  "Date and time at which the item was added to the cart"
  addedAt
}


enum CartMessageSeverity {
  "Informational message"
  info

  "Warning message"
  warning

  "Error message"
  error
}

"The cart message type"
type CartMessage {
  "Cart message ID"
  _id: ID!

  "Cart message title"
  title: String!

  "Cart message severity"
  severity: CartMessageSeverity!

  "Cart message content"
  message: String

  "Cart message is acknowledged"
  acknowledged: Boolean

  "Cart message subject"
  subject: String

  "Cart message meta fields"
  metaFields: JSONObject

  "The cart message should be confirm by user or not"
  requiresReadAcknowledgement: Boolean
}

"""
Wraps a list of `CartItem`s, providing pagination cursors and information.

For information about what Relay-compatible connections are and how to use them, see the following articles:
- [Relay Connection Documentation](https://facebook.github.io/relay/docs/en/graphql-server-specification.html#connections)
- [Relay Connection Specification](https://facebook.github.io/relay/graphql/connections.htm)
- [Using Relay-style Connections With Apollo Client](https://www.apollographql.com/docs/react/recipes/pagination.html)
"""
type CartItemConnection {
  "The list of nodes that match the query, wrapped in an edge to provide a cursor string for each"
  edges: [CartItemEdge]

  """
  You can request the `nodes` directly to avoid the extra wrapping that `NodeEdge` has,
  if you know you will not need to paginate the results.
  """
  nodes: [CartItem]

  "Information to help a client request the next or previous page"
  pageInfo: PageInfo!

  "The total number of nodes that match your query"
  totalCount: Int!
}

"A connection edge in which each node is a `CartItem` object"
type CartItemEdge implements NodeEdge {
  "The cursor that represents this node in the paginated results"
  cursor: ConnectionCursor!

  "The cart item"
  node: CartItem
}

"One attribute of a cart item"
type CartItemAttribute {
  "The attribute label, e.g., Color"
  label: String

  "The attribute value, e.g., Blue"
  value: String
}

"""
A single item in a cart. The item contains information about an intended purchase.
"""
type CartItem implements Node {
  "The cart item ID"
  _id: ID!

  """"
  The date and time at which this item was first added to the associated cart.
  If an item is added, removed, and then added again, this will reflect the most recent addition.
  However, if an item is added twice, the quantity will increase but this date will remain
  the initial added date.
  """
  addedAt: DateTime!

  """
  FUTURE. Additional attributes of the chosen item. For example, if this item is for a product, socks, where `blue` and `small`
  options were chosen for some configurable attributes, then `color:blue` and `size:small` will be indicated here.
  """
  attributes: [CartItemAttribute]

  "The current comparison (e.g., MSRP) price of the item"
  compareAtPrice: Money

  """
  The date and time at which the cart item was created. If an item is added, removed, and then added again,
  the original item is destroyed and this field will reflect the time it was created for the most recent addition.
  """
  createdAt: DateTime!

  "The URLs for a picture of the item in various sizes"
  imageURLs: ImageSizes

  "Arbitrary additional metadata about this cart item."
  metafields: [Metafield]

  "The selected variant optionTitle"
  optionTitle: String

  "Packing information such as item weight, height, length, and depth. Used for calculating shipping rates."
  parcel: ShippingParcel

  "The current price of the item"
  price: Money!

  # This will permit us to perform calculations based on how the price has changed since
  # the item was added to the cart.
  "The price at which this item was listed when it was added to the cart"
  priceWhenAdded: Money!

  "The product and chosen options"
  productConfiguration: ProductConfiguration!

  "The product's slug"
  productSlug: String

  "The list of tags that have been applied to this product"
  productTags(
    "Return only results that come after this cursor. Use this with `first` to specify the number of results to return."
    after: ConnectionCursor,

    "Return only results that come before this cursor. Use this with `last` to specify the number of results to return."
    before: ConnectionCursor,

    "Return at most this many results. This parameter may be used with either `after` or `offset` parameters."
    first: ConnectionLimitInt,

    "Return at most this many results. This parameter may be used with the `before` parameter."
    last: ConnectionLimitInt,

    "Return only results that come after the Nth result. This parameter may be used with the `first` parameter."
    offset: Int,

    "Return results sorted in this order"
    sortOrder: SortOrder = asc,

    "By default, tags are sorted by ID. Set this to sort by one of the other allowed fields"
    sortBy: TagSortByField = _id
  ): TagConnection

  "The type of product, used to display cart items differently"
  productType: String

  "The product vendor"
  productVendor: String

  "The quantity of this item that has been added to the cart. This must be a positive integer. Remove this `CartItem` from it's associated cart if you want `0` of this item."
  quantity: Int!

  "The shop associated with this cart item."
  shop: Shop!

  "The current price of the item multiplied by the quantity"
  subtotal: Money!

  "A title for use in cart/orders that conveys the selected product's title + chosen options"
  title: String!

  "The fulfillment type of the item (if implemented in UI & selected by user) while adding item to cart"
  selectedFulfillmentType: String

  "The fulfillment types supported by the item (received from Catalog)"
  supportedFulfillmentTypes: [String]

  "The date and time at which this item was last updated"
  updatedAt: DateTime!

  "The selected variant title"
  variantTitle: String
}

"Supported cart reconciliation modes"
enum CartReconciliationMode {
  "Delete the anonymous cart and use the account cart."
  keepAccountCart

  "Assign the anonymous cart to the account, and delete the account cart."
  keepAnonymousCart

  """
  Move all items from the anonymous cart into the account cart along with existing
  account cart items. If the same item is in both carts, combine the quantities.
  """
  merge
}

####################
# Queries
####################

extend type Query {
  "Finds a cart by the cart ID and anonymous cart token."
  anonymousCartByCartId(
    "The cart ID. Must be an anonymous cart (that is, one with no linked account)."
    cartId: ID!,

    """
    A valid anonymous cart access cartToken for this cart. This is returned when you create
    an anonymous cart and should be stored securely in storefront client storage.
    """
    cartToken: String!
  ): Cart

  # Access control should ensure that only authenticated users can find their own
  # cart. Additionally, administrative roles can find carts.
  # Shop ID is necessary if shops have separate carts but share the same account pool
  "Find a cart for a given account ID."
  accountCartByAccountId(
    "Account that owns the cart"
    accountId: ID!,

    "Shop that owns the cart"
    shopId: ID!
  ): Cart
}

####################
# Inputs
####################

"The input necessary to create a cart"
input CreateCartInput {
  "An optional string identifying the mutation call, which will be returned in the response payload"
  clientMutationId: String

  "Array of items to add to new cart."
  items: [CartItemInput]!

  "ShopId association for the cart."
  shopId: ID!
}

"Information about an item to add to a cart"
input CartItemInput {
  "Arbitrary additional metadata about this cart item."
  metafields: [MetafieldInput]

  """
  The price of this item, for validating that this matches the actual price in the system,
  in case the client has stale data.
  """
  price: MoneyInput!

  "The product and chosen options"
  productConfiguration: ProductConfigurationInput!

  "The number of this item to add to the cart"
  quantity: Int!

  "The fulfillment type of the item (if implemented in UI & selected by user) while adding item to cart"
  selectedFulfillmentType: String
}

"Input that defines a single configuration of a product"
input ProductConfigurationInput {
  "The Product ID"
  productId: ID!

  "The ProductVariant ID"
  productVariantId: ID!
}

"Input for the `addCartItems` mutation"
input AddCartItemsInput {
  "The cart ID"
  cartId: ID!

  "If this cart is anonymous, provide the `cartToken` that was returned in the `CreateCartPayload`"
  cartToken: String

  "An optional string identifying the mutation call, which will be returned in the response payload"
  clientMutationId: String

  "Array of items to be added to the cart"
  items: [CartItemInput]!
}

"Input for the `setFulfillmentTypeForItems` mutation"
input SetFulfillmentTypeForItemsInput {
  "The cart ID"
  cartId: ID!

  "If this cart is anonymous, provide the `cartToken` that was returned in the `CreateCartPayload`"
  cartToken: String

  "An optional string identifying the mutation call, which will be returned in the response payload"
  clientMutationId: String

  "Fulfillment Type to be assigned to the items"
  fulfillmentType: String!

  "Array of item IDs for which selectedFulfillmentType has to be updated"
  itemIds: [String]!
}

"Input for the `removeCartItems` mutation"
input RemoveCartItemsInput {
  "The cart ID"
  cartId: ID!

  "Array of items to remove from the cart."
  cartItemIds: [ID]!

  "If this cart is anonymous, provide the `cartToken` that was returned in the `CreateCartPayload`"
  cartToken: String

  "An optional string identifying the mutation call, which will be returned in the response payload"
  clientMutationId: String
}

"Input for the `updateCartItem` mutation"
input UpdateCartItemInput {
  "The cart item ID"
  cartItemId: ID!

  "New absolute value for specified cart item's quantity. Not an incremental value."
  quantity: Int!
}

"Input for the `updateCartItemsQuantity` mutation"
input UpdateCartItemsQuantityInput {
  "The cart ID"
  cartId: ID!

  "If this cart is anonymous, provide the `cartToken` that was returned in the `CreateCartPayload`"
  cartToken: String

  "An optional string identifying the mutation call, which will be returned in the response payload"
  clientMutationId: String

  "Array of cart item quantities to update."
  items: [UpdateCartItemInput]!
}

"Input for the `reconcileCarts` mutation call"
input ReconcileCartsInput {
  "An anonymous cart ID"
  anonymousCartId: ID!

  "An anonymous cart token"
  cartToken: String!

  "An optional string identifying the mutation call, which will be returned in the response payload"
  clientMutationId: String

  """
  If both an anonymous cart and a cart for the authenticated account are found, how do we combine them?
  Default mode is `merge`, where all anonymous items are moved into the account cart along with existing
  account cart items, and quantities are combined.
  """
  mode: CartReconciliationMode

  "The ID of the shop that owns both carts"
  shopId: ID!
}

"Input for the `setEmailOnAnonymousCart` mutation call"
input SetEmailOnAnonymousCartInput {
  "An anonymous cart ID"
  cartId: ID!

  "Provide the `cartToken` that was returned in the `CreateCartPayload`"
  cartToken: String!

  "An optional string identifying the mutation call, which will be returned in the response payload"
  clientMutationId: String

  """
  The email address to associate with this cart. This address is used for order communication and
  other fulfillment purposes.
  """
  email: String!
}

input CartUpdatedInput {
  "The cart ID"
  cartId: ID!,

  "The cart account ID"
  accountId: ID

  "The cart anonymous token"
  cartToken: String
}

"Input for the `acknowledgeCartMessage` mutation call"
input AcknowledgeCartMessageInput {
  "The cart ID"
  cartId: ID!,

  "The message to acknowledge"
  messageId: String!

  "An optional string identifying the mutation call, which will be returned in the response payload"
  clientMutationId: String

  "The cart anonymous token"
  cartToken: String
}

####################
# Payloads
# These types are used as return values for mutation calls
####################

"Details about a CartItemInput that failed to be added to a cart due to a price mismatch"
type IncorrectPriceFailureDetails {
  "The current price in the system for this product configuration in the requested currency"
  currentPrice: Money!

  "The productConfiguration that was provided with the CartItemInput that caused this failure"
  productConfiguration: ProductConfiguration!

  "The price that was provided with the CartItemInput that caused this failure"
  providedPrice: Money!
}

"Details about a CartItemInput that failed to be added to a cart due to a quantity error"
type MinOrderQuantityFailureDetails {
  "The minimum quantity that can be added to a cart"
  minOrderQuantity: Int!

  "The productConfiguration that was provided with the CartItemInput that caused this failure"
  productConfiguration: ProductConfiguration!

  "The quantity that was provided with the CartItemInput that caused this failure"
  quantity: Int!
}

"The payload returned from the `createCart` mutation call"
type CreateCartPayload {
  """
  The created cart, if at least one item could be added. Otherwise null, and you should check
  `incorrectPriceFailures` and `minOrderQuantityFailures` for information necessary to display
  errors to the shopper.
  """
  cart: Cart

  "The same string you sent with the mutation params, for matching mutation calls with their responses"
  clientMutationId: String

  """
  Clients should check to see if any items failed to be added due to the price not matching the current price.
  In general, a user interface should display the correct current prices to the shopper, confirm that they still
  want to add the items, and then call `createCart` or `addCartItems` to do so.

  Note that this field will always exist but may be an empty array if there were no failures of this type.
  """
  incorrectPriceFailures: [IncorrectPriceFailureDetails]!

  """
  Clients should check to see if any items failed to be added due to quantity being below the minimum order
  quantity defined for the product variant. In general, a user interface should display the minimum order
  quantity to the shopper and allow them to add that quantity or greater.

  Note that this field will always exist but may be an empty array if there were no failures of this type.
  """
  minOrderQuantityFailures: [MinOrderQuantityFailureDetails]!

  """
  If no identity token is provided with the request, then this mutation will create an anonymous cart. All
  anonymous carts have a token associated with them, which allows the client that created the cart to access
  that cart in the future. This is the only time this token is returned, so clients must store this securely
  in some type of local storage solution, and then send it along with all future anonymous cart queries and
  mutations.
  """
  token: String
}

"The payload returned from the `setFulfillmentTypeForItems` mutation call"
type SetFulfillmentTypeForItemsPayload {
  """
  The modified cart.
  """
  cart: Cart

  "The same string you sent with the mutation params, for matching mutation calls with their responses"
  clientMutationId: String
}

"The payload returned from the `addCartItems` mutation call"
type AddCartItemsPayload {
  """
  The modified cart. You should check `incorrectPriceFailures` and `minOrderQuantityFailures` for
  information necessary to display errors to the shopper. Some items may not have been added.
  """
  cart: Cart

  "The same string you sent with the mutation params, for matching mutation calls with their responses"
  clientMutationId: String

  """
  Clients should check to see if any items failed to be added due to the price not matching the current price.
  In general, a user interface should display the correct current prices to the shopper, confirm that they still
  want to add the items, and then call `createCart` or `addCartItems` to do so.

  Note that this field will always exist but may be an empty array if there were no failures of this type.
  """
  incorrectPriceFailures: [IncorrectPriceFailureDetails]!

  """
  Clients should check to see if any items failed to be added due to quantity being below the minimum order
  quantity defined for the product variant. In general, a user interface should display the minimum order
  quantity to the shopper and allow them to add that quantity or greater.

  Note that this field will always exist but may be an empty array if there were no failures of this type.
  """
  minOrderQuantityFailures: [MinOrderQuantityFailureDetails]!
}

"The payload returned from the `removeCartItems` mutation call"
type RemoveCartItemsPayload {
  "The modified cart"
  cart: Cart!

  "The same string you sent with the mutation params, for matching mutation calls with their responses"
  clientMutationId: String
}

"The payload returned from the `updateCartItemsQuantity` mutation call"
type UpdateCartItemsQuantityPayload {
  "The modified cart"
  cart: Cart!

  "The same string you sent with the mutation params, for matching mutation calls with their responses"
  clientMutationId: String
}

"The payload returned from the `reconcileCarts` mutation call"
type ReconcileCartsPayload {
  "The account cart, potentially modified"
  cart: Cart!

  "The same string you sent with the mutation params, for matching mutation calls with their responses"
  clientMutationId: String
}

"The payload returned from the `setEmailOnAnonymousCart` mutation call"
type SetEmailOnAnonymousCartPayload {
  "The modified cart"
  cart: Cart!

  "The same string you sent with the mutation params, for matching mutation calls with their responses"
  clientMutationId: String
}

"The payload returned from the `acknowledgeCartMessage` mutation call"
type AcknowledgeCartMessagePayload {
  "The modified cart"
  cart: Cart!
}

####################
# Mutations
####################

extend type Mutation {
  "Add item(s) to a cart"
  addCartItems(
    "Mutation input"
    input: AddCartItemsInput!
  ): AddCartItemsPayload!

  "Create a new cart"
  createCart(
    "Mutation input"
    input: CreateCartInput!
  ): CreateCartPayload!

  "Updates the Selected fulfillment type and Fulfillment groups of the item(s)"
  setFulfillmentTypeForItems(
    "SetFulfillmentTypeForItems input"
    input: SetFulfillmentTypeForItemsInput!
  ): SetFulfillmentTypeForItemsPayload!

  "Reconcile an anonymous cart with the current account cart for the same shop"
  reconcileCarts(
    "Mutation input"
    input: ReconcileCartsInput!
  ): ReconcileCartsPayload!

  "Remove item(s) from a cart"
  removeCartItems(
    "Mutation input"
    input: RemoveCartItemsInput!
  ): RemoveCartItemsPayload!

  "Set the email address for an anonymous cart"
  setEmailOnAnonymousCart(
    "Mutation input"
    input: SetEmailOnAnonymousCartInput!
  ): SetEmailOnAnonymousCartPayload!

  "Update cart item(s) quantity. Use absolute quantity. If updating to 0, the item will be removed."
  updateCartItemsQuantity(
    "Mutation input"
    input: UpdateCartItemsQuantityInput!
  ): UpdateCartItemsQuantityPayload!

  "Acknowledge a message on the account cart"
  acknowledgeCartMessage(
    "Mutation input"
    input: AcknowledgeCartMessageInput!
  ): AcknowledgeCartMessagePayload!
}

####################
 # Subscriptions
 ####################
extend type Subscription {
  "Subscribe to changes to cart"
  cartUpdated(
    input: CartUpdatedInput!
  ): Cart!
}
