/**
 * This module allows you to evaluate [Arcade expressions](https://developers.arcgis.com/javascript/latest/arcade/) outside
 * traditional ArcGIS Arcade [profiles](https://developers.arcgis.com/javascript/latest/arcade/#where-can-i-use-arcade). Profiles define the valid input variables
 * (i.e. profile variables) for expressions, supported functions, and valid return types of expressions.
 * All Arcade expressions executed in the ArcGIS Maps SDK for JavaScript must conform to the rules of the profile where
 * they are written. The [createArcadeExecutor()](https://developers.arcgis.com/javascript/latest/references/core/arcade/#createArcadeExecutor) method provides an API for executing
 * Arcade expressions outside the context of predefined ArcGIS Arcade profiles.
 *
 * This module allows you to do the following:
 *
 * - Execute expressions already written for existing ArcGIS profiles in another context. For example, an end user may
 *   have written an expression for a renderer, popup, or label in a WebMap and the data produced by the expression
 *   should be displayed outside the WebMap in a custom component, table, or summarized in a chart.
 * - Define custom Arcade profiles and provide a configurable experience within an app for users to write their own expressions.
 * - Developers may want to take advantage of the simplified experience of filtering and querying data
 *   using the chainable FeatureSet functions Arcade provides.
 *
 * @since 4.24
 * @see [Sample - Execute Arcade for a custom chart](https://developers.arcgis.com/javascript/latest/sample-code/arcade-execute-chart/)
 * @see [What is Arcade?](https://developers.arcgis.com/javascript/latest/arcade/)
 * @see [ArcGIS Arcade](https://developers.arcgis.com/arcade/)
 */
import type Graphic from "./Graphic.js";
import type EsriMap from "./Map.js";
import type SpatialReference from "./geometry/SpatialReference.js";
import type CatalogLayer from "./layers/CatalogLayer.js";
import type CSVLayer from "./layers/CSVLayer.js";
import type FeatureLayer from "./layers/FeatureLayer.js";
import type GeoJSONLayer from "./layers/GeoJSONLayer.js";
import type OrientedImageryLayer from "./layers/OrientedImageryLayer.js";
import type ParquetLayer from "./layers/ParquetLayer.js";
import type SubtypeGroupLayer from "./layers/SubtypeGroupLayer.js";
import type WFSLayer from "./layers/WFSLayer.js";
import type CatalogFootprintLayer from "./layers/catalog/CatalogFootprintLayer.js";
import type KnowledgeGraphSublayer from "./layers/knowledgeGraph/KnowledgeGraphSublayer.js";
import type SubtypeSublayer from "./layers/support/SubtypeSublayer.js";
import type Portal from "./portal/Portal.js";
import type KnowledgeGraph from "./rest/knowledgeGraph/KnowledgeGraph.js";
import type FeatureSet from "./rest/support/FeatureSet.js";
import type { Breakpoint, Debugger, DebugValue } from "./arcade/debug.js";
import type { GeometryUnion } from "./geometry/types.js";

/** Layer types that may be used to construct Arcade FeatureSets. */
export type ArcadeSupportedFeatureSetLayerUnion = CatalogFootprintLayer | CatalogLayer | CSVLayer | FeatureLayer | GeoJSONLayer | KnowledgeGraphSublayer | OrientedImageryLayer | ParquetLayer | SubtypeGroupLayer | SubtypeSublayer | WFSLayer;

/**
 * Creates a [Profile](https://developers.arcgis.com/javascript/latest/references/core/arcade/#Profile) definition for an Arcade profile implemented in the ArcGIS Maps SDK for JavaScript.
 * The result of this should be provided to the [createArcadeExecutor()](https://developers.arcgis.com/javascript/latest/references/core/arcade/#createArcadeExecutor) method.
 *
 * @param profileName - The name of the Arcade profile definition to create. _The values `"feature-reduction-popup"` and `"feature-reduction-popup-element"` are deprecated since 4.32._
 * @returns Returns the profile definition used to execute an expression
 *   with the [ArcadeExecutor](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ArcadeExecutor).
 * @since 4.25
 * @see [Arcade profile specifications](https://developers.arcgis.com/arcade/profiles/)
 * @see [Arcade profiles implemented by the JS API](https://developers.arcgis.com/javascript/latest/arcade/#where-can-i-use-arcade)
 * @see [createArcadeExecutor()](https://developers.arcgis.com/javascript/latest/references/core/arcade/#createArcadeExecutor)
 * @example
 * const labelingProfile = arcade.createArcadeProfile("labeling");
 * // creates the following object to be provided to the createArcadeExecutor method.
 * // {
 * //   variables: [{
 * //     name: "$feature",
 * //     type: "feature"
 * //   }]
 * // }
 *
 * const labelExecutor = await createArcadeExecutor("$feature.NAME + ' COUNTY'", labelingProfile);
 */
export function createArcadeProfile(profileName: ProfileName | "feature-reduction-popup" | "feature-reduction-popup-element"): Profile;

/**
 * Compiles an Arcade expression and its profile into an executor. The executor provides functions to evaluate
 * the results for profile variables.
 *
 * @param script - The [Arcade expression](https://developers.arcgis.com/javascript/latest/arcade/) to compile and execute.
 * @param profile - The profile definition used to execute the given expression. The profile
 *   defines the profile variable names and data types the user may use as inputs in the expression.
 *   The variable instances are provided to the executor function.
 * @param options - _Since 4.33_ Options used to configure the executor.
 * @returns Resolves to an [ArcadeExecutor](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ArcadeExecutor)
 *   that provides a synchronous (if applicable) and asynchronous function used to evaluate the compiled Arcade expression
 *   when provided with valid input profile variables.
 * @see [Sample - Execute Arcade for a custom chart](https://developers.arcgis.com/javascript/latest/sample-code/arcade-execute-chart/)
 * @example
 * // Synchronous execution for all features in layer view
 * // % of the population not in labor force
 * const arcadeScript = "(($feature.POP_16UP - $feature.EMP_CY) / $feature.POP_16UP) * 100"
 *
 * const profile = {
 *   variables: [{
 *     name: "$feature",
 *     type: "feature"
 *   }]
 * };
 *
 * const laborForceExecutor = await createArcadeExecutor(arcadeScript, profile);
 * // ensures data values from fields used in the expression are
 * // available when the expression executes.
 * layer.outFields = laborForceExecutor.fieldsUsed;
 *
 * const { features } = await layerView.queryFeatures();
 *
 * const allValues = features.map( (feature) => {
 *   return laborForceExecutor.execute({
 *     "$feature": feature
 *   });
 * });
 *
 * // allValues can be displayed in a chart, or used to
 * // report stats for this variable
 * @example
 * // Asynchronous execution for one feature clicked in the view
 *
 * const profile = {
 *   variables: [{
 *     name: "$feature",
 *     type: "feature"
 *   }, {
 *     name: "$layer",
 *     type: "featureSet"
 *   }]
 * };
 *
 * // This expression executes on each feature click. It compares the % of the population
 * // not participating in the labor force with the same value of all features within
 * // one mile of the selected feature's location
 * const arcadeScript = `
 *   var featureNotLaborForce = Round((($feature.POP_16UP - $feature.EMP_CY)/$feature.POP_16UP)*100);
 *   var id = $feature.OBJECTID;
 *
 *   var neighbors = Filter( Intersects( $layer, BufferGeodetic($feature, 1, "mile") ), "OBJECTID <> \@id" );
 *   if( Count(neighbors) == 0 ){
 *     return "No neighbors within 1 mile";
 *   }
 *
 *   var neighborsNotLaborForceAverage = Average(neighbors, "( (POP_16UP - EMP_CY) / POP_16UP) * 100");
 *   var difference = Round(featureNotLaborForce - neighborsNotLaborForceAverage);
 *   var differenceText = IIF(difference > 0, "+"+difference, Text(difference));
 *   return differenceText;
 * `;
 *
 * const neighborhoodExecutor = await createArcadeExecutor(arcadeScript, profile);
 * // ensures data values from fields used in the expression are
 * // available when the expression executes.
 * layer.outFields = neighborhoodExecutor.fieldsUsed;
 *
 * view.on("click", async (event) => {
 *   const hitResponse = await view.hitTest(event, include: { layer });
 *   const hitGraphic = hitResponse?.results[0].graphic;
 *
 *   const scriptResult = await neighborhoodExecutor.executeAsync({
 *     "$feature": hitGraphic,
 *     "$layer": hitGraphic.layer
 *   }, {
 *     spatialReference: view.spatialReference
 *   });
 *
 *   // display the value of scriptResult in a UI component or use it
 *   // in some form of client-side analysis
 * });
 */
export function createArcadeExecutor(script: string, profile: Profile, options?: ExecutorOptions): Promise<ArcadeExecutor>;

/**
 * Compiles an Arcade expression and its profile into an executor for debugging. When the executing code hits one of the
 * provided breakpoints, it will [Debugger](https://developers.arcgis.com/javascript/latest/references/core/arcade/debug/#Debugger-pause) the provided debugger service and suspend execution
 * until the returned promise is resolved.
 *
 * Compared to [createArcadeExecutor()](https://developers.arcgis.com/javascript/latest/references/core/arcade/#createArcadeExecutor), this executor only supports async execution (required for debugging)
 * and returns a [DebugValue](https://developers.arcgis.com/javascript/latest/references/core/arcade/debug/#DebugValue) result for inspection.
 *
 * @param script - The [Arcade expression](https://developers.arcgis.com/javascript/latest/arcade/) to compile and
 * execute.
 * @param profile - The profile definition used to execute the given expression. The profile defines the profile
 * variable names and data types the user may use as inputs in the expression. The variable instances are provided to
 * the executor function.
 * @param options - Options used to configure the executor. The debugger host service can be provided here if it is the
 * same for all executions or when executing the expression.
 * @internal
 */
export function createArcadeDebugExecutor(script: string, profile: Profile, options?: DebugExecutorOptions): Promise<ArcadeDebugExecutor>;

/** @see [createArcadeCache()](https://developers.arcgis.com/javascript/latest/references/core/arcade/#createArcadeCache) */
export interface ArcadeCache {
  clear(): void;
}

/**
 * @see [ExecuteContext#cache](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ExecuteContext)
 * @see [ExecutorOptions#cache](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ExecutorOptions)
 */
export function createArcadeCache(): ArcadeCache;

export type ProfileName = "aggregate-field" | "form-constraint" | "feature-z" | "field-calculation" | "form-calculation" | "form-attachment-filename-calculation" | "labeling" | "popup" | "popup-element" | "popup-feature-reduction" | "popup-element-feature-reduction" | "visualization" | "popup-voxel" | "popup-element-voxel" | "popup-imagery" | "popup-element-imagery";

/**
 * Defines the definitions of a profile's input variables. This consists of an array of objects specifying
 * the name of each profile variable and its Arcade data type. All profile names must begin with the `$` character.
 *
 * @example
 * const profile = {
 *   variables: [{
 *     name: "$mapClick",
 *     type: "geometry"
 *   }, {
 *     name: "$threshold",
 *     type: "number"
 *   }, {
 *     name: "$feature",
 *     type: "feature"
 *   }, {
 *     name: "$map",
 *     type: "featureSetCollection"
 *   }]
 * };
 */
export interface Profile {
  /**
   * The Arcade type definitions for profile variables
   *   used as input to the compiled expression.
   */
  variables: Variable[];
}

export type VariableType = "array" | "boolean" | "date" | "dateOnly" | "dictionary" | "feature" | "featureSet" | "featureSetCollection" | "geometry" | "json" | "knowledgeGraph" | "number" | "pixel" | "text" | "time" | "voxel";

export interface VariableBase {
  /**
   * The name of the profile variable. To follow Arcade's naming convention, this should start with the `$`
   *   character unless this variable is nested in a [DictionaryVariable](https://developers.arcgis.com/javascript/latest/references/core/arcade/#DictionaryVariable).
   */
  name: string;
}

/**
 * The type definition for a simple Arcade profile variable.
 *
 * @example
 * const profile = {
 *   variables: [{
 *     name: "$mapClick",
 *     type: "geometry"
 *   }, {
 *     name: "$threshold",
 *     type: "number"
 *   }, {
 *     name: "$feature",
 *     type: "feature"
 *   }, {
 *     name: "$map",
 *     type: "featureSetCollection"
 *   }]
 * };
 */
export interface SimpleVariable extends VariableBase {
  /**
   * The
   *   Arcade data type of the variable. See [ProfileVariableInstance](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ProfileVariableInstance) to see the mapping of Arcade types
   *   to JavaScript types.
   */
  type: Exclude<VariableType, "dictionary" | "array">;
}

/**
 * The type definition for an Arcade dictionary profile variable.
 *
 * @example
 * const profile = {
 *   variables: [{
 *     name: "$analysisParams",
 *     type: "dictionary",
 *     properties: [{
 *       name: "distance",
 *       type: "number"
 *     }, {
 *       name: "projectType",
 *       type: "text"
 *     }, {
 *       name: "includeBuffer",
 *       type: "boolean"
 *     }]
 *   }, {
 *      name: "$projectLocation",
 *      type: "feature"
 *   }]
 * };
 */
export interface DictionaryVariable extends VariableBase {
  /**
   * The Arcade data type of the variable.
   *   The corresponding JavaScript type to an Arcade Dictionary is a plain Object following the specification defined in
   *   [ProfileVariableInstance](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ProfileVariableInstance).
   */
  type: "dictionary";
  /**
   * The type definitions of the dictionary's properties. The names of
   *   these properties should not begin with the `$` character.
   */
  properties: Variable[];
}

/**
 * The type definition for an Arcade array profile variable.
 *
 * @example
 * const profile = {
 *   variables: [{
 *     name: "$checkpoints",
 *     type: "array",
 *     elementType: {
 *       type: "feature"
 *     }
 *   }]
 * };
 */
export interface ArrayVariable extends VariableBase {
  /**
   * The Arcade data type of the variable. For ArrayVariable, the type is always `array`.
   *   The corresponding JavaScript type to an Arcade Array is an array following the specification defined in
   *   [ProfileVariableInstance](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ProfileVariableInstance).
   */
  type: "array";
  /**
   * The type definition of the Array's items. Arcade arrays must be of
   *   a single type when used as an input profile variable. When indicating an elementType, you do not need to specify a name
   *   for the element.
   */
  elementType: ArrayElement;
}

/** The type definition of a profile variable. See the individual profile variable types for more information and examples. */
export type Variable = SimpleVariable | DictionaryVariable | ArrayVariable;

/** The type definition of a profile variable of type `array`. */
export type ArrayElement = Omit<SimpleVariable, "name"> | Omit<DictionaryVariable, "name"> | Omit<ArrayVariable, "name">;

/**
 * The JavaScript types that may be used as inputs to profile variables when evaluating compiled expressions
 * with [ExecuteFunction](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ExecuteFunction) or [ExecuteFunctionAsync](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ExecuteFunctionAsync). See the table below for the following mapping
 * of JavaScript types that may be used to hydrate profile variables for corresponding Arcade data types.
 *
 * Arcade type | JS type | Notes
 * -------------|--------|------
 * [Number](https://developers.arcgis.com/arcade/guide/types/#number) | [Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)
 * [Text](https://developers.arcgis.com/arcade/guide/types/#text) | [String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)
 * [Date](https://developers.arcgis.com/arcade/guide/types/#date) | [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | Dates returned from Arcade expressions will not preserve time zone information included in the Arcade Date value. Since native JS Dates objects do not include time zone information, they will always display in the local or system time of the client executing the expression. To preserve time zone information in dates returned from Arcade, the expression author should cast dates to Arcade Text values in the return statement of the expression.
 * [DateOnly](https://developers.arcgis.com/arcade/guide/types/#dateonly) | [String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)
 * [Time](https://developers.arcgis.com/arcade/guide/types/#time) | [String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)
 * [Boolean](https://developers.arcgis.com/arcade/guide/types/#boolean) | [Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)
 * [Dictionary](https://developers.arcgis.com/arcade/guide/types/#dictionary) | [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object) | An object with key/value pairs with keys defined by a [DictionaryVariable](https://developers.arcgis.com/javascript/latest/references/core/arcade/#DictionaryVariable) whose values must be one of [ProfileVariableInstance](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ProfileVariableInstance).
 * [KnowledgeGraph](https://developers.arcgis.com/arcade/guide/types/#knowledgegraph) | [KnowledgeGraph](https://developers.arcgis.com/javascript/latest/references/core/rest/knowledgeGraph/KnowledgeGraph/) | This value typically comes from [KnowledgeGraphLayer.knowledgeGraph](https://developers.arcgis.com/javascript/latest/references/core/layers/KnowledgeGraphLayer/#knowledgeGraph).
 * [Array](https://developers.arcgis.com/arcade/guide/types/#array) | [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) | Arcade arrays are immutable and must be of a single [ProfileVariableInstance](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ProfileVariableInstance).
 * [Geometry](https://developers.arcgis.com/arcade/guide/types/#geometry) | [Geometry](https://developers.arcgis.com/javascript/latest/references/core/geometry/Geometry/)
 * [Feature](https://developers.arcgis.com/arcade/guide/types/#feature) | [Graphic](https://developers.arcgis.com/javascript/latest/references/core/Graphic/)
 * [FeatureSet](https://developers.arcgis.com/arcade/guide/types/#featureset) | [ArcadeSupportedFeatureSetLayerUnion](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ArcadeSupportedFeatureSetLayerUnion) \| [FeatureSet](https://developers.arcgis.com/javascript/latest/references/core/rest/support/FeatureSet/) |
 * [FeatureSetCollection](https://developers.arcgis.com/arcade/guide/types/#featuresetcollection) | [Map](https://developers.arcgis.com/javascript/latest/references/core/Map/) \| [String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | A string may be provided, pointing to the url of a Feature Service.
 */
export type ProfileVariableInstance = ArcadeSupportedFeatureSetLayerUnion | boolean | Date | EsriMap | FeatureSet | GeometryUnion | Graphic | KnowledgeGraph | null | undefined | number | string | { [key: string]: ProfileVariableInstance; } | ProfileVariableInstance[];

export type ProfileVariableInstances = Record<string, ProfileVariableInstance>;

/**
 * The execution context of the Arcade expression.
 *
 * @example
 * // the input feature's geometry is expected
 * // to be in the spatial reference of the view
 * // All Arcade dates will be represented in the
 * // time zone of the view or web map.
 * await executor.executeAsync({
 *   "$feature": feature
 * }, {
 *   spatialReference: view.spatialReference,
 *   timeZone: view.timeZone
 * });
 * @example
 * // the input feature's geometry is expected
 * // to be in the spatial reference of the layer
 * await executor.executeAsync({
 *   "$feature": feature
 * }, {
 *   spatialReference: layer.spatialReference
 * });
 * @example
 * // the input feature's geometry is expected
 * // to be Web Mercator
 * await executor.executeAsync({
 *   "$feature": feature
 * });
 */
export interface ExecuteContext {
  /**
   * The expected spatial reference of
   *   input geometries in geometry functions. If executing an expression in the context of a map, then set the spatial
   *   reference of the view to this property. If executing an expression in the context of a layer,
   *   then use the layer's spatial reference. If not specified, then the spatial reference defaults to
   *   [SpatialReference.WebMercator](https://developers.arcgis.com/javascript/latest/references/core/geometry/SpatialReference/#WebMercator).
   */
  spatialReference?: SpatialReference;
  /**
   * @deprecated since version 5.0. Use `cache` instead.
   * @since 4.33
   */
  lruCache?: ArcadeCache;
  /** @since 5.0 */
  cache?: ArcadeCache;
  /**
   * Required for expressions that use the
   * `GetUser()` Arcade function.
   *
   * @since 4.33
   */
  services?: ArcadeServices;
  /** @internal */
  console?: (...args: any[]) => void;
  /**
   * Defines the default time zone in which to create and
   *   display Arcade date types. Possible values include any time zone that may be set on the view's
   *   [timeZone](https://developers.arcgis.com/javascript/latest/references/core/views/View2D/#timeZone). Generally, if the Arcade context's profile involves a
   *   map, or features originating from a map, the value of the view's [timeZone](https://developers.arcgis.com/javascript/latest/references/core/views/View2D/#timeZone) should be set
   *   here. By default, the time zone is set to "system".
   *
   * @default system
   * @since 4.28
   */
  timeZone?: string | null;
  /** @internal */
  rawOutput?: boolean;
}

/**
 * ArcadeServices provides access to services and user information that are required for executing Arcade expressions.
 *
 * @since 4.33
 */
export interface ArcadeServices {
  /**
   * An ArcGIS Portal instance. This is required for expressions that use the `GetUser()`
   * Arcade function. By default, the portal referenced in [esriConfig.portalUrl](https://developers.arcgis.com/javascript/latest/references/core/config/#Config-portalUrl) is used.
   */
  portal?: Portal;
  /** @internal */
  debugger?: Debugger;
}

export interface ExecutorOptions {
  /**
   * @deprecated since version 5.0. Use `cache` instead.
   * @since 4.33
   */
  lruCache?: ArcadeCache;
  /** @since 5.0 */
  cache?: ArcadeCache;
  /**
   * Required for expressions that use the
   * `GetUser()` Arcade function.
   *
   * @since 4.33
   */
  services?: ArcadeServices;
}

/** The JavaScript type of the result returned from the Arcade expression. */
export type ResultType = number | string | Date | boolean | Graphic | GeometryUnion | Object | Array<ResultType> | null | undefined;

/**
 * Type definition for a settled promise of the asynchronous execution of an Arcade expression.
 *
 * @since 4.33
 */
export type SettledArcadePromise = FulfilledArcadePromise | RejectedArcadePromise;

/**
 * Type definition for a fulfilled promise of the asynchronous execution of an Arcade expression.
 *
 * @since 4.33
 */
export type FulfilledArcadePromise = PromiseFulfilledResult<ResultType>;

/**
 * Type definition for a rejected promise of the asynchronous execution of an Arcade expression.
 *
 * @since 4.33
 */
export type RejectedArcadePromise = PromiseRejectedResult;

/**
 * An executor provides a synchronous (if applicable) and asynchronous function used to evaluate the
 * compiled Arcade expression with the inputs of the declared profile variables.
 * It also provides metadata about the compilation result.
 *
 * @see [createArcadeExecutor()](https://developers.arcgis.com/javascript/latest/references/core/arcade/#createArcadeExecutor)
 * @example
 * // Synchronous execution for all features in layer view
 * // % of the population not in labor force
 * const arcadeScript = "(($feature.POP_16UP - $feature.EMP_CY) / $feature.POP_16UP) * 100"
 *
 * const profile = {
 *   variables: [{
 *     name: "$feature",
 *     type: "feature"
 *   }]
 * };
 *
 * const laborForceExecutor = await createArcadeExecutor(arcadeScript, profile);
 * // ensures data values from fields used in the expression are
 * // available when the expression executes.
 * layer.outFields = laborForceExecutor.fieldsUsed;
 *
 * const { features } = await layerView.queryFeatures();
 *
 * const allValues = features.map( (feature) => {
 *   return laborForceExecutor.execute({
 *     "$feature": feature
 *   });
 * });
 *
 * // allValues can be displayed in a chart, or used to
 * // report stats for this variable
 * @example
 * // Asynchronous execution for one feature clicked in the view
 *
 * const profile = {
 *   variables: [{
 *     name: "$feature",
 *     type: "feature"
 *   }, {
 *     name: "$layer",
 *     type: "featureSet"
 *   }]
 * };
 *
 * // This expression executes on each feature click. It compares the % of the population
 * // not participating in the labor force with the same value of all features within
 * // one mile of the selected feature's location
 * const arcadeScript = `
 *   var featureNotLaborForce = Round((($feature.POP_16UP - $feature.EMP_CY)/$feature.POP_16UP)*100);
 *   var id = $feature.OBJECTID;
 *
 *   var neighbors = Filter( Intersects( $layer, BufferGeodetic($feature, 1, "mile") ), "OBJECTID <> \@id" );
 *   if( Count(neighbors) == 0 ){
 *     return "No neighbors within 1 mile";
 *   }
 *
 *   var neighborsNotLaborForceAverage = Average(neighbors, "( (POP_16UP - EMP_CY) / POP_16UP) * 100");
 *   var difference = Round(featureNotLaborForce - neighborsNotLaborForceAverage);
 *   var differenceText = IIF(difference > 0, "+"+difference, Text(difference));
 *   return differenceText;
 * `;
 *
 * const neighborhoodExecutor = await createArcadeExecutor(arcadeScript, profile);
 * // ensures data values from fields used in the expression are
 * // available when the expression executes.
 * layer.outFields = neighborhoodExecutor.fieldsUsed;
 *
 * view.on("click", async (event) => {
 *   const hitResponse = await view.hitTest(event, include: { layer });
 *   const hitGraphic = hitResponse?.results[0].graphic;
 *
 *   const scriptResult = await neighborhoodExecutor.executeAsync({
 *     "$feature": hitGraphic,
 *     "$layer": hitGraphic.layer
 *   }, {
 *     spatialReference: view.spatialReference
 *   });
 *
 *   // display the value of scriptResult in a UI component or use it
 *   // in some form of client-side analysis
 * });
 * @example
 * const profile = {
 *   variables: [{
 *     name: "$feature",
 *     type: "feature"
 *   }]
 * };
 *
 * // Arcade expression defined by user...
 * const syncExecutor = await createArcadeExecutor(arcadeScript, profile);
 * // ensures data values from fields used in the expression are
 * // available when the expression executes.
 * layer.outFields = syncExecutor.fieldsUsed;
 *
 * // throw error if expression from user uses
 * // invalid functions that require async execution
 * if(syncExecutor.isAsync){
 *   throw new Error("Invalid expression. Expression should not use FeatureSet functions.");
 * }
 *
 * const { features } = await layerView.queryFeatures();
 *
 * const allValues = features.map( (feature) => {
 *   return syncExecutor.execute({
 *     "$feature": feature
 *   });
 * });
 *
 * // allValues can be displayed in a chart, or used to
 * // report stats for this variable
 */
export interface ArcadeExecutor {
  /**
   * A function that can be invoked to evaluate the compiled
   *   expression for a set of profile variables. Will return the results of the evaluation. This function will
   *   only be available if `isAsync` is false.
   */
  execute: ExecuteFunction;
  /**
   * Asynchronous function that evaluates the compiled
   *   expression. This function may always be used to evaluate the expression, but must be used if `isAsync` is true.
   */
  executeAsync: ExecuteFunctionAsync;
  /**
   * Asynchronous function
   *   that evaluates the compiled expression for a batch of profile variable instances. This is the preferred execution
   *   method for large batches of features that require asynchronous execution.
   *
   * @since 4.33
   */
  executeAsyncBatch: ExecuteFunctionAsyncBatch;
  /**
   * Indicates whether the compiled expression accesses data using a FeatureSet function
   *   and therefore must be executed using the `executeAsync` function. If `false`, the expression may be executed
   *   with `execute` or `executeAsync`.
   */
  isAsync: boolean;
  /**
   * References the names of profile variables used in the compiled expression.
   *
   * @since 4.34
   */
  variablesUsed: string[];
  /**
   * References the names of fields used in the compiled expression when
   *   working with the `$feature` profile variable or functions that expect field names, such as `Expects`.
   *   Field names referenced here should be requested by the underlying layer or datastore. In JavaScript,
   *   this is typically done with `layer.outFields`.
   */
  fieldsUsed: string[];
  /** Indicates whether the expression accesses or uses a feature's geometry. */
  geometryUsed: boolean;
}

/**
 * Executes the compiled Arcade expression synchronously with the given profile variable instances. This function
 * may not be used if the expression accesses data using FeatureSet functions.
 *
 * @param profileVariableInstances - An object of key/value pairs where the key is the name of a
 *   [Variable](https://developers.arcgis.com/javascript/latest/references/core/arcade/#Variable) defined when creating the executor. The key's value must be of type
 *   [ProfileVariableInstance](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ProfileVariableInstance).
 * @param context - The context with which to execute the expression.
 * @returns Returns the value of the evaluated expression.
 * @example
 * // Synchronous execution for all features in layer view
 * // % of the population not in labor force
 * const arcadeScript = "(($feature.POP_16UP - $feature.EMP_CY) / $feature.POP_16UP) * 100"
 *
 * const profile = {
 *   variables: [{
 *     name: "$feature",
 *     type: "feature"
 *   }]
 * };
 *
 * const laborForceExecutor = await createArcadeExecutor(arcadeScript, profile);
 * // ensures data values from fields used in the expression are
 * // available when the expression executes.
 * layer.outFields = laborForceExecutor.fieldsUsed;
 *
 * const { features } = await layerView.queryFeatures();
 *
 * const allValues = features.map( (feature) => {
 *   return laborForceExecutor.execute({
 *     "$feature": feature
 *   });
 * });
 *
 * // allValues can be displayed in a chart, or used to
 * // report stats for this variable
 * @example
 * const profile = {
 *   variables: [{
 *     name: "$feature",
 *     type: "feature"
 *   }]
 * };
 *
 * // Arcade expression defined by user...
 * const syncExecutor = await createArcadeExecutor(arcadeScript, profile);
 * // ensures data values from fields used in the expression are
 * // available when the expression executes.
 * layer.outFields = syncExecutor.fieldsUsed;
 *
 * // throw error if expression from user uses invalid functions
 * if(syncExecutor.isAsync){
 *   throw new Error("Invalid expression. Expression should not use FeatureSet functions.");
 * }
 *
 * const { features } = await layerView.queryFeatures();
 *
 * const allValues = features.map( (feature) => {
 *   return syncExecutor.execute({
 *     "$feature": feature
 *   });
 * });
 *
 * // allValues can be displayed in a chart, or used to
 * // report stats for this variable
 */
export type ExecuteFunction = (profileVariableInstances: ProfileVariableInstances, context?: ExecuteContext) => ResultType;

/**
 * Executes the compiled Arcade expression asynchronously with the given profile variable instances.
 *
 * @param profileVariableInstances - An object of key/value pairs where the key is the name of a
 *   [Variable](https://developers.arcgis.com/javascript/latest/references/core/arcade/#Variable) defined when creating the executor, whose value must be of type
 *   [ProfileVariableInstance](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ProfileVariableInstance).
 * @param context - The context with which to execute the expression.
 * @returns Resolves to the value of the evaluated expression.
 * @example
 * // Asynchronous execution for one feature clicked in the view
 *
 * const profile = {
 *   variables: [{
 *     name: "$feature",
 *     type: "feature"
 *   }, {
 *     name: "$layer",
 *     type: "featureSet"
 *   }]
 * };
 *
 * // This expression executes on each feature click. It compares the % of the population
 * // not participating in the labor force with the same value of all features within
 * // one mile of the selected feature's location
 * const arcadeScript = `
 *   var featureNotLaborForce = Round((($feature.POP_16UP - $feature.EMP_CY)/$feature.POP_16UP)*100);
 *   var id = $feature.OBJECTID;
 *
 *   var neighbors = Filter( Intersects( $layer, BufferGeodetic($feature, 1, "mile") ), "OBJECTID <> \@id" );
 *   if( Count(neighbors) == 0 ){
 *     return "No neighbors within 1 mile";
 *   }
 *
 *   var neighborsNotLaborForceAverage = Average(neighbors, "( (POP_16UP - EMP_CY) / POP_16UP) * 100");
 *   var difference = Round(featureNotLaborForce - neighborsNotLaborForceAverage);
 *   var differenceText = IIF(difference > 0, "+"+difference, Text(difference));
 *   return differenceText;
 * `;
 *
 * const neighborhoodExecutor = await createArcadeExecutor(arcadeScript, profile);
 * // ensures data values from fields used in the expression are
 * // available when the expression executes.
 * layer.outFields = neighborhoodExecutor.fieldsUsed;
 *
 * view.on("click", async (event) => {
 *   const hitResponse = await view.hitTest(event, include: { layer });
 *   const hitGraphic = hitResponse?.results[0].graphic;
 *
 *   const scriptResult = await neighborhoodExecutor.executeAsync({
 *     "$feature": hitGraphic,
 *     "$layer": hitGraphic.layer
 *   }, {
 *     spatialReference: view.spatialReference
 *   });
 *
 *   // display the value of scriptResult in a UI component or use it
 *   // in some form of client-side analysis
 * });
 */
export type ExecuteFunctionAsync = (profileVariableInstances: ProfileVariableInstances, context?: ExecuteContext) => Promise<ResultType>;

/**
 * Executes the compiled Arcade expression asynchronously for a batch of profile variable instances (i.e. inputs).
 * This is the preferred execution method for large batches of features that require asynchronous execution.
 *
 * @param inputData - An iterable object, such as an array or collection, containing the
 *   data used to hydrate the profile variable instances.
 * @param variableInstancesCreator - Callback function used to
 *   create profile variable instances from each `inputData` item.
 * @param context - The context with which to execute the expression.
 * @returns Resolves with the results of evaluating the
 *   expression for each `inputData` item.
 * @since 4.33
 * @example
 * const profile = {
 *   variables: [{
 *     name: "$text",
 *     type: "text"
 *   }]
 * };
 *
 * const arcadeScript = `
 *   var locale = GetEnvironment().locale;  // browser locale
 *   var result = TranslateText($text, locale);
 *   // result will be a dictionary with the translated text
 *   if (result.success){
 *     if(HasValue(result, ["results", 0, "translation"])){
 *       // returns "Hola mundo" if the device locale is 'es' and $text is "Hello world"
 *       return result.results[0].translation[locale];
 *     }
 *     if (HasValue(result, ["results", 0, "text"])){
 *       // returns "Hello world" if translation could not be performed
 *       return result.results[0].text;
 *     }
 *   }
 *   return "Translation not successful";
 * `;
 *
 * const executor = await createArcadeExecutor(arcadeScript, profile);
 * const textToTranslate = ["Hello world", "Goodbye world", "Hello again", "Goodbye again"];
 *
 * const translatedText = await executor.executeAsyncBatch(
 *   textToTranslate,
 *   (input) => ({
 *     "$text": input
 *   })
 * );
 *
 * // persist the translated text for later use
 */
export type ExecuteFunctionAsyncBatch = <A>(inputData: Iterable<A>, variableInstancesCreator: VariableInstancesCreator<A>, context?: BatchExecuteContext) => Promise<Array<SettledArcadePromise>>;

/**
 * The context of the Arcade expression for batch execution.
 *
 * @since 4.33
 */
export interface BatchExecuteContext extends ExecuteContext {
  /**
   * The maximum number of concurrent executions of the expression.
   *
   * @default 64
   */
  maxConcurrency?: number;
}

/**
 * Callback function used to create profile variable instances from generic input data.
 *
 * @param inputData - Input data used to hydrate the profile variable instances.
 * @returns An object of profile variable instances represented as key/value pairs where the
 *   key is the name of a [Variable](https://developers.arcgis.com/javascript/latest/references/core/arcade/#Variable) defined when creating the executor,
 *   whose value must be of type [ProfileVariableInstance](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ProfileVariableInstance).
 * @since 4.33
 */
export type VariableInstancesCreator<A> = (inputData: A) => ProfileVariableInstances;

/** @internal */
export interface DebugExecutorOptions extends ExecutorOptions {
  /** @internal */
  breakpoints?: Breakpoint[];
}

/** @internal */
export interface ArcadeDebugExecutor {
  /**
   * @param profileVariableInstances - An object of key/value pairs where the key is the name of a
   *   [Variable](https://developers.arcgis.com/javascript/latest/references/core/arcade/#Variable) defined when creating the executor, whose value must be of type
   *   [ProfileVariableInstance](https://developers.arcgis.com/javascript/latest/references/core/arcade/#ProfileVariableInstance).
   * @param context - The context with which to execute the expression.
   * @internal
   */
  executeAsync(profileVariableInstances: ProfileVariableInstances, context?: ExecuteContext): Promise<DebugValue>;
}