export enum ServiceState {
  /**
   * The service instance was created.
   * This is the starting point for any service.
   */
  Initialized,
  /**
   * After the `onCreate()` callback was called.
   */
  Created,
  /**
   * After the `onStart()` callback was called.
   */
  Started,
  /**
   * After the `onStop()` callback was called.
   */
  Stopped,
  /**
   * After the `onDestroy()` callback was called.
   */
  Destroyed,
}

export class Service extends EventTarget {
  /**
   * When set it will be used as the key to register and lookup a container.
   */
  static key?: string

  #state: ServiceState = ServiceState.Initialized

  get currentState(): ServiceState {
    return this.#state
  }

  /**
   * Called only once when the service is created. Use this for one-time setup
   * (e.g. fetching initial data or configuration).
   */
  protected async onCreate?(): Promise<void>

  /**
   * Called when the service is started (either explicitly or implicitly by the first method call).
   * Useful for setting up resources or connections that might need to be re-established
   * if the service was previously stopped.
   */
  protected async onStart?(): Promise<void>

  /**
   * Called when the service is stopped (e.g., because the application is closing).
   * Use this to release resources or clean up.
   */
  protected async onStop?(): Promise<void>

  /**
   * Called just before the service is destroyed.
   */
  protected async onDestroy?(): Promise<void>

  /**
   * Start the service explicitly. It won't be called again until the service stops first.
   */
  async start(): Promise<void> {
    if (this.#state !== ServiceState.Initialized) {
      return
    }
    if (this.onCreate) {
      await this.onCreate()
      if (this.isDestroyed()) {
        return
      }
    }
    this.#state = ServiceState.Created
    this.dispatchEvent(new CustomEvent('service:created'))
    if (this.onStart) {
      await this.onStart()
      if (this.isDestroyed()) {
        return
      }
    }
    this.#state = ServiceState.Started
    this.dispatchEvent(new CustomEvent('service:started'))
  }

  /**
   * Stop the service explicitly. This allows the service to release resources, close connections, etc.
   */
  public async stop(): Promise<void> {
    if (![ServiceState.Started, ServiceState.Initialized, ServiceState.Created].includes(this.#state)) {
      return
    }
    this.#state = ServiceState.Stopped
    if (this.onStop) {
      await this.onStop()
    }
    this.dispatchEvent(new CustomEvent('service:stopped'))
  }

  public async destroy(): Promise<void> {
    if (this.#state === ServiceState.Destroyed) {
      return
    }
    this.#state = ServiceState.Destroyed
    if (this.onDestroy) {
      await this.onDestroy()
    }
    this.dispatchEvent(new CustomEvent('service:destroyed'))
  }

  isDestroyed(): boolean {
    return this.#state === ServiceState.Destroyed
  }
}
