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.
 *
 * @since 5.0
 */
export interface FeatureIdentifier {
  /**
   * The objectId of the feature or the attachmentId of the attachment that was added/edited/deleted.
   *
   * @since 5.0
   */
  objectId?: number | null;
  /**
   * The globalId of the feature or the attachment that was edited.
   *
   * @since 5.0
   */
  globalId?: string | null;
}

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

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

/** @since 5.0 */
export interface Attachment {
  /**
   * 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.
   *
   * @since 5.0
   */
  globalId: string;
  /**
   * The name of the attachment. This parameter must be set if the attachment type is `Blob`.
   *
   * @since 5.0
   */
  name?: string;
  /**
   * 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.
   *
   * @since 5.0
   */
  contentType?: string;
  /**
   * The id of pre-loaded attachment.
   *
   * @since 5.0
   */
  uploadId?: string;
  /**
   * The attachment data.
   *
   * @since 5.0
   */
  data?: Blob | File | string;
}

/** @since 5.0 */
export interface EditOptions {
  /**
   * The geodatabase version to apply the edits. This parameter applies only if the
   * [capabilities.data.isVersioned](https://developers.arcgis.com/javascript/latest/references/core/layers/types/#EditingCapabilities) property of the layer is `true`. If the gdbVersion parameter is not specified, edits are made to the published map's version.
   *
   * @since 5.0
   */
  gdbVersion?: string;
  /**
   * 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 [capabilities.editing.supportsRollbackOnFailure](https://developers.arcgis.com/javascript/latest/references/core/layers/types/#EditingCapabilities) 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.
   *
   * @since 5.0
   */
  rollbackOnFailureEnabled?: boolean;
  /**
   * Indicates whether the edits can be applied using globalIds of features or attachments.
   * This parameter applies only if the layer's [capabilities.editing.supportsGlobalId](https://developers.arcgis.com/javascript/latest/references/core/layers/types/#EditingCapabilities) 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);
   *   });
   * }
   * ```
   *
   * @since 5.0
   */
  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.
   *
   * @since 5.0
   */
  returnEditMoment?: boolean;
  /**
   * If set to `original-and-current-features`, the [EditedFeatureResult](https://developers.arcgis.com/javascript/latest/references/core/editing/types/#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.
   *
   * @since 5.0
   */
  returnServiceEditsOption?: "none" | "original-and-current-features";
  /**
   * Attests that the edits were prepared in a way that avoids the loss of true curve geometries. An app should only set this parameter to `true` if it can
   * guarantee that the entire client-side chain of custody handles true curves properly.
   *
   * Setting this parameter is only necessary when the feature service is configured to restrict updating of true curve geometries to clients that
   * explicitly indicate support for true curve editing.
   *
   * Considerations for curve-aware editing include:
   *   - Prior to editing, the feature must be obtained from the service with its true curves intact, e.g. by a query with the [Query.returnTrueCurves](https://developers.arcgis.com/javascript/latest/references/core/rest/support/Query/#returnTrueCurves) parameter set to `true`.
   *   - Ensure all spatial operations performed on the geometry preserve curves — i.e., avoids densifying curves into linear approximations.
   *   - Beware of code that references a geometry's [Polygon.rings](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#rings) or [Polyline.paths](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polyline/#paths) properties directly. Curve-aware iteration over the vertices of a geometry requires a conditional logic whereby [Polygon.curveRings](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#curveRings) or [Polyline.curvePaths](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polyline/#curvePaths) are used when defined, and [Polygon.rings](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polygon/#rings) or [Polyline.paths](https://developers.arcgis.com/javascript/latest/references/core/geometry/Polyline/#paths) are used otherwise.
   */
  editsRespectTrueCurves?: boolean;
}

/** @since 5.0 */
export interface Edits {
  /**
   * An array or a
   * [Collection](https://developers.arcgis.com/javascript/latest/references/core/core/Collection/) of features to be added. Feature geometries must be of type
   * [Mesh](https://developers.arcgis.com/javascript/latest/references/core/geometry/Mesh/). 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.
   *
   * @since 5.0
   */
  addFeatures?: Graphic[] | Collection<Graphic>;
  /**
   * An array or a
   * [Collection](https://developers.arcgis.com/javascript/latest/references/core/core/Collection/) of features to be updated. Each feature must have a valid
   * [objectId](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#objectIdField) or [globalId](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#globalIdField) for attribute updates, while geometry updates must always use a valid
   * [globalId](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#globalIdField). 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.
   *
   * @since 5.0
   */
  updateFeatures?: Graphic[] | Collection<Graphic>;
  /**
   * An array or a
   * [Collection](https://developers.arcgis.com/javascript/latest/references/core/core/Collection/) of features, or an array of objects with [globalId](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#globalIdField) of each feature to be deleted.
   * When an array or collection of features is passed, each feature must have a valid [globalId](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#globalIdField). When an array of objects is used,
   * each object must have a valid value set for the [globalId](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#globalIdField) property.
   *
   * @since 5.0
   */
  deleteFeatures?: Graphic[] | Collection<Graphic> | FeatureIdentifier[];
  /**
   * 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/references/core/editing/types/#AttachmentEdit)s for all attachments to be added.
   *
   * @since 5.0
   */
  addAttachments?: AttachmentEdit[];
  /**
   * 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/references/core/editing/types/#AttachmentEdit)s for all attachments to be updated.
   *
   * @since 5.0
   */
  updateAttachments?: AttachmentEdit[];
  /**
   * An array of [globalId](https://developers.arcgis.com/javascript/latest/references/core/editing/types/#AttachmentEdit)s for attachments to be deleted. Applies only when the `options.globalIdUsed`
   * parameter is set to `true`.
   *
   * @since 5.0
   */
  deleteAttachments?: string[];
}

/**
 * Results returned from the [FeatureLayer.applyEdits()](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#applyEdits) method.
 *
 * @since 5.0
 */
export interface EditsResult {
  /**
   * Result of adding features.
   *
   * @since 5.0
   */
  addFeatureResults: FeatureEditResult[];
  /**
   * Result of updating features.
   *
   * @since 5.0
   */
  updateFeatureResults: FeatureEditResult[];
  /**
   * Result of deleting features.
   *
   * @since 5.0
   */
  deleteFeatureResults: FeatureEditResult[];
  /**
   * Result of adding attachments.
   *
   * @since 5.0
   */
  addAttachmentResults: FeatureEditResult[];
  /**
   * Result of updating attachments.
   *
   * @since 5.0
   */
  updateAttachmentResults: FeatureEditResult[];
  /**
   * Result of deleting attachments.
   *
   * @since 5.0
   */
  deleteAttachmentResults: 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/references/core/layers/FeatureLayer/#applyEdits) method is set to `original-and-current-features`. This parameter was added at 4.20.
   *
   * @since 5.0
   */
  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/references/core/layers/FeatureLayer/#applyEdits) method is set to `true`. This option was added at the version 4.20.
   *
   * @since 5.0
   */
  editMoment?: number;
}

/**
 * Results returned from the [FeatureLayer.applyEdits()](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#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 {
  /**
   * The layerId of the feature layer where features were edited.
   *
   * @since 5.0
   */
  layerId: number;
  /**
   * Object containing all edited features belonging to the specified layer.
   *
   * @since 5.0
   */
  editedFeatures: EditedFeatures;
}

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

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

/** @since 5.0 */
export interface EditsResultEvent {
  /**
   * An array of successfully added features.
   *
   * @since 5.0
   */
  addedFeatures: FeatureEditResult[];
  /**
   * An array of successfully updated features.
   *
   * @since 5.0
   */
  updatedFeatures: FeatureEditResult[];
  /**
   * An array of successfully deleted features.
   *
   * @since 5.0
   */
  deletedFeatures: FeatureEditResult[];
  /**
   * An array of successfully added attachments.
   *
   * @since 5.0
   */
  addedAttachments: FeatureEditResult[];
  /**
   * An array of successfully updated attachments.
   *
   * @since 5.0
   */
  updatedAttachments: FeatureEditResult[];
  /**
   * An array of successfully deleted attachments.
   *
   * @since 5.0
   */
  deletedAttachments: 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/references/core/layers/FeatureLayer/#applyEdits) method is set to `original-and-current-features`. This parameter was added at 4.20.
   *
   * @since 5.0
   */
  editedFeatures?: EditedFeatureResult[];
  /**
   * Returns `true` when the number of records returned exceeds the maximum number configured on the service.
   *
   * @since 5.0
   */
  exceededTransferLimit: boolean;
}