import type {
  JoanGlyphEngine,
  SetSpriteOptions,
  SpriteName,
  SpriteRecipe,
  TransitionName,
} from "./index.js";

export type DirectorSprite = SpriteName | SpriteRecipe;

export interface StateDirectorOptions {
  deepThinkingAfterMs?: number;
  stillWorkingAfterMs?: number;
  transition?: TransitionName;
}

export interface DirectorSetOptions extends SetSpriteOptions {
  reason?: string;
  sprite?: DirectorSprite;
}

export interface StateChangeDetail {
  state: string;
  from: string;
  to: string;
  reason: string;
}

export interface DirectorRunContext {
  signal?: AbortSignal;
  director: StateDirector;
  engine: JoanGlyphEngine;
}

export interface DirectorRunOptions extends StateDirectorOptions {
  signal?: AbortSignal;
  thinking?: StateDirectorOptions & DirectorSetOptions;
  complete?: DirectorSetOptions;
  fail?: DirectorSetOptions;
  cancel?: DirectorSetOptions;
  successSprite?: DirectorSprite;
  errorSprite?: DirectorSprite;
  cancelledSprite?: DirectorSprite;
}

export class StateDirector extends EventTarget {
  constructor(engine: JoanGlyphEngine, options?: StateDirectorOptions);
  readonly engine: JoanGlyphEngine;
  readonly options: Required<StateDirectorOptions>;
  readonly timers: Set<ReturnType<typeof setTimeout>>;
  state: string;
  destroyed: boolean;

  addEventListener(
    type: "statechange",
    listener: (this: StateDirector, event: CustomEvent<StateChangeDetail>) => unknown,
    options?: boolean | AddEventListenerOptions,
  ): void;
  addEventListener(
    type: string,
    listener: EventListenerOrEventListenerObject | null,
    options?: boolean | AddEventListenerOptions,
  ): void;

  clearTimers(): void;
  set(sprite: DirectorSprite, options?: DirectorSetOptions): this;
  beginThinking(options?: StateDirectorOptions & DirectorSetOptions): this;
  complete(options?: DirectorSetOptions): this;
  fail(options?: DirectorSetOptions): this;
  cancel(options?: DirectorSetOptions): this;
  reset(options?: DirectorSetOptions): this;
  run<T>(
    work:
      | PromiseLike<T>
      | ((context: DirectorRunContext) => T | PromiseLike<T>),
    options?: DirectorRunOptions,
  ): Promise<T>;
  destroy(): void;
}

export default StateDirector;
