/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import { BaseTokenStorage } from './base-token-storage.js';
import { KeychainTokenStorage } from './keychain-token-storage.js';
import {
  TokenStorageType,
  type TokenStorage,
  type OAuthCredentials,
} from './types.js';
import { coreEvents } from '../../utils/events.js';
import { TokenStorageInitializationEvent } from '../../telemetry/types.js';
import { FORCE_FILE_STORAGE_ENV_VAR } from '../../services/keychainService.js';

export class HybridTokenStorage extends BaseTokenStorage {
  private storage: TokenStorage | null = null;
  private storageType: TokenStorageType | null = null;
  private storageInitPromise: Promise<TokenStorage> | null = null;

  constructor(serviceName: string) {
    super(serviceName);
  }

  private async initializeStorage(): Promise<TokenStorage> {
    const forceFileStorage = process.env[FORCE_FILE_STORAGE_ENV_VAR] === 'true';

    const keychainStorage = new KeychainTokenStorage(this.serviceName);
    this.storage = keychainStorage;

    const isUsingFileFallback = await keychainStorage.isUsingFileFallback();

    this.storageType = isUsingFileFallback
      ? TokenStorageType.ENCRYPTED_FILE
      : TokenStorageType.KEYCHAIN;

    coreEvents.emitTelemetryTokenStorageType(
      new TokenStorageInitializationEvent(
        isUsingFileFallback ? 'encrypted_file' : 'keychain',
        forceFileStorage,
      ),
    );

    return this.storage;
  }

  private async getStorage(): Promise<TokenStorage> {
    if (this.storage !== null) {
      return this.storage;
    }

    // Use a single initialization promise to avoid race conditions
    if (!this.storageInitPromise) {
      this.storageInitPromise = this.initializeStorage();
    }

    // Wait for initialization to complete
    return this.storageInitPromise;
  }

  async getCredentials(serverName: string): Promise<OAuthCredentials | null> {
    const storage = await this.getStorage();
    return storage.getCredentials(serverName);
  }

  async setCredentials(credentials: OAuthCredentials): Promise<void> {
    const storage = await this.getStorage();
    await storage.setCredentials(credentials);
  }

  async deleteCredentials(serverName: string): Promise<void> {
    const storage = await this.getStorage();
    await storage.deleteCredentials(serverName);
  }

  async listServers(): Promise<string[]> {
    const storage = await this.getStorage();
    return storage.listServers();
  }

  async getAllCredentials(): Promise<Map<string, OAuthCredentials>> {
    const storage = await this.getStorage();
    return storage.getAllCredentials();
  }

  async clearAll(): Promise<void> {
    const storage = await this.getStorage();
    await storage.clearAll();
  }

  async getStorageType(): Promise<TokenStorageType> {
    await this.getStorage();
    return this.storageType!;
  }
}
