export interface RuntimeGenerationToken {
  readonly runtimeGeneration: number;
}

export interface OperatorIntentToken extends RuntimeGenerationToken {
  readonly intentGeneration: number;
}

/**
 * Invalidates asynchronous UI work across both plugin activation changes and
 * successive explicit operator intents.
 *
 * A boolean "mounted" flag is insufficient here: an operation started before
 * active=true -> false -> true would otherwise observe true again and resume.
 */
export class OperatorIntentGeneration {
  private runtimeGeneration = 0;
  private intentGeneration = 0;
  private active = false;

  activateRuntime(): RuntimeGenerationToken {
    this.runtimeGeneration += 1;
    this.active = true;
    return Object.freeze({ runtimeGeneration: this.runtimeGeneration });
  }

  deactivateRuntime(): void {
    if (!this.active) return;
    this.runtimeGeneration += 1;
    this.active = false;
  }

  captureRuntime(): RuntimeGenerationToken | null {
    if (!this.active) return null;
    return Object.freeze({ runtimeGeneration: this.runtimeGeneration });
  }

  beginOperatorIntent(): OperatorIntentToken | null {
    if (!this.active) return null;
    this.intentGeneration += 1;
    return Object.freeze({
      runtimeGeneration: this.runtimeGeneration,
      intentGeneration: this.intentGeneration,
    });
  }

  isRuntimeCurrent(token: RuntimeGenerationToken): boolean {
    return this.active && token.runtimeGeneration === this.runtimeGeneration;
  }

  isOperatorIntentCurrent(token: OperatorIntentToken): boolean {
    return (
      this.isRuntimeCurrent(token)
      && token.intentGeneration === this.intentGeneration
    );
  }
}

export async function awaitWhileIntentCurrent<T>(
  assertCurrent: () => void,
  operation: () => Promise<T>,
): Promise<T> {
  assertCurrent();
  const result = await operation();
  assertCurrent();
  return result;
}
