import type Graphic from "../../Graphic.js";
import type Collection from "../../core/Collection.js";
import type EsriError from "../../core/Error.js";
import type SpatialReference from "../../geometry/SpatialReference.js";

/**
 * A feature identifier.
 * Either objectId or globalId is required when this object is used as input
 * for operations.
 */
export interface FeatureIdentifier {
  /** The globalId of the feature or the attachment that was edited. */
  globalId?: string | null;
  /** The objectId of the feature or the attachmentId of the attachment that was added/edited/deleted. */
  objectId?: number | null;
}

/** FeatureEditResult represents the result of adding, updating or deleting a feature or an attachment. */
export interface FeatureEditResult extends FeatureIdentifier {
  /** If the edit failed, the edit result includes an error. */
  error?: EsriError | null;
}

/**
 * AttachmentEdit represents an attachment that can be added, updated or deleted via [FeatureLayer#applyEdits()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#applyEdits).
 * This object can be either pre-uploaded data or base 64 encoded data.
 */
export interface AttachmentEdit {
  /** The attachment to be added, updated or deleted. */
  attachment: Attachment;
  /**
   * The feature, `objectId` or `globalId` of feature associated
   * with the attachment.
   */
  feature: Graphic | FeatureIdentifier;
}

export interface Attachment {
  /**
   * The content type of the attachment. For example, `'image/jpeg'`.
   * See the [ArcGIS REST API documentation](https://developers.arcgis.com/rest/services-reference/query-attachments-feature-service-layer-.htm)
   * for more information on supported attachment types.
   */
  contentType?: string;
  /** The attachment data. */
  data?: Blob | File | string;
  /**
   * The globalId of the attachment to be added or updated. These Global IDs must be from the Global ID field created by ArcGIS.
   * For more information on ArcGIS generated Global IDs, see the Global IDs and Attachments and relationship classes sections in the
   * [Data Preparation](https://enterprise.arcgis.com/en/server/latest/publish-services/windows/prepare-data-for-offline-use.htm#ESRI_SECTION1_CDC34577197B43A980E4B5021DB1C32A) documentation.
   */
  globalId: string;
  /** The name of the attachment. This parameter must be set if the attachment type is `Blob`. */
  name?: string;
  /** The id of pre-loaded attachment. */
  uploadId?: string;
}

export interface EditOptions {
  /**
   * The geodatabase version to apply the edits. This parameter applies only if the
   * {@link layers/graphics/editingSupport#capabilities capabilities.data.isVersioned} property of the layer is `true`. If the gdbVersion parameter is not specified, edits are made to the published map's version.
   */
  gdbVersion?: string;
  /**
   * Indicates whether the edits can be applied using globalIds of features or attachments.
   * This parameter applies only if the layer's {@link layers/graphics/editingSupport#capabilities capabilities.editing.supportsGlobalId} property is `true`.
   * When `false`, globalIds submitted with the features are ignored and the service assigns new globalIds
   * to the new features. When `true`, the globalIds must be submitted with the new features. When updating existing features, if the `globalIdUsed` is
   * `false`, the objectIds of the features to be updated must be provided. If the `globalIdUsed` is `true`, globalIds of features to be updated must be provided.
   * When deleting existing features, set this property to `false` as deletes operation only accepts `objectIds` at the current version of the API.
   *
   * When adding, updating or deleting attachments, `globalIdUsed` parameter must be set to `true` and the attachment globalId must be set.
   * For new attachments, the user must provide globalIds. In order for an attachment to be updated or deleted, clients must include its globalId.
   * Attachments are not supported in an edit payload when `globalIdUsed` is `false`.
   *
   * ```js
   * // add an image attachments to features
   * function addAttachment(selectedFeature) {
   *   const blob = new Blob(byteArrays, { type: "image/png" });
   *   addAttachments.push({
   *     feature: selectedFeature,
   *     attachment: {
   *       globalId: "8c4d6085-a33c-42a0-8e11-21e9528bca0d",
   *       name: "brokenLight",
   *       data: blob
   *     }
   *   });
   *
   *   const edits = {
   *     addAttachments: addAttachments
   *   };
   *
   *   const options = {
   *     // globalIdUsed has to be true when adding, updating or deleting attachments
   *     globalIdUsed: true,
   *     rollbackOnFailureEnabled: true
   *   };
   *
   *   featureLayer.applyEdits(edits, options).then(function(results) {
   *     console.log("edits added: ", results);
   *   });
   * }
   * ```
   */
  globalIdUsed?: boolean;
  /**
   * Indicates whether the edit results should return the time edits were applied. If `true`, the feature service will return
   * the time edits were applied in the edit result's `editMoment` property. Only applicable with ArcGIS Server services only. This option was added at the version 4.20.
   */
  returnEditMoment?: boolean;
  /**
   * If set to `original-and-current-features`, the [EditedFeatureResult](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-graphics-editingSupport.html#EditedFeatureResult) parameter will be included
   * in the `applyEdits` response. It contains all edited features participating in composite relationships in a database as result of editing a feature. Note that even for deletions, the geometry and attributes
   * of the deleted feature are returned. The `original-and-current-features` option is only valid when `rollbackOnFailureEnabled` is `true`. The default value is `none`, which will not include the
   * `EditedFeatureResult` parameter in the response. This is only applicable with ArcGIS Server services only. This option was added at the version 4.20.
   */
  returnServiceEditsOption?: "none" | "original-and-current-features";
  /**
   * Indicates whether the edits should be applied only if all submitted edits succeed.
   * If `false`, the server will apply the edits that succeed even if some of the submitted edits fail. If `true`, the server will apply the edits
   * only if all edits succeed. The layer's {@link layers/graphics/editingSupport#capabilities capabilities.editing.supportsRollbackOnFailure} property must be `true` if using this parameter.
   * If `supportsRollbackOnFailure` is `false` for a layer, then `rollbackOnFailureEnabled` will always be true, regardless of how the parameter is set.
   */
  rollbackOnFailureEnabled?: boolean;
}

export interface Edits {
  /**
   * An array of attachments to be added. Applies only when the `options.globalIdUsed`
   * parameter is set to `true`. User must provide [globalId](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-graphics-editingSupport.html#AttachmentEdit)s for all attachments to be added.
   */
  addAttachments?: AttachmentEdit[];
  /**
   * An array or a
   * [Collection](https://developers.arcgis.com/javascript/latest/api-reference/esri-core-Collection.html) of features to be added. Feature geometries must be of type
   * [Mesh](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-Mesh.html). Values of non nullable fields must be provided when adding new features. Date fields must have
   * [numeric](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime) values
   * representing universal time.
   */
  addFeatures?: Graphic[] | Collection<Graphic>;
  /**
   * An array of [globalId](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-graphics-editingSupport.html#AttachmentEdit)s for attachments to be deleted. Applies only when the `options.globalIdUsed`
   * parameter is set to `true`.
   */
  deleteAttachments?: string[];
  /**
   * An array or a
   * [Collection](https://developers.arcgis.com/javascript/latest/api-reference/esri-core-Collection.html) of features, or an array of objects with {@link layers/graphics/editingSupport#globalIdField globalId} of each feature to be deleted.
   * When an array or collection of features is passed, each feature must have a valid {@link layers/graphics/editingSupport#globalIdField globalId}. When an array of objects is used,
   * each object must have a valid value set for the {@link layers/graphics/editingSupport#globalIdField globalId} property.
   */
  deleteFeatures?: Graphic[] | Collection<Graphic> | FeatureIdentifier[];
  /**
   * An array of attachments to be updated. Applies only when the `options.globalIdUsed`
   * parameter is set to `true`. User must provide [globalId](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-graphics-editingSupport.html#AttachmentEdit)s for all attachments to be updated.
   */
  updateAttachments?: AttachmentEdit[];
  /**
   * An array or a
   * [Collection](https://developers.arcgis.com/javascript/latest/api-reference/esri-core-Collection.html) of features to be updated. Each feature must have a valid
   * {@link layers/graphics/editingSupport#objectIdField objectId} or {@link layers/graphics/editingSupport#globalIdField globalId} for attribute updates, while geometry updates must always use a valid
   * {@link layers/graphics/editingSupport#globalIdField globalId}. Values of non nullable fields must be provided when updating features. Date fields must have
   * [numeric](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime) values
   * representing universal time.
   */
  updateFeatures?: Graphic[] | Collection<Graphic>;
}

