import type VoxelLayer from "@arcgis/core/layers/VoxelLayer.js";
import type ImageryLayer from "@arcgis/core/layers/ImageryLayer.js";
import type ImageryTileLayer from "@arcgis/core/layers/ImageryTileLayer.js";
import type FeatureLayer from "@arcgis/core/layers/FeatureLayer.js";
import type WebMap from "@arcgis/core/WebMap.js";
import type WebScene from "@arcgis/core/WebScene.js";
import type Map from "@arcgis/core/Map.js";
import type SpatialReference from "@arcgis/core/geometry/SpatialReference.js";
import type { Diagnostic as ArcadeLanguageServiceDiagnostic } from "@arcgis/arcade-languageservice";
import type { ApiItem, BundleType, ProfileId } from "@arcgis/languages-api-utils";
import type { ProfileVariableInstances, ArcadeServices } from "@arcgis/core/arcade.js";

export type Diagnostic = ArcadeLanguageServiceDiagnostic;

/**
 * Editor Profile definition used for configuring the editor:
 * - presenting to the user the Arcade API
 * - validating the expression
 * - proposing code completion
 * - formatting code
 */
export interface IEditorProfileDefinition {
  /** Optional label for the profile. Used in the editor when displaying the variables panel. */
  label?: string;
  /**
   * The collection of variables that will be passed to the expression at execution time.
   * The variable declarations are used to help the editor provide validation and completion.
   */
  variables: IProfileVariable[];
  /** The version of the Arcade API that the editor should use during validation and completion. */
  apiVersion?: string;
  /** The Arcade API bundles that the editor should use during validation and completion. */
  bundles?: BundleType[];
  /** A collection of additional Arcade API items that will be provided at runtime. */
  additionalApiItems?: ApiItem[];
  /** A collection of api item names to hide. */
  hiddenApiItems?: string[];
}

export interface IPredefinedProfile {
  /** The well known profile ID. */
  id: ProfileId;
  /** The variables that should be disabled for the profile. */
  disabledVariables?: string[];
  /** Provides the definitions for the definition based variables (feature, featureSet, featureSetCollection). */
  definitions: VariableDefinitions;
  /** A collection of api item names to hide. */
  hiddenApiItems?: string[];
}

/** The type of value variables supported. */
export type ProfileVariableValueType = "boolean" | "date" | "dateOnly" | "geometry" | "knowledgeGraph" | "number" | "text" | "time";

/**
 * The supported profile variable types.
 *
 * @internalremarks TODO: hopefully align with \@arcgis/core's Variable type in the future
 */
export type IProfileVariable = IProfileArray | IProfileDictionary | IProfileFeature | IProfileFeatureSet | IProfileFeatureSetCollection | IProfilePixel | IProfileValue | IProfileVoxel;

/** Properties common to all profile variables. */
export interface IProfileVariableBase {
  /** Name of the variable */
  name: string;
  /** Description for the variable */
  description?: string;
}

/** Single value profile variable declaration. */
export interface IProfileValue extends IProfileVariableBase {
  readonly type: ProfileVariableValueType;
}

/** Dictionary profile variable declaration. Properties of a dictionary are profile variables. */
export interface IProfileDictionary extends IProfileVariableBase {
  readonly type: "dictionary";
  /** The dictionary properties. Properties are variables as well. */
  properties?: IProfileVariable[];
}

/** Dictionary profile variable declaration. Properties of a dictionary are profile variables. */
export interface IProfileArray extends IProfileVariableBase {
  readonly type: "array";
  /** The type for the array elements. */
  elementType?: IProfileVariable;
}

/**
 * Feature profile variable declaration.
 * Features contains values that can be accessed by a name (aka field name or attribute name).
 * Optionally features can have a geometry that can be accessed by a specialized Arcade function.
 */
export interface IProfileFeature extends IProfileVariableBase {
  readonly type: "feature";
  /** Describes how the editor will find the definition for the feature. */
  definition?: FeatureDefinition;
}

/**
 * FeatureSet profile variable declaration.
 * FeatureSet represents a collection of Features.
 * Individual feature or subset of features can be accessed by using specialized Arcade functions.
 */
export interface IProfileFeatureSet extends IProfileVariableBase {
  readonly type: "featureSet";
  /** Describes how the editor will find the definition for the feature set. */
  definition?: FeatureSetDefinition;
}

/**
 * FeatureSetCollection profile variable declaration.
 * FeatureSetCollections are used to represent Feature Services or Web Maps in Arcade expressions.
 * FeatureSetCollections expose two collections of Layers and Tables.
 */
export interface IProfileFeatureSetCollection extends IProfileVariableBase {
  readonly type: "featureSetCollection";
  /** Describes how the featureSetCollection will find its definition. */
  definition?: FeatureSetCollectionDefinition;
}

/** Voxel profile variable declaration. */
export interface IProfileVoxel extends IProfileVariableBase {
  readonly type: "voxel";
  /** Describes how the editor will find the definition for the voxel. */
  definition?: VoxelDefinition;
}

/** Pixel profile variable declaration. */
export interface IProfilePixel extends IProfileVariableBase {
  readonly type: "pixel";
  /** Describes how the editor will find the definition for the pixel. */
  definition?: PixelDefinition;
}

export type IFeatureLikeProfile = IProfileFeature | IProfilePixel | IProfileVoxel;

/** Describes a portal item. */
export interface IPortalItemProperties {
  /** The unique portal item id. */
  id: string;
  /** The optional portal url. Default: www.arcgis.com. */
  portal?: { url: string; };
}

/** WebMap and Service FeatureSetCollection can be defined by a portal item. */
export interface IPortalItemDefinition {
  /** Describes a portal item. */
  portalItem: IPortalItemProperties;
}

/** FeatureLayer can be defined from a Feature Layer portal item. */
export interface IFeatureLayerItemDefinition extends IPortalItemDefinition {
  /** The layer id in the feature layer portal item. Default to 0. */
  layerId?: number;
}

/** FeatureLayers and GroupLayers can be created by providing the url to the resource in a feature service. */
export interface IUrlDefinition {
  /** The url of the resource. */
  url?: string | null;
}

/** The most basic way to define a feature or feature set is by passing a collection of fields */
export type IFieldsDefinition = Pick<FeatureLayer, "fields">;

/**
 * FeatureDefinition represents various ways a Feature variable could be defined.
 * Layer instance is the most common way to define a feature,
 * the minimum required is that the layer instance exposes a collection of fields.
 * An object with just fields can be used to define a feature from a collection of fields.
 * An object with a url property can be used to define a feature from a feature service.
 * An object with minimal portal item properties and a layer id can be used to define a feature from a feature layer portal item.
 * For the last two options, a feature layer instance will be created by the editor profile.
 */
export type FeatureDefinition = IFeatureLayerItemDefinition | IFieldsDefinition | IUrlDefinition;

/**
 * VoxelDefinition represents various ways a Voxel variable could be defined.
 * Layer instance is the most common way to define a voxel.
 */
export type VoxelDefinition = IFeatureLayerItemDefinition | IUrlDefinition | VoxelLayer;

/** PixelDefinition represents various ways a Pixel variable could be defined. */
export type PixelDefinition = IFeatureLayerItemDefinition | ImageryLayer | ImageryTileLayer | IUrlDefinition;

/**
 * FeatureSetDefinition represents the various ways a FeatureSet variable could be defined.
 * See FeatureDefinition for more details.
 */
export type FeatureSetDefinition = IFeatureLayerItemDefinition | IFieldsDefinition | IUrlDefinition;

/**
 * FeatureSetCollectionDefinition represents the various ways a FeatureSetCollection could be defined
 * as a feature service or a web map.
 */
export type FeatureSetCollectionDefinition = IPortalItemDefinition | IUrlDefinition | Map | WebMap | WebScene;

/** A collection of variables that make up a profile definition. */
export type VariableDefinitions = Record<string, FeatureDefinition | FeatureSetCollectionDefinition | FeatureSetDefinition | IProfileVariable[] | undefined>;

export interface IExtendedPredefinedProfile extends IPredefinedProfile {
  additionalVariables?: IProfileVariable[];
}

export interface IEditorCodeSuggestionGroup {
  /** Label for the suggestion group */
  label: string;
  /** List of suggestions for the group */
  suggestions: IEditorCodeSuggestion[];
}

export interface IEditorCodeSuggestion {
  /** A short label for the suggestion. Can be thought as a title */
  label: string;
  /** A short description that will be showed below the label in the list of suggestions panel */
  description?: string;
  /** Markdown string that will be rendered along with the code in the detail panel */
  documentation?: string;
  /** The code for the suggestion. Will be injected in the editor is selected */
  code: string;
}

export type SidePanelName = "api" | "none" | "suggestions" | "variables";

export interface IContextReferences {
  /**
   * Spatial reference object used to define the spatial reference for the arcade runtime.
   * By defaults, the spatial reference is set to Web Mercator (wkid: 3857).
   */
  spatialReference?: SpatialReference | { wkid: number; } | null;
  /**
   * Defines the default time zone in which to create and display Arcade date types.
   * By default, the time zone is set to "system".
   */
  timeZone?: string;
  /** Defines the "services" passed to the arcade executor. */
  services?: ArcadeServices;
}

/**
 * If a profile doesn't contain a map, the spatial reference of geometries will be defaulted to
 * wkid: 3857.
 * The test context objects allows to set the execution spatial reference for such scenario.
 */
export interface IEditorTestContext extends IContextReferences {
  /**
   * An object of key/value pairs where the key is the name of a profile variable.
   * The key's value must be of type ArcGIS core ProfileVariableInstance.
   */
  profileVariableInstances: ProfileVariableInstances;
}