import type Graphic from "../../Graphic.js";
import type EsriMap from "../../Map.js";
import type FormTemplate from "../../form/FormTemplate.js";
import type FeatureLayer from "../../layers/FeatureLayer.js";
import type Association from "../../rest/networks/support/Association.js";
import type FieldInput from "./FieldInput.js";
import type UtilityNetworkAssociationInput from "./UtilityNetworkAssociationInput.js";
import type { EventedAccessor } from "../../core/Evented.js";
import type { EsriPromiseMixin } from "../../core/Promise.js";
import type { FormTemplateProperties } from "../../form/FormTemplate.js";
import type { FieldValue } from "../../layers/support/fieldUtils.js";
import type { TimeZone } from "../../time/types.js";
import type { Attributes, FeatureFormLayerUnion, FeatureFormSupportedInput } from "./types.js";
import type { EditType } from "../support/forms/types.js";
import type { AssociationProperties } from "../../rest/networks/support/Association.js";

export interface FeatureFormViewModelProperties extends Partial<Pick<FeatureFormViewModel, "editType" | "feature" | "layer" | "map">> {
  /**
   * The [Association][Association](https://developers.arcgis.com/javascript/latest/references/core/rest/networks/support/Association/) that the selected [feature](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/FeatureFormViewModel/#feature) is involved in.
   * The association is provided by the [activeAssociationInput](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/FeatureFormViewModel/#activeAssociationInput) of the previous Editor workflow.
   *
   * This property is null until the user has drilled into an associated feature in the Editor widget.
   */
  activeAssociation?: AssociationProperties | null;
  /**
   * The associated [template](https://developers.arcgis.com/javascript/latest/references/core/form/FormTemplate/) used for the form.
   *
   * The [FormTemplate](https://developers.arcgis.com/javascript/latest/references/core/form/FormTemplate/) is where you configure how the form should display and set any associated properties for the form, e.g. title, description, field elements, etc.
   *
   * @since 4.16
   * @see [Sample - Update Feature Attributes](https://developers.arcgis.com/javascript/latest/sample-code/editing-groupedfeatureform/)
   * @see [Sample - Advanced Attribute Editing](https://developers.arcgis.com/javascript/latest/sample-code/editing-featureform-fieldvisibility/)
   */
  formTemplate?: FormTemplateProperties | null;
  /**
   * The timezone displayed within the form. If `unknown`, it will display the [FeatureLayer.preferredTimeZone](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#preferredTimeZone) of the layer. If this layer property is not set, it will default to UTC.
   *
   * @since 4.28
   */
  timeZone?: TimeZone | null;
}

export interface FeatureFormViewModelEvents {
  /**
   * Fires when the [FeatureForm.submit()](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/#submit) method is called.
   * Call [FeatureLayer.applyEdits()](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#applyEdits) method
   * to update a feature's attributes.
   *
   * @see [FeatureForm.submit()](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/#submit)
   * @see [FeatureForm.getValues()](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/#getValues)
   * @example
   * // Listen to the feature form's submit event.
   * featureForm.on("submit", function(){
   *   if (editFeature) {
   *     // Grab updated attributes from the form.
   *     const updated = featureForm.getValues();
   *
   *     // Loop through updated attributes and assign
   *     // the updated values to feature attributes.
   *     Object.keys(updated).forEach(function(name) {
   *       editFeature.attributes[name] = updated[name];
   *     });
   *
   *     // Setup the applyEdits parameter with updates.
   *     const edits = {
   *       updateFeatures: [editFeature]
   *     };
   *     applyEdits(edits);
   *   }
   * });
   */
  submit: SubmitEvent;
  /** Fires when a field value is updated. */
  "value-change": ValueChangeEvent;
}

export interface SubmitEvent {
  /** The invalid field names. */
  invalid: string[];
  /** The valid field names. */
  valid: string[];
  /**
   * An object of key-value pairs of field names with all of their values,
   * regardless of whether or not they were updated.
   */
  values: Attributes;
}

export interface ValueChangeEvent {
  /** The associated feature layer. */
  layer?: FeatureLayer | null;
  /** The associated feature. */
  feature?: Graphic | null;
  /** The updated field. */
  fieldName: string;
  /** The updated field value. */
  value: FieldValue;
  /** When true, the value conforms to the field's definition. */
  valid: boolean;
}

export type FeatureFormViewModelState = "disabled" | "ready";

/**
 * Provides the logic for the [FeatureForm](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/) widget.
 *
 * > [!WARNING]
 * >
 * > **Known Limitations**
 * >
 * > This widget is not yet at full parity with the functionality provided in the 3.x
 * > [AttributeInspector](https://developers.arcgis.com/javascript/3/jsapi/attributeinspector-amd.html)
 * > widget. There is currently no support for editing attachments or related records solely within this widget, although it is possible to edit attachments and relationship data via the [Editor](https://developers.arcgis.com/javascript/latest/references/core/widgets/Editor/) widget. Please refer to the [Editor](https://developers.arcgis.com/javascript/latest/references/core/widgets/Editor/) documentation for any known limitations regarding this.
 *
 * @since 4.9
 * @see [Programming patterns: Widget viewModel pattern](https://developers.arcgis.com/javascript/latest/programming-patterns/#widget-viewmodel-pattern)
 * @see [FeatureForm](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/)
 * @see [FormTemplate](https://developers.arcgis.com/javascript/latest/references/core/form/FormTemplate/)
 * @example
 * let featureForm = new FeatureForm({
 *   viewModel: { // Autocasts as new FeatureFormViewModel()
 *     map: map, // Required if using Arcade expressions that use the global $map variable
 *     layer: featureLayer   // Associates the FeatureForm to the layer
 *   },
 *   container: "formDiv"
 * });
 */
export default class FeatureFormViewModel extends FeatureFormViewModelSuperclass {
  /**
   * @deprecated
   * Do not directly reference this property.
   * Use EventNames and EventTypes helpers from \@arcgis/core/Evented
   */
  "@eventTypes": FeatureFormViewModelEvents;
  constructor(properties?: FeatureFormViewModelProperties);
  /**
   * The [Association][Association](https://developers.arcgis.com/javascript/latest/references/core/rest/networks/support/Association/) that the selected [feature](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/FeatureFormViewModel/#feature) is involved in.
   * The association is provided by the [activeAssociationInput](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/FeatureFormViewModel/#activeAssociationInput) of the previous Editor workflow.
   *
   * This property is null until the user has drilled into an associated feature in the Editor widget.
   */
  get activeAssociation(): Association | null | undefined;
  set activeAssociation(value: AssociationProperties | null | undefined);
  /**
   * The [UtilityNetworkAssociationInput][UtilityNetworkAssociationInput](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/UtilityNetworkAssociationInput/) providing
   * association data for the active workflow and selected association type in the Editor widget.
   *
   * This property is null until an association type is selected in the Editor widget.
   */
  get activeAssociationInput(): UtilityNetworkAssociationInput | null | undefined;
  /**
   * The edit type for the form, which determines the editing context and behavior.
   *
   * This property is used in two primary ways:
   *
   * 1. **Arcade Expressions Context**: When evaluating [Arcade expressions](https://developers.arcgis.com/javascript/latest/arcade/#form-calculation),
   * the value of this property is assigned to the `$editContext.editType` variable.
   *    This allows Arcade expressions to adapt their behavior based on the type of edit
   *    being performed (e.g., "INSERT", "UPDATE", "DELETE", or "NA").
   *
   * 2. **Layer Editing Capabilities**: This property is also used to determine whether
   *    the layer allows the specified type of edit. For example:
   *    - If `editType` is set to `"INSERT"` but the layer has `supportsAdd: false`,
   *      all inputs in the form will be read-only.
   *    - If `editType` is set to `"UPDATE"` but the layer has `supportsUpdate: false`,
   *      all inputs will also be read-only.
   *
   * This behavior applies regardless of whether a form template or Arcade expressions
   * are present. For instance:
   * - If a layer has `{ supportsAdd: true, supportsUpdate: false }` and no form template,
   *   setting `editType` to `"INSERT"` will allow the user to modify all fields, while
   *   setting it to `"UPDATE"` will make all fields read-only.
   * - If a form template is present with field elements that include `editableExpression`
   *   or `valueExpression`, the `editType` still determines whether the layer permits
   *   editing in the given context.
   *
   * @default "NA"
   * @since 4.33
   * @see [Arcade Form Calculation - profile variables](https://developers.arcgis.com/arcade/profiles/form-calculation/#profile-variables)
   */
  accessor editType: EditType;
  /**
   * The associated feature containing the editable attributes.
   * A common way to access this is via the [MapView.hitTest()](https://developers.arcgis.com/javascript/latest/references/core/views/MapView/#hitTest)
   * or [SceneView's](https://developers.arcgis.com/javascript/latest/references/core/views/SceneView/#hitTest) `hitTest()`
   * method.
   *
   * @example
   * // Check if a user clicked on an incident feature.
   * view.on("click", function(event) {
   *   view.hitTest(event).then(function(response) {
   *     // Display the attributes of selected incident feature in the form
   *     if (response.results[0].graphic && response.results[0].graphic.layer.id == "incidentsLayer") {
   *        formVM.feature = result.results[0].graphic
   *     }
   *   });
   * });
   */
  accessor feature: Graphic | null | undefined;
  /**
   * The associated [template](https://developers.arcgis.com/javascript/latest/references/core/form/FormTemplate/) used for the form.
   *
   * The [FormTemplate](https://developers.arcgis.com/javascript/latest/references/core/form/FormTemplate/) is where you configure how the form should display and set any associated properties for the form, e.g. title, description, field elements, etc.
   *
   * @since 4.16
   * @see [Sample - Update Feature Attributes](https://developers.arcgis.com/javascript/latest/sample-code/editing-groupedfeatureform/)
   * @see [Sample - Advanced Attribute Editing](https://developers.arcgis.com/javascript/latest/sample-code/editing-featureform-fieldvisibility/)
   */
  get formTemplate(): FormTemplate | null | undefined;
  set formTemplate(value: FormTemplateProperties | null | undefined);
  /**
   * The field, group, or relationship inputs that make up the form
   * [FeatureForm](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/) widget.
   *
   * @since 4.27
   */
  get inputs(): Array<FeatureFormSupportedInput>;
  /**
   * Layer containing the editable feature attributes.
   * If this layer is not specified, it is the same as the
   * [graphic's layer](https://developers.arcgis.com/javascript/latest/references/core/Graphic/#layer).
   */
  accessor layer: FeatureFormLayerUnion | null | undefined;
  /**
   * 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 `FeatureForm` that make use of the `$map` global variable.
   *
   * @since 4.27
   */
  accessor map: EsriMap | null | undefined;
  /**
   * The widget's state. Possible values are in the table below.
   *
   * Value | Description
   * ------|------------
   * ready | Dependencies are met and has valid property values.
   * disabled | Dependencies are missing and cannot provide valid inputs.
   *
   * @default "disabled"
   */
  get state(): FeatureFormViewModelState;
  /** Indicates if the field's value can be submitted without introducing data validation issues. */
  get submittable(): boolean;
  /**
   * The timezone displayed within the form. If `unknown`, it will display the [FeatureLayer.preferredTimeZone](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/#preferredTimeZone) of the layer. If this layer property is not set, it will default to UTC.
   *
   * @since 4.28
   */
  get timeZone(): TimeZone;
  set timeZone(value: TimeZone | null | undefined);
  /**
   * Indicates whether the form is currently updating.
   *
   * @since 4.27
   */
  get updating(): boolean;
  /** Indicates whether all of the input fields are valid. */
  get valid(): boolean;
  /**
   * Convenience method to find field inputs.
   *
   * @param fieldName - The input field to find.
   * @returns Returns an instance of the FieldInput.
   */
  findField(fieldName: string | null | undefined): FieldInput | null | undefined;
  /**
   * Returns all of the field values, regardless of whether or not they were updated.
   *
   * @returns An object of key-value pairs of field names with their values.
   * @see [@submit](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/FeatureFormViewModel/#event-submit) event
   * @see [submit()](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/FeatureFormViewModel/#submit) method
   * @example
   * function updateFeature() {
   *   // Get the updated field values
   *   const attributes = formVM.getValues();
   *   // Call applyEdits on the feature layer
   *   layer.applyEdits({
   *     // Pass in the updated field values
   *     updateFeatures: [{ attributes }]
   *   });
   * }
   */
  getValues(): Attributes;
  /**
   * The method used to set the updated field value.
   *
   * > [!WARNING]
   * >
   * > This renders the new value within the form but does not update the underlying input feature's attributes.
   *
   * @param fieldName - The target field to update.
   * @param value - The value to set on the target field.
   * @since 4.21
   */
  setValue(fieldName: string, value: FieldValue): void;
  /**
   * Fires the [@submit](https://developers.arcgis.com/javascript/latest/references/core/widgets/FeatureForm/FeatureFormViewModel/#event-submit) event.
   *
   * @example
   * // Listen for when 'submit' is called.
   * // Once it is fired, update the feature.
   * formVM.on("submit", updateFeature);
   *
   * // When the DOM's button (btnUpdate) is clicked,
   * // Update attributes of the selected feature.
   * document.getElementById("btnUpdate").onclick = function() {
   *   // Fires feature form's submit event.
   *   formVM.submit();
   * }
   */
  submit(): void;
  /**
   * Validates whether a feature's attribute values conform to the defined contingent values.
   *
   * @param values - A hash map of the form fields and their values.
   * @param options - An object specifying additional options on what should be considered an error.
   * @returns An array of objects, each representing a violation of a field group's contingent value rules.
   * @since 4.23
   * @example
   * // Validate a features attribute values against the layer's contingent values
   * featureForm.on("value-change", () => {
   *   const validations = featureForm.viewModel.validateContingencyConstraints(featureForm.getValues());
   *   !!validations.length ? console.log("found some validation errors: ", validations) : console.log("no errors found!");
   * });
   */
  validateContingencyConstraints(values: Attributes, options?: FeatureFormViewModelValidateContingencyConstraintsOptions): object[];
}
declare const FeatureFormViewModelSuperclass: typeof EventedAccessor & typeof EsriPromiseMixin

export interface FeatureFormViewModelValidateContingencyConstraintsOptions {
  /** If `true`, return contingency violations for field groups that are invalid because values have not yet been specified for all their fields. If `false`, any of these violations are ignored. */
  includeIncompleteViolations: boolean;
}