import type FeatureLayer from "../../layers/FeatureLayer.js";
import type SubtypeGroupLayer from "../../layers/SubtypeGroupLayer.js";
import type FeatureService from "./FeatureService.js";
import type { CurveType } from "../../geometry/types.js";
import type { FeatureLayerCapabilities } from "../../layers/types.js";
import type { EditedFeatures, Edits, FeatureEditResult } from "../../layers/graphics/editingSupport.js";
import type { AssociationJsonType } from "../../networks/support/typeUtils.js";
import type { GeometryType } from "../support/types.js";

export interface DataCapabilities {
  /** Indicates if the feature service is branch versioned. */
  isDataBranchVersioned: boolean;
  /** Indicates if the feature service is versioned. */
  isDataVersioned: boolean;
  /** The types of true curves the service supports. */
  supportedCurveTypes: readonly CurveType[];
  /** Indicates if the service supports geometries with true curves. */
  supportsTrueCurve: boolean;
}

export interface SyncDataOptions {
  /** Indicates whether the full annotation model should be included in the sync data. */
  annotationFullModel: boolean;
  /** Indicates whether annotations should be included in the sync data. */
  annotations: boolean;
  /** Indicates whether attribute rules should be included in the sync data. */
  attributeRules: boolean;
  /** Indicates whether contingent values should be included in the sync data. */
  contingentValues: boolean;
  /** Indicates whether dimensions should be included in the sync data. */
  dimensions: boolean;
  /** Indicates whether 3D objects should be included in the sync data. */
  include3DObjects: boolean;
  /** Indicates whether true curves should be preserved in the sync data. */
  preserveTrueCurves: boolean;
  /** Indicates whether missing utility network layers should be included in the sync data. */
  utilityNetworkMissingLayers: boolean;
  /** Indicates whether the utility network system should be included in the sync data. */
  utilityNetworkSystem: boolean;
}

export interface SyncCapabilities {
  /** Indicates the supported sync data options for the service */
  supportedSyncDataOptions: SyncDataOptions;
  /** Indicates whether the service supports asynchronous synchronization. */
  supportsAsync: boolean;
}

export interface OperationCapabilities {
  /** Indicates if new features can be [added](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-featureService-FeatureService.html#applyEdits) to the service. */
  supportsAdd: boolean;
  /** Indicates whether the service supports change tracking for features. */
  supportsChangeTracking: boolean;
  /** Indicates if features can be [deleted](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-featureService-FeatureService.html#applyEdits) from the service. */
  supportsDelete: boolean;
  /**
   * Indicates if features in the service can be [edited](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-featureService-FeatureService.html#applyEdits).
   *  Use `supportsAdd`, `supportsUpdate` and `supportsDelete` to determine which editing operations are supported.
   */
  supportsEditing: boolean;
  /** Indicates if features in the service can be {@link rest/featureService/FeatureService#queryFeatures queried}. */
  supportsQuery: boolean;
  /** Indicates whether the service supports querying contingent values. */
  supportsQueryContingentValues: boolean;
  /** Indicates whether the service supports querying data elements. */
  supportsQueryDataElements: boolean;
  /** Indicates whether the service supports querying domains. */
  supportsQueryDomains: boolean;
  /** Indicates whether the service supports synchronization. */
  supportsSync: boolean;
  /** Indicates whether the service supports updating features. */
  supportsUpdate: boolean;
}

export interface QueryCapabilities {
  /** The maximum number of records that will be returned for a given query. */
  maxRecordCount?: number | null;
  /** This property is used to limit the number of records returned based on a factor of the `query.maxRecordCount`. */
  maxRecordCountFactor?: number | null;
}

export interface EditingCapabilities {
  /** Indicates if the service supports async apply edits. */
  supportsAsyncApplyEdits: boolean;
  /** Indicates if the `globalId` values provided by the client are used in [FeatureService#applyEdits()](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-featureService-FeatureService.html#applyEdits). */
  supportsGlobalId: boolean;
  /** Indicates that the service supports returning edits in the source spatial reference of the layer they belong to. */
  supportsReturnServiceEditsInSourceSpatialReference: boolean;
  /** Indicates whether the service supports splitting features. */
  supportsSplit: boolean;
  /** Indicates if feature geometries containing true curves can be updated. */
  supportsTrueCurveUpdate: boolean;
  /**
   * Indicates whether updates to true curves should be restricted to clients
   * that support true editing. If `true`, `applyEdits` calls that update true
   * curves must have the `trueCurveClient` parameter set to `true`.
   */
  supportsTrueCurveUpdateByTrueCurveClientsOnly: boolean;
}

/**
 * Describes the layer's supported capabilities.
 *
 * @since 4.28
 */
export interface ServiceCapabilities {
  /** Describes characteristics of the data in the service. */
  data: DataCapabilities;
  /** Describes editing capabilities that can be performed on the features in the layer via [FeatureService#applyEdits()](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-featureService-FeatureService.html#applyEdits). */
  editing: EditingCapabilities;
  /** Describes operations that can be performed on features in the layer. */
  operations: OperationCapabilities;
  /**
   * Describes [Query](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-Query.html)
   * operations that can be performed on the Feature Service.
   */
  query: QueryCapabilities;
  /** Describes the synchronization capabilities of a service. */
  sync: SyncCapabilities;
}

/**
 * Results returned from the {@link rest/featureService/interfaces#createFeatureService} method.
 *
 * @since 4.28
 */
export interface FeatureServiceResourcesBundle {
  /** Returns a featureService object. */
  featureService: FeatureService;
  /** An array of [FeatureLayers](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html) */
  layers: (FeatureLayer | SubtypeGroupLayer)[];
}

/**
 * Contains information for a table in the Feature Service.
 *
 * @since 4.30
 */
export interface TableInfo {
  /** The id of a table. */
  id: number;
  /** The name of a table. */
  name: string;
  /** The type of the table. */
  type: string;
  /** The service url */
  url: string;
}

/**
 * Contains information for a layer in the Feature Service.
 *
 * @since 4.30
 */
export interface LayerInfo extends TableInfo {
  /** The geometry type of a layer. */
  geometryType?: GeometryType;
}

/**
 * Contains information for a layer in the Feature Service.
 *
 * @since 4.30
 */
export interface LayerDefinition extends LayerInfo {
  /** The capabilities of the layer. */
  capabilities: FeatureLayerCapabilities;
}

/**
 * Contains information for a table in the Feature Service.
 *
 * @since 4.30
 */
export interface TableDefinition extends TableInfo {
  /** The capabilities of the table. */
  capabilities: FeatureLayerCapabilities;
}

/**
 * Contains information returned from the service containing layerInfos, tableInfos, and server capabilities.
 *
 * @since 4.30
 */
export interface ServiceContents {
  /** Contains the Layer Definition of a layer. */
  layers: LayerDefinition[];
  /** Contains the Table Definition of a layer. */
  tables: TableDefinition[];
}

/**
 * Specifies a traditional or foreign-key association to delete.
 * To delete a foreign-key association, `globalId` must be the null GUID `{00000000-0000-0000-0000-000000000000}`.
 *
 * @beta
 * @since 4.34
 */
export interface DeleteAssociationEdit {
  /** The type of association. */
  associationType: AssociationJsonType;
  /** The global ID of the "from" feature. */
  fromGlobalId: string;
  /** The source ID of the "from" feature. */
  fromSourceId: number;
  /** The terminal ID of the "from" feature. */
  fromTerminalId: number;
  /** The global ID of the association to delete. */
  globalId: string;
  /** The global ID of the "to" feature. */
  toGlobalId: string;
  /** The source ID of the "to" feature. */
  toSourceId: number;
  /** The terminal ID of the "to" feature. */
  toTerminalId: number;
}

/**
 * Specifies grouped objects to combine in a telecom domain network.
 *
 * @beta
 * @since 4.34
 */
export interface CombineGroupedObjectsEdit {
  /**
   * The global IDs of the grouped features to combine.
   *   The objects being combined must have consecutive unit IDs, exist in the same table and unit container,
   *   and have the same attribute values.
   */
  globalIds: string[];
}

/**
 * Specifies a grouped object to divide, and the units to divide the object into.
 *
 * @beta
 * @since 4.34
 */
export interface DivideGroupedObjectsEdit {
  /** The global ID of the grouped object to divide. */
  globalId: string;
  /**
   * The units to divide the grouped object into.
   *   Each number in this array describes the number of units to assign an object resulting from the division.
   *   E.g., to divide one object with 6 units into an object with 5 units and another object with 1 unit, specify `[5, 1]`.
   */
  numUnits: number[];
}

/**
 * Results returned from the [UtilityNetwork#generateAddAssociations()](https://developers.arcgis.com/javascript/latest/api-reference/esri-networks-UtilityNetwork.html#generateAddAssociations), [UtilityNetwork#generateDeleteAssociations()](https://developers.arcgis.com/javascript/latest/api-reference/esri-networks-UtilityNetwork.html#generateDeleteAssociations),
 * [UtilityNetwork#generateCombineNetworkElements()](https://developers.arcgis.com/javascript/latest/api-reference/esri-networks-UtilityNetwork.html#generateCombineNetworkElements), and [UtilityNetwork#generateDivideNetworkElements()](https://developers.arcgis.com/javascript/latest/api-reference/esri-networks-UtilityNetwork.html#generateDivideNetworkElements).
 *
 * @since 4.29
 */
export interface ServiceEdits extends Edits {
  /**
   * An array of objects specifying
   *   grouped objects to combine in a telecom domain network. The objects being combined must have consecutive unit IDs, exist in the same table and unit container,
   *   and have the same attribute values.
   */
  combineGroupedObjects?: CombineGroupedObjectsEdit[];
  /**
   * An array of objects specifying
   *   traditional or foreign-key associations to delete. To delete associations with `deleteAssociations`, all "from" and "to" properties must
   *   be specified in the [DeleteAssociationEdit](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-featureService-types.html#DeleteAssociationEdit) object.
   */
  deleteAssociations?: DeleteAssociationEdit[];
  /**
   * An array of objects specifying
   *   a grouped objects to divide, and the units to divide the objects into, in a telecom domain network.
   */
  divideGroupedObjects?: DivideGroupedObjectsEdit[];
  /** The layerId of the feature layer. */
  id: number;
  /** Identifier for globalId and objectId. */
  identifierFields?: ServiceEditsIdentifierFields;
}

export interface ServiceEditsIdentifierFields {
  /** The globalId identifier. */
  globalIdField?: string | null;
  /** The objectId identifier. */
  objectIdField: string;
}

export interface ServiceEditOptions {
  /**
   * The geodatabase version to apply the edits. This parameter applies only if the
   * {@link rest/featureService/interfaces#capabilities capabilities.data.isDataVersioned} property of the layer is `true`.
   */
  gdbVersion?: string;
  /**
   * Indicates whether the edits can be applied using globalIds of features or attachments.
   * This parameter applies only if the layer's {@link rest/featureService/interfaces#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`.
   */
  globalIdUsed?: boolean;
  /**
   * Added at 10.5 and works with ArcGIS Server services only.
   * This parameter specifies whether to apply edits in the order they are submitted in the JSON.
   * If true, edits will apply in the submitted order. If false, which is the default, edits will apply in ascending layer-ID order.
   * All the edits for the layer with the lowest ID will apply first.
   */
  honorSequenceOfEdits?: boolean;
  /**
   * Indicates to the server whether the client is true curve capable.
   * This parameter should be set to `true` when submitting edits with geometries containing true curves for non-hosted feature services referencing an enterprise geodatabase.
   * By default, this parameter is set to false.
   */
  trueCurveClient?: boolean;
  /**
   * This option was added at 10.6. This is set by a client during long transaction editing on a branch version.
   * This parameter is used to apply the edits with the same edit moment as the previous set of edits.
   * This allows an editor to apply a single block of edits partially, complete another task, and then complete the block of edits.
   *  When set to true, the edits are applied with the same edit moment as the previous sets of edits.
   * When set to false the edits are applied with a new edit moment. By default, this parameter is set to false.
   */
  usePreviousEditMoment?: boolean;
}

/**
 * Represents the result of dividing a grouped object in a telecom domain network.
 *
 * @beta
 * @since 4.34
 */
export interface DivideGroupedObjectResult {
  /**
   * If the divided object was a junction object that was directly connected
   *   to one or more edge objects prior to the division, the edge objects are also divided to maintain connectivity.
   *   This array contains GUIDs of any edge objects that are connected to the divided junction objects,
   *   including those that were connected prior to the division.
   */
  connectedEdgeGlobalIds: string[];
  /**
   * The global IDs of objects resulting from the division, including the original object.
   *   I.e., the GUIDs that the target object was divided into.
   */
  dividedGlobalIds: string[];
  /** Global ID of the original object before division. */
  globalId: string;
}

/**
 * Represents the result of combining one or more grouped objects in a telecom domain network.
 *
 * @beta
 * @since 4.34
 */
export interface CombineGroupedObjectResult {
  /** The global ID of the object resulting from the combine operation. */
  combinedGlobalId: string;
  /** The global IDs of the objects deleted to perform the combination. */
  deletedGlobalIds: string[];
}

/**
 * Results returned from the [FeatureService#applyEdits()](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-featureService-FeatureService.html#applyEdits) method. It contains features
 * that were added, deleted or updated in different feature layers. It also contains the edit moment.
 *
 * @since 4.28
 */
export interface ServiceEditsResult {
  /** Results returned from an add attachments operation. */
  addAttachmentResults: FeatureEditResult[];
  /** Results returned from an add operation. */
  addFeatureResults: FeatureEditResult[];
  /** Results returned from a combine operation. */
  combineGroupedObjectResults?: CombineGroupedObjectResult[];
  /** Results returned from a delete attachments operation. */
  deleteAttachmentResults: FeatureEditResult[];
  /** Results returned from a delete operation. */
  deleteFeatureResults: FeatureEditResult[];
  /** Results returned from a divide operation. */
  divideGroupedObjectResults?: DivideGroupedObjectResult[];
  /** Object containing all edited features belonging to the specified layer. */
  editedFeatures?: EditedFeatures;
  /** Returns the editMoment of an edit. */
  editMoment?: number;
  /** The layerId of the feature layer where features were edited. */
  id: number;
  /** Results returned from a split operation. */
  splitsResults?: SplitsResult[];
  /** Results returned from an update attachments operation. */
  updateAttachmentResults: FeatureEditResult[];
  /** Results returned from an update operation. */
  updateFeatureResults: FeatureEditResult[];
}

/** Represents the result of a split operation. */
export interface SplitsResult {
  /** The object ID of the feature that was split. */
  objectId: number;
  /** The object ID of the original feature and the feature(s) created from the split. */
  results: number[];
}