// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

export type ConstructorT<T> = new (...args: any[]) => T;

/**
 * Container for singletons scoped to a single DevTools universe.
 *
 * When wiring up dependencies, strongly prefer to pass all direct dependencies
 * via constructor, and not just pass a {@link DevToolsContext} around. That would hide
 * dependencies and we want to be explicit.
 */
export interface DevToolsContext {
  get<T>(ctor: ConstructorT<T>): T;
}

/**
 * The actual implementation. Should only be accessed by test-setup code or the bootstrapper.
 */
export class WritableDevToolsContext implements DevToolsContext {
  readonly #instances = new Map<ConstructorT<unknown>, unknown>();

  get<T>(ctor: ConstructorT<T>): T {
    const instance = this.#instances.get(ctor) as T | undefined;
    if (!instance) {
      throw new Error(`No instance for ${ctor.name}. Ensure the bootstrapper creates it.`);
    }
    return instance;
  }

  /** @deprecated Should only be used by existing `instance` accessors. */
  has<T>(ctor: ConstructorT<T>): boolean {
    return this.#instances.has(ctor);
  }

  /**
   * Should only be used by existing `instance` accessors and the bootstrapper.
   */
  set<T>(ctor: ConstructorT<T>, instance: T): void {
    // TODO(crbug.com/458180550): We need to throw here if an instance was already set!
    this.#instances.set(ctor, instance);
  }

  /** @deprecated Should only be used by existing `removeInstance` static methods. */
  delete<T>(ctor: ConstructorT<T>): void {
    this.#instances.delete(ctor);
  }
}

let gInstance: WritableDevToolsContext|null = null;

/**
 * @deprecated Exists to migrate instance() methods.
 */
export function globalInstance(): WritableDevToolsContext {
  if (!gInstance) {
    // TODO(crbug.com/458180550): This should really throw to prevent side-effects and globals
    //                            from leaking all over the place.
    gInstance = new WritableDevToolsContext();
  }
  return gInstance;
}

/**
 * @deprecated Should only be called by test setup and MainImpl
 */
export function setGlobalInstance(context: WritableDevToolsContext|null): void {
  gInstance = context;
}
