import type { Channel, ResolvedCredentials } from '@zyphon/shared-types';
import type {
  ProviderAdapter,
  ProviderHealth,
  ProviderDefinition,
} from '@zyphon/provider-contracts';
import type { CredentialVault } from './credential-vault.js';

export class ProviderRegistry {
  private adapters = new Map<string, ProviderAdapter>();
  private definitions = new Map<string, ProviderDefinition>();
  private vault: CredentialVault;

  constructor(vault: CredentialVault) {
    this.vault = vault;
  }

  public registerAdapter(adapter: ProviderAdapter, definition?: ProviderDefinition): void {
    this.adapters.set(adapter.id, adapter);
    if (definition) {
      this.definitions.set(adapter.id, definition);
    }
  }

  public unregisterAdapter(id: string): void {
    this.adapters.delete(id);
    this.definitions.delete(id);
  }

  public getDefinition(id: string): ProviderDefinition | undefined {
    return this.definitions.get(id);
  }

  public getAdapter(id: string): ProviderAdapter | undefined {
    return this.adapters.get(id);
  }

  public getAllAdapters(): ProviderAdapter[] {
    return Array.from(this.adapters.values());
  }

  public getAdaptersForChannel(channel: Channel): ProviderAdapter[] {
    return Array.from(this.adapters.values()).filter((a) => a.channel === channel);
  }

  /**
   * Merges non-secret provider definition config (from providers.yaml) with
   * decrypted credentials from the encrypted vault.
   */
  public getResolvedCredentials(id: string): ResolvedCredentials {
    const defConfig = (this.definitions.get(id)?.config || {}) as Record<string, string | number | boolean>;
    const secrets = this.vault.getSecret(id) || {};
    return {
      ...defConfig,
      ...secrets,
    };
  }

  public async checkHealth(id: string): Promise<ProviderHealth> {
    const adapter = this.adapters.get(id);
    if (!adapter) {
      return {
        status: 'down',
        latency_ms: 0,
        checked_at: Date.now(),
        message: `Provider '${id}' not found`,
      };
    }

    const creds = this.getResolvedCredentials(id);
    try {
      const health = await adapter.healthCheck(creds);
      return health;
    } catch (err: any) {
      return {
        status: 'down',
        latency_ms: 0,
        checked_at: Date.now(),
        message: err.message || 'Health check error',
      };
    }
  }

  public async checkAllHealth(): Promise<Record<string, ProviderHealth>> {
    const results: Record<string, ProviderHealth> = {};
    for (const [id] of this.adapters.entries()) {
      results[id] = await this.checkHealth(id);
    }
    return results;
  }
}