/** Results returned from the [FeatureLayer#applyEdits()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#applyEdits) method. */
export interface EditsResult {
  /** Result of adding attachments. */
  addAttachmentResults: FeatureEditResult[];
  /** Result of adding features. */
  addFeatureResults: FeatureEditResult[];
  /** Result of deleting attachments. */
  deleteAttachmentResults: FeatureEditResult[];
  /** Result of deleting features. */
  deleteFeatureResults: FeatureEditResult[];
  /**
   * Edited features as result of editing a feature that participates in composite relationships in a database.
   * This parameter is returned only when the `returnServiceEditsOption` parameter of the [FeatureLayer#applyEdits()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#applyEdits) method is set to `original-and-current-features`. This parameter was added at 4.20.
   */
  editedFeatureResults?: EditedFeatureResult[];
  /**
   * The time edits were applied to the feature service.
   * This parameter is returned only when the `returnEditMoment` parameter of the [FeatureLayer#applyEdits()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#applyEdits) method is set to `true`. This option was added at the version 4.20.
   */
  editMoment?: number;
  /** Result of updating attachments. */
  updateAttachmentResults: FeatureEditResult[];
  /** Result of updating features. */
  updateFeatureResults: FeatureEditResult[];
}

/**
 * Results returned from the [FeatureLayer#applyEdits()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#applyEdits) method if the `returnServiceEditsOption` parameter is set to `original-and-current-features`. It contains features
 * that were added, deleted or updated in different feature layers of a feature service as a result of editing a single feature that participates in a composite relationship in
 * a database. The results are organized by each layer affected by the edit.
 * For example, if a feature is deleted and it is the origin in a composite relationship, the edited features as a result of this deletion are returned.
 *
 * The `editedFeatures` object returns full features including newly added features, the original features prior to delete, the original and current features for updates.
 *
 * @since 4.20
 */
export interface EditedFeatureResult {
  /** Object containing all edited features belonging to the specified layer. */
  editedFeatures: EditedFeatures;
  /** The layerId of the feature layer where features were edited. */
  layerId: number;
}

export interface EditedFeatures {
  /** Newly added features as a result of editing a feature that participates in a composite relationship. */
  adds: Graphic[];
  /** Deleted features as a result of editing a feature that participates in a composite relationship. */
  deletes: Graphic[];
  /** Edited features are returned in the spatial reference of the feature service. */
  spatialReference?: SpatialReference | null;
  /** Object containing original and updated features as a result of editing a feature that participates in a composite relationship. */
  updates: EditedFeaturesUpdate[];
}

export interface EditedFeaturesUpdate {
  /** Updated feature as a result of editing a feature that participates in a composite relationship. */
  current: Graphic;
  /** Original feature before the update took place. */
  original: Graphic;
}

export interface EditsResultEvent {
  /** An array of successfully added attachments. */
  addedAttachments: FeatureEditResult[];
  /** An array of successfully added features. */
  addedFeatures: FeatureEditResult[];
  /** An array of successfully deleted attachments. */
  deletedAttachments: FeatureEditResult[];
  /** An array of successfully deleted features. */
  deletedFeatures: FeatureEditResult[];
  /**
   * Edited features as result of editing a feature that participates in composite relationships in a database.
   * This parameter is returned only when the `returnServiceEditsOption` parameter of the [FeatureLayer#applyEdits()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#applyEdits) method is set to `original-and-current-features`. This parameter was added at 4.20.
   */
  editedFeatures?: EditedFeatureResult[];
  /** Returns `true` when the number of records returned exceeds the maximum number configured on the service. */
  exceededTransferLimit: boolean;
  /** An array of successfully updated attachments. */
  updatedAttachments: FeatureEditResult[];
  /** An array of successfully updated features. */
  updatedFeatures: FeatureEditResult[];
}