import type Graphic from "../../Graphic.js";
import type EsriMap from "../../Map.js";
import type Collection from "../../core/Collection.js";
import type UtilityNetworkAssociationType from "../../popup/support/UtilityNetworkAssociationType.js";
import type Association from "../../rest/networks/support/Association.js";
import type AttachmentInfo from "../../rest/query/support/AttachmentInfo.js";
import type AttachmentElementInput from "./inputs/AttachmentElementInput.js";
import type BatchFormInputs from "./inputs/BatchFormInputs.js";
import type FieldInput from "./inputs/FieldInput.js";
import type UtilityNetworkAssociationsElementInput from "./inputs/UtilityNetworkAssociationsElementInput.js";
import type { EventedAccessor } from "../../core/Evented.js";
import type { FieldValue } from "../../layers/support/fieldUtils.js";
import type { TimeZone } from "../../time/types.js";
import type { AssociationsLayerUnion, AttachmentsChangeEvent, AttributeFormSupportedLayerInfo, AttributeFormSupportedLayerUnion, BatchAttributeFormMode, BatchAttributeFormState, PendingAttachmentEdits, SelectedAttachmentIdentifier, SelectedAttachmentDetails, SubmitEvent, ValueChangeEvent, ValuesChangeEvent } from "./types.js";
import type { FormInput } from "./inputs/types.js";
import type { EditType } from "../support/forms/types.js";

export interface BatchAttributeFormViewModelProperties extends Partial<Pick<BatchAttributeFormViewModel, "activeFeatureIndex" | "attachmentsEnabled" | "disabled" | "editType" | "features" | "layerInfos" | "map" | "readOnly" | "selectedAttachment" | "selectedAttachmentAddElementId" | "selectedAttachmentListElementId" | "showingSelectedAttachmentReplacePage" | "submitHasBeenAttempted" | "timeZone" | "userHasChangedValues">> {}

export interface BatchAttributeFormViewModelEvents {
  /**
   * Fires when field values are updated.
   *
   * @deprecated since 5.1. Use `"values-change"` instead, as it provides more comprehensive information about changes to field values.
   * @example
   * form.on("value-change", (event) => {
   *   const result = {};
   *   for (const feature of form.features) {
   *     const value = form.viewModel.getValues(feature)[event.fieldName];
   *     result[feature.getObjectId()] = typeof value === "number" ? new Date(value).toISOString() : value;
   *   }
   *   console.log(`Values for field ${event.fieldName}`, result);
   * });
   */
  "value-change": ValueChangeEvent;
  /**
   * Fires when field values are updated.
   *
   * @since 5.1
   */
  "values-change": ValuesChangeEvent;
  /**
   * Fires when staged attachment edits are updated.
   *
   * @since 5.1
   */
  "attachments-change": AttachmentsChangeEvent;
  /** Fires when the [BatchAttributeForm.submit()](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/#submit) method is called. */
  submit: SubmitEvent;
}

/**
 * Provides the logic for the [BatchAttributeForm](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/) widget.
 *
 * > [!WARNING]
 * >
 * > **Known Limitations**
 * >
 * >  Currently, [relationship elements](https://developers.arcgis.com/javascript/latest/references/core/form/elements/RelationshipElement/) and [associations elements](https://developers.arcgis.com/javascript/latest/references/core/form/elements/UtilityNetworkAssociationsElement/) are not supported within the [BatchAttributeForm](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/) widget. Users can individually edit related records and association fields using the [FeatureForm](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/) widget.
 *
 * @since 4.33
 * @see [Editor](https://developers.arcgis.com/javascript/latest/references/core/widgets/Editor/)
 * @see [BatchAttributeForm](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/)
 * @example
 * let batchAttributeForm = new BatchAttributeForm({
 *   viewModel: { // Autocasts as new BatchAttributeFormViewModel()
 *     map: map, // Required if using Arcade expressions that use the global $map variable
 *     features: featureCollection,
 *   },
 *   container: "formDiv"
 * });
 */
export default class BatchAttributeFormViewModel extends EventedAccessor {
  /**
   * @deprecated
   * Do not directly reference this property.
   * Use EventNames and EventTypes helpers from \@arcgis/core/Evented
   */
  "@eventTypes": BatchAttributeFormViewModelEvents;
  constructor(properties?: BatchAttributeFormViewModelProperties);
  /** The active feature in the form, which is the feature that is currently being edited while in `single`[mode](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#mode). If the `mode` is `batch`, this property will be `null`. */
  get activeFeature(): Graphic | null | undefined;
  /**
   * The index of the active feature that is currently being edited. This will be `-1` when [mode](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#mode) is `batch`.
   * To enter `single` mode, set this property to the index in the [features](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#features) array of the feature you wish to edit individually. To enter `batch` mode, set this property to `-1`.
   *
   * @default -1
   */
  accessor activeFeatureIndex: number;
  /**
   * Returns the [batch attribute form inputs](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/inputs/BatchFormInputs/) that are currently being edited. This is
   * dependent on the form's [mode](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#mode). If the [mode](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#mode) is
   * `batch`, it returns the [shared form](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#sharedForm) inputs. If `single`, it
   * returns the form inputs for the [active feature](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#activeFeature).
   * This property is read-only and should not be modified directly.
   * To change which form is active, see [activeFeatureIndex](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#activeFeatureIndex).
   */
  get activeForm(): BatchFormInputs;
  /**
   * Indicates whether attachment elements should be rendered in the form. Changing this value will cause the form to regenerate
   * and discard any pending edits.
   *
   * @default false
   * @internal
   */
  accessor attachmentsEnabled: boolean;
  /**
   * Indicates whether the form is currently evaluating [Arcade expressions](https://developers.arcgis.com/javascript/latest/arcade/). It returns `true` when the form is evaluating expressions or when it is waiting for a response from the server.
   *
   * @see [Arcade Form Constraint profile](https://developers.arcgis.com/arcade/profiles/form-constraint/)
   * @see [Arcade Form Calculation - profile variables](https://developers.arcgis.com/arcade/profiles/form-calculation/#profile-variables)
   */
  get calculating(): boolean;
  /**
   * Indicates whether the associated view should be in `read-only` mode.
   * When `true`, the associated view should display the form in a read-only
   * mode, disabling any editing.
   *
   * @default false
   */
  accessor disabled: boolean;
  /**
   * Specifies the type of edit operation being performed by the form (for example, add, update, or delete).
   * This property can affect whether certain fields are editable, depending on the layer's capabilities.
   * Its value is also used for the `$editcontext.editType` variable in Arcade expressions.
   * The value "NA" is always permitted when checking if a layer supports the current edit type.
   *
   * @default "NA"
   * @see [Arcade Form Calculation - profile variables](https://developers.arcgis.com/arcade/profiles/form-calculation/#profile-variables)
   */
  accessor editType: EditType;
  /**
   * Indicates whether any of the Arcade expressions in the form have
   * failed to evaluate.
   *
   * @see [Arcade expressions](https://developers.arcgis.com/javascript/latest/arcade/)
   */
  get expressionEvaluationFailed(): boolean;
  /**
   * The set of features whose attributes are being edited by the form.
   * This collection is intended to be immutable: do not add, remove, or reorder features in place, as this may cause unexpected behavior.
   * To update the features, assign a new collection to this property. Doing so will regenerate the form inputs and discard any unsaved changes.
   */
  accessor features: Collection<Graphic>;
  /** Indicates whether the form cannot load because there are too many features whose forms have complex [Arcade](https://developers.arcgis.com/javascript/latest/arcade/) expressions. These are expressions that that involve advanced logic, calculations, or asynchronous operations, such as accessing data from other layers, performing network requests, or using functions that require waiting for a response. These expressions go beyond simple field value lookups or basic calculations. */
  get hasTooManyComplexFeatures(): boolean;
  /** Indicates whether the form has too many features to be loaded. */
  get hasTooManyFeatures(): boolean;
  /**
   * Indicates whether there are any visible inputs in the form.
   * This is `true` if there are any inputs that are visible to the user, meaning they are not hidden by a visibility expression or group visibility expression.
   */
  get hasVisibleInputs(): boolean;
  /** Returns an array of features that are invalid, meaning they have at least one invalid attribute value. */
  get invalidFeatures(): Graphic[];
  /**
   * An array of objects containing a [FormTemplate](https://developers.arcgis.com/javascript/latest/references/core/form/FormTemplate/) and its corresponding layer.
   * This allows per-layer overrides which provides a [FormTemplate](https://developers.arcgis.com/javascript/latest/references/core/form/FormTemplate/) for building batch form templates. The `formTemplate` defined here overrides any `formTemplate` defined on the layer itself. Otherwise, the layer's own `formTemplate` is used if it exists.
   *
   * @example
   * // Query for features
   * const { features } = await featureLayer.queryFeatures();
   *
   * // Initialize BatchAttributeFormViewModel
   * const batchAttributeFormViewModel = new BatchAttributeFormViewModel({
   *   features: features,
   *   layerInfos: [{
   *     layer: featureLayer,
   *     formTemplate:  { // autocasts to FormTemplate
   *       elements: [
   *         { // autocasts to FieldElement
   *           type: "field",
   *           fieldName: "fulladdr",
   *           label: "Full Address"
   *         }
   *       ]
   *     },
   *   }]
   * });
   */
  accessor layerInfos: AttributeFormSupportedLayerInfo[] | null | undefined;
  /**
   * An array of layers included in the batch attribute form. Each layer must support feature editing and field access.
   *
   * The following layer types are supported:
   * - [FeatureLayer](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/)
   * - [GeoJSONLayer](https://developers.arcgis.com/javascript/latest/references/core/layers/GeoJSONLayer/)
   * - [SceneLayer](https://developers.arcgis.com/javascript/latest/references/core/layers/SceneLayer/) (with `featureLayer` property)
   * - [SubtypeSublayer](https://developers.arcgis.com/javascript/latest/references/core/layers/support/SubtypeSublayer/)
   * - [OrientedImageryLayer](https://developers.arcgis.com/javascript/latest/references/core/layers/OrientedImageryLayer/)
   * - [KnowledgeGraphSublayer](https://developers.arcgis.com/javascript/latest/references/core/layers/knowledgeGraph/KnowledgeGraphSublayer/)
   */
  get layers(): AttributeFormSupportedLayerUnion[];
  /**
   * A reference to the associated [Map](https://developers.arcgis.com/javascript/latest/references/core/Map/).
   *
   * > [!WARNING]
   * >
   * > This property is required if working with [Arcade expressions](https://developers.arcgis.com/javascript/latest/arcade/) in the `BatchAttributeForm` that make use of the `$map` global variable.
   */
  accessor map: EsriMap | null | undefined;
  /**
   * The mode of the form, which can be either `"single"` or `"batch"`.
   * In `"single"` mode, the form is editing a single feature at a time, while in
   * `"batch"` mode, it is editing all of the features in the [features](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#features) collection. To change the mode, set the [activeFeatureIndex](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#activeFeatureIndex) property.
   */
  get mode(): BatchAttributeFormMode;
  /**
   * Indicates whether the form is in a read-only state. When `true`, the user
   *  cannot modify any attribute values in the form. This is different from
   *  [disabled](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#disabled), which indicates whether the entire form is
   *  disabled, including any UI controls.
   *
   * @default false
   */
  accessor readOnly: boolean;
  /**
   * A reference to the current selected attachment. This is used to determine what attachment details to show in the "viewing-attachment-details" page.
   *
   * @internal
   */
  accessor selectedAttachment: SelectedAttachmentIdentifier | null;
  /** @internal */
  accessor selectedAttachmentAddElementId: string | null;
  /** @internal */
  get selectedAttachmentAddInput(): AttachmentElementInput | null;
  /**
   * Details about the selected attachment. These details are used to drive the "viewing-attachment-details" page.
   *
   * @internal
   */
  get selectedAttachmentDetails(): SelectedAttachmentDetails | null;
  /** @internal */
  accessor selectedAttachmentListElementId: string | null;
  /** @internal */
  get selectedAttachmentListInput(): AttachmentElementInput | null;
  /** The set of form inputs used in `batch` editing [mode](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#mode). */
  get sharedForm(): BatchFormInputs;
  /** @internal */
  accessor showingSelectedAttachmentReplacePage: boolean;
  /**
   * Indicates whether the view model is ready to be used. Upon construction,
   * and then again any time the `features` Collection is modified or
   * re-assigned, the view model must perform certain asynchronous tasks in
   * order to compile the shared attribute form. Use the `status` property to
   * determine when this processing occurs.
   *
   * Value | Description
   * ------|------------
   * not-loaded | The [features](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#features) property has not been initialized or is an empty [Collection](https://developers.arcgis.com/javascript/latest/references/core/core/Collection/).
   * loading | The [features](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#features) property has been set or modified, and the view model is compiling the shared form. Certain properties, like [activeForm](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#activeForm), should not be accessed yet.
   * loaded | The view model is ready to use.
   * failed | An error occurred while compiling the shared form.
   */
  get status(): BatchAttributeFormState;
  /**
   * Indicates whether the user has attempted to submit the form.
   *
   * @default false
   */
  accessor submitHasBeenAttempted: boolean;
  /**
   * The time zone used to interpret date values in the form. If this property is `null` or set to `"unknown"`, date values are displayed in the time zone specified by the layer's [FeatureLayer.preferredTimeZone](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#preferredTimeZone). If none is provided there, date values are interpreted in UTC.
   *
   * @see [FeatureLayer.preferredTimeZone](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#preferredTimeZone)
   * @see [View2D.timeZone](https://developers.arcgis.com/javascript/latest/references/core/views/View2D/#timeZone)
   */
  accessor timeZone: TimeZone | null | undefined;
  /**
   * Indicates whether the form is updating or calculating expressions.
   *
   * @see [Arcade expressions](https://developers.arcgis.com/javascript/latest/arcade/).
   */
  get updating(): boolean;
  /**
   * Indicates whether any values, for any features, have been modified by the
   * user. This property does not take into account changes to attribute values
   * that resulted from [calculated value expressions](https://developers.arcgis.com/javascript/latest/references/core/form/elements/FieldElement/#valueExpression).
   *
   * @default false
   */
  accessor userHasChangedValues: boolean;
  /** Returns true if all fields for all features in the form are valid, with no validation errors present. */
  get valid(): boolean;
  /**
   * An array of inputs in the active form that are currently visible to the user.
   * This array includes only those inputs that are not hidden by visibility settings or expressions.
   */
  get visibleInputs(): FormInput[];
  /**
   * Applies any pending edits from a previous workflow, in cases the working features have changed.
   *
   * @param edits - The pending edits to apply to the attachment inputs.
   * @internal
   */
  applyStashedAttachmentEdits(edits: PendingAttachmentEdits[]): void;
  /**
   * @param elementId - Attachment element identifier.
   * @internal
   */
  clearAttachmentChanges(elementId: string): void;
  /**
   * Finds the field input with the specified `elementId` in the active form.
   *
   * @param elementId - The elementId of the input field to find.
   * @returns Returns an instance of the FieldInput.
   */
  findFieldInput(elementId: string | null | undefined): FieldInput | null | undefined;
  /**
   * Gets the updated value for the specified field in the active form.
   *
   * @param elementId - The id of the target field where the value is accessed.
   * @returns Returns the associated field value, or `undefined` if there is no matching attribute.
   */
  getFieldInputValue(elementId: string): FieldValue | undefined;
  /**
   * Gets the updated attribute values for the specified feature.
   *
   * @param feature
   * @returns The current attribute values for the given feature.
   */
  getValues(feature: Graphic): Graphic["attributes"];
  /**
   * Method for navigating back from nested pages. If there are no nested pages, this method does nothing.
   * NOTE: Method is async for future proofing.
   *
   * @internal
   */
  goBack(): Promise<void>;
  /**
   * Reset all nested views. This is similar to calling 'back()' on all the potentially nested views.
   *
   * @internal
   */
  goHome(): Promise<void>;
  /**
   * @param elementId - Attachment element identifier.
   * @param key - Global identifier of the attachment whose staged changes should be cleared.
   * @internal
   */
  resetAttachmentChanges(elementId: string, key: string): void;
  /**
   * Used to indicate that the current feature being edited is part of an association.
   * This is determined outside of BatchAttributeForm, typically used in nested Editor workflows to
   * determine capabilities for the nested workflow e.g. can the association be deleted.
   *
   * @param association - A reference to association the current feature belongs to.
   * @internal
   */
  setActiveAssociation(association: Association | null | undefined): void;
  /**
   * Used to specify a specific input to view associated layers for.
   * Related to the 'viewing-associated-layers' page.
   *
   * @param input - A reference to the input to display associated layers for.
   * @internal
   */
  setActiveAssociationsInput(input: UtilityNetworkAssociationsElementInput | null | undefined): void;
  /**
   * Used to specify a layer to view associated features for.
   * Related to the 'viewing-associated-features' page.
   *
   * @param layer - A reference to a specific layer.
   * @internal
   */
  setActiveAssociationsLayer(layer: AssociationsLayerUnion | null | undefined): void;
  /**
   * Used to filter the types of associations displayed in the 'viewing-associated-layers' page.
   *
   * @param type - A reference to a specific type of association.
   * @internal
   */
  setActiveAssociationType(type: UtilityNetworkAssociationType | null | undefined): void;
  /**
   * @param value - Selected attachment identifier, or `null`/`undefined` to clear the selection.
   * @internal
   */
  setSelectedAttachment(value: SelectedAttachmentIdentifier | null | undefined): void;
  /**
   * @param elementId - Attachment element identifier, or `null`/`undefined` to clear the selection.
   * @internal
   */
  setSelectedAttachmentAddElementId(elementId: string | null | undefined): void;
  /**
   * @param elementId - Attachment element identifier, or `null`/`undefined` to clear the selection.
   * @internal
   */
  setSelectedAttachmentListElementId(elementId: string | null | undefined): void;
  /**
   * @param value - Whether the selected attachment replace page should be shown.
   * @internal
   */
  setShowingSelectedAttachmentReplacePage(value: boolean): void;
  /**
   * Assigns a value to a field using the view model's mutation and event pipeline.
   *
   * @param fieldName - The name of the field to update.
   * @param value - The value to assign.
   * @param features - The features whose field values should be updated.
   * @returns A promise that resolves after mutation, validity reconciliation, and event emission complete.
   * @internal
   */
  setValueForFieldName(fieldName: string, value: FieldValue, features: Iterable<Graphic>): Promise<void>;
  /**
   * Assigns per-feature values to a field using the view model's mutation and event pipeline.
   *
   * @param fieldName - The name of the field to update.
   * @param values - The values to assign, keyed by feature.
   * @returns A promise that resolves after mutation, validity reconciliation, and event emission complete.
   * @internal
   */
  setValuePerFeatureForFieldName(fieldName: string, values: Map<Graphic, FieldValue>): Promise<void>;
  /**
   * @param elementId - Attachment element identifier.
   * @param files - Files to stage for addition.
   * @returns The attachments staged for addition.
   * @internal
   */
  stageAddAttachments(elementId: string, files: (Blob | File)[]): Promise<AttachmentInfo[]>;
  /**
   * @param elementId - Attachment element identifier.
   * @param key - A key representing the attachment to stage for deletion. This is either a GlobalID, an ID, or the name of the attachment.
   * @internal
   */
  stageDeleteAttachment(elementId: string, key: string): void;
  /**
   * @param elementId - Attachment element identifier.
   * @param key - Internal identifier of the attachment to rename.
   * @param name - New attachment name.
   * @internal
   */
  stageRenameAttachment(elementId: string, key: string, name: string): Promise<void>;
  /**
   * @param elementId - Attachment element identifier.
   * @param key - Internal identifier of the attachment to replace.
   * @param file - File contents for the replacement attachment.
   * @internal
   */
  stageReplaceAttachment(elementId: string, key: string, file: Blob | File): Promise<boolean>;
  /**
   * This method triggers the `"submit"` event, and causes [submitHasBeenAttempted](https://developers.arcgis.com/javascript/latest/references/core/widgets/BatchAttributeForm/BatchAttributeFormViewModel/#submitHasBeenAttempted) to become `true`. It does _not_ do anything to persist the updated values. It is up to the client to verify if these values are appropriate. For example, calling [FeatureLayer.applyEdits()](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#applyEdits) method to update the features' attributes.
   *
   * @returns Resolves after any pending attachment hydration completes and the `"submit"` event has been emitted.
   * @example
   * // Listen for when 'submit' is called on the form.
   * // Once it is fired, update the feature.
   * form.on("submit", updateFeatures);
   * // When the DOM's button (btnUpdate) is clicked,
   * // execute the 'submit()' method.
   * document.getElementById("btnUpdate").addEventListener("click", () => form.submit());
   */
  submit(): Promise<void>;
  /**
   * Determines if user input has caused the given feature to become invalid. Only returns `true` if _all_ of the following three conditions are true of the given feature:
   *  1. It had no validation errors at the start of the editing session.
   *  2. It currently has validation errors.
   *  3. At least one of the validation errors was caused by user input, as opposed to a [value expression](https://developers.arcgis.com/javascript/latest/references/core/form/elements/FieldElement/#valueExpression).
   *
   * @param feature
   */
  userChangesHaveMadeFeatureInvalid(feature: Graphic): boolean;
}