import type Field from "@arcgis/core/layers/support/Field.js";
import type KnowledgeGraph from "@arcgis/core/rest/knowledgeGraph/KnowledgeGraph.js";
import type Geometry from "@arcgis/core/geometry/Geometry.js";
import type { createArcadeExecutor } from "@arcgis/core/arcade.js";
import type { GeometryUnion } from "@arcgis/core/geometry/types.js";
import type { DebugValue as ArcadeDebugValue } from "@arcgis/core/arcade/debug.js";

/**
 * Breakpoint breakpoints used in the arcade debugger.
 *
 * @internal
 */
export type Breakpoint = {
  line: number;
};

/** @internal */
export type ArcadeDebugger = NonNullable<NonNullable<NonNullable<Parameters<typeof createArcadeExecutor>[2]>["services"]>["debugger"]>;

/** @internal */
export type ArcadeDebugProgramState = Parameters<NonNullable<ArcadeDebugger>["pause"]>[1];

/** @internal */
export interface IArcadeRuntimeDictionary {
  declaredClass?: string;
  arcadeDeclaredClass?: string;
  keys: () => string[];
  field: (name: string) => unknown;
  geometry?: () => GeometryUnion;
  castAsJson: () => Record<string, unknown>;
}

/** @internal */
export interface IAbortSignal {
  aborted: boolean;
}

/** @internal */
export interface IArcadeRuntimeFeatureSet {
  declaredRootClass: string;
  fields: Field[] | null;
  load: () => Promise<IArcadeRuntimeFeatureSet>;
  iterator: (abortSignal: IAbortSignal) => IArcadeRuntimeFeatureSetIterator;
}

/** @internal */
export interface IArcadeRuntimeFeatureSetIterator {
  nextBatchAsArcadeFeatures: (bsize: number, timeZone: string) => Promise<IArcadeRuntimeDictionary[] | null>;
}

/** @internal */
export interface IArcadeRuntimeImmutableArray {
  length: () => number;
  get: (index: number) => unknown;
  toArray: () => unknown[];
  _elements: unknown[];
}

/** @internal */
export interface IArcadeRuntimeDate {
  declaredRootClass: string;
  toDateTime: () => { setLocale: (locale: string) => { toFormat: (format: string) => string; }; };
  isUnknownTimeZone: boolean;
}

/** @internal */
export interface IArcadeRuntimeDateOnly {
  declaredRootClass: string;
  /** To ISO Date format */
  toString: () => string;
}

/** @internal */
export interface IArcadeRuntimeTimeOnly {
  declaredRootClass: string;
  /** To ISO Time format */
  toString: () => string;
}

/** @internal */
export type ResultType = "array" | "attachment" | "boolean" | "date" | "dateOnly" | "dictionary" | "error" | "feature" | "featureSet" | "geometry" | "knowledgeGraph" | "null" | "number" | "pixel" | "portal" | "text" | "time" | "unknown";

/** @internal */
export interface IArcadeResultBase {
  type: ResultType;
  timeStamp?: Date;
  compilationTime?: number;
  executionTime?: number;
}

/** @internal */
export interface IArcadeResultUnknown extends IArcadeResultBase {
  type: "unknown";
}

/** @internal */
export interface IArcadeResultNull extends IArcadeResultBase {
  type: "null";
}

/** @internal */
export interface IArcadeResultError extends IArcadeResultBase {
  type: "error";
  value: string;
  error: unknown;
}

/** @internal */
export interface IArcadeResultArray extends IArcadeResultBase {
  type: "array";
  value: unknown[];
}

/** @internal */
export interface IArcadeResultDate extends IArcadeResultBase {
  type: "date";
  value: Date | IArcadeRuntimeDate;
}

/** @internal */
export interface IArcadeResultDateOnly extends IArcadeResultBase {
  type: "dateOnly";
  value: IArcadeRuntimeDateOnly;
}

/** @internal */
export interface IArcadeResultTimeOnly extends IArcadeResultBase {
  type: "time";
  value: IArcadeRuntimeTimeOnly;
}

/** @internal */
export interface IArcadeResultString extends IArcadeResultBase {
  type: "text";
  value: string;
}

/** @internal */
export interface IArcadeResultNumber extends IArcadeResultBase {
  type: "number";
  value: number;
}

/** @internal */
export interface IArcadeResultBoolean extends IArcadeResultBase {
  type: "boolean";
  value: boolean;
}

/** @internal */
export interface IArcadeResultDictionary extends IArcadeResultBase {
  type: "dictionary";
  value: IArcadeRuntimeDictionary;
}

/** @internal */
export interface IArcadeResultGeometry extends IArcadeResultBase {
  type: "geometry";
  value: Geometry;
}

/** @internal */
export interface IArcadeResultKnowledgeGraph extends IArcadeResultBase {
  type: "knowledgeGraph";
  value: KnowledgeGraph;
}

/** @internal */
export interface IArcadeResultFeature extends IArcadeResultBase {
  type: "feature";
  value: IArcadeRuntimeDictionary;
}

/** @internal */
export interface IArcadeResultPixel extends IArcadeResultBase {
  type: "pixel";
  value: IArcadeRuntimeDictionary;
}

/** @internal */
export interface IArcadeResultFeatureSet extends IArcadeResultBase {
  type: "featureSet";
  value: IArcadeRuntimeFeatureSet;
  iterator: IArcadeRuntimeFeatureSetIterator | null;
  features: IArcadeRuntimeDictionary[];
}

/** @internal */
export interface IArcadeResultPortal extends IArcadeResultBase {
  type: "portal";
  value: IArcadeRuntimeDictionary;
}

/** @internal */
export interface IArcadeResultAttachment extends IArcadeResultBase {
  type: "attachment";
  value: IArcadeRuntimeDictionary;
}

/** @internal */
export type ArcadeResult = IArcadeResultArray | IArcadeResultAttachment | IArcadeResultBoolean | IArcadeResultDate | IArcadeResultDateOnly | IArcadeResultDictionary | IArcadeResultError | IArcadeResultFeature | IArcadeResultFeatureSet | IArcadeResultGeometry | IArcadeResultKnowledgeGraph | IArcadeResultNull | IArcadeResultNumber | IArcadeResultPixel | IArcadeResultPortal | IArcadeResultString | IArcadeResultTimeOnly | IArcadeResultUnknown;

/**
 * Allows users to provide an external executor for the arcade editor.
 *
 * @internal
 */
export type EditorExternalExecutor = {
  execute: (request: EditorExecuteRequest) => Promise<ArcadeDebugValue>;
};

/** @internal */
export interface ExecuteRequestBase {
  /** Determines whether the script should be executed in debug mode or run mode. */
  mode: string;
  /** Script within the editor */
  script: string;
  /** Function to hook up Console calls to the console panel in the editor */
  console: (log: string) => void;
  /** Alerts caller to cleanup any existing execution sessions for this execution */
  signal: AbortSignal;
}

/** @internal */
export interface EditorExecuteDebugRequest extends ExecuteRequestBase {
  mode: "debug";
  /** Breakpoints for the debugger */
  breakpoints: Breakpoint[];
  /** Function to call when the debugger pauses */
  onDebugPause: ArcadeDebugger["pause"];
}

/** @internal */
export interface EditorExecuteRunRequest extends ExecuteRequestBase {
  mode: "run";
}

/** @internal */
export type EditorExecuteRequest = EditorExecuteDebugRequest | EditorExecuteRunRequest;