/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */

interface ConsoleWithAsyncTagging {
  createTask(name: string): ConsoleTask;
}

interface ConsoleTask {
  run<T>(f: () => T): T;
}

interface ZoneConsoleTask extends Task {
  consoleTask?: ConsoleTask;
}

export class AsyncStackTaggingZoneSpec implements ZoneSpec {
  createTask: ConsoleWithAsyncTagging['createTask'];

  constructor(
    namePrefix: string,
    consoleAsyncStackTaggingImpl: ConsoleWithAsyncTagging = console as any,
  ) {
    this.name = 'asyncStackTagging for ' + namePrefix;
    this.createTask = consoleAsyncStackTaggingImpl?.createTask ?? (() => null);
  }

  // ZoneSpec implementation below.
  name: string;

  onScheduleTask(
    delegate: ZoneDelegate,
    _current: Zone,
    target: Zone,
    task: ZoneConsoleTask,
  ): Task {
    task.consoleTask = this.createTask(`Zone - ${task.source || task.type}`);
    return delegate.scheduleTask(target, task);
  }

  onInvokeTask(
    delegate: ZoneDelegate,
    _currentZone: Zone,
    targetZone: Zone,
    task: ZoneConsoleTask,
    applyThis: any,
    applyArgs?: any[],
  ) {
    let ret;
    if (task.consoleTask) {
      ret = task.consoleTask.run(() => delegate.invokeTask(targetZone, task, applyThis, applyArgs));
    } else {
      ret = delegate.invokeTask(targetZone, task, applyThis, applyArgs);
    }
    return ret;
  }
}
