/**
 * Vault Client - Handles HashiCorp Vault integration for configuration
 */

import vault from 'node-vault';

export interface VaultConfig {
  addr: string;
  roleId?: string;
  secretId?: string;
  translatorConfigPath: string;
}

export interface TranslatorConfig {
  walletAllowlist?: string[];
  maxGasLimit?: string;
  maxGasPriceWei?: string;
  minGasPriceWei?: string;
  methodAllowlist?: string[];
  methodDenylist?: string[];
}

export class VaultClient {
  private client: vault.client;
  private config: VaultConfig;
  private token?: string;
  private tokenRenewalInterval?: NodeJS.Timeout;

  constructor(config: VaultConfig) {
    this.config = config;
    this.client = vault({
      endpoint: config.addr,
      apiVersion: 'v1',
    });
  }

  async initialize(): Promise<void> {
    if (!this.config.roleId || !this.config.secretId) {
      throw new Error('Vault AppRole credentials (roleId and secretId) are required');
    }

    try {
      // Authenticate using AppRole
      const response = await this.client.approleLogin({
        role_id: this.config.roleId,
        secret_id: this.config.secretId,
      });

      if (!response.auth || !response.auth.client_token) {
        throw new Error('Vault authentication failed: no token received');
      }

      this.token = response.auth.client_token as string;
      this.client.token = this.token;

      // Start token renewal loop
      const leaseDuration = response.auth.lease_duration || 3600;
      const renewInterval = (leaseDuration * 1000 * 0.8); // Renew at 80% of lease duration

      this.tokenRenewalInterval = setInterval(async () => {
        await this.renewToken();
      }, renewInterval);

      console.log('Vault authentication successful');
    } catch (error: any) {
      throw new Error(`Vault initialization failed: ${error.message}`);
    }
  }

  private async renewToken(): Promise<void> {
    if (!this.token) {
      console.warn('No Vault token to renew');
      return;
    }

    try {
      const response = await this.client.tokenRenewSelf();
      if (response.auth && response.auth.client_token) {
        this.token = response.auth.client_token as string;
        this.client.token = this.token;
        console.log('Vault token renewed successfully');
      }
    } catch (error: any) {
      console.warn('Vault token renewal failed:', error.message);
      // Attempt to re-authenticate
      if (this.config.roleId && this.config.secretId) {
        try {
          await this.initialize();
        } catch (reauthError) {
          console.error('Vault re-authentication failed:', reauthError);
        }
      }
    }
  }

  async getTranslatorConfig(): Promise<TranslatorConfig> {
    if (!this.token) {
      throw new Error('Vault client not initialized. Call initialize() first.');
    }

    try {
      // Vault KV v2 API uses /data/ path
      const path = this.config.translatorConfigPath;
      const response = await this.client.read(path);

      if (!response.data || !response.data.data) {
        return {};
      }

      const data = response.data.data;
      const config: TranslatorConfig = {};

      if (data.walletAllowlist) {
        config.walletAllowlist = Array.isArray(data.walletAllowlist)
          ? data.walletAllowlist
          : data.walletAllowlist.split(',').map((s: string) => s.trim());
      }

      if (data.maxGasLimit) {
        config.maxGasLimit = String(data.maxGasLimit);
      }

      if (data.maxGasPriceWei) {
        config.maxGasPriceWei = String(data.maxGasPriceWei);
      }

      if (data.minGasPriceWei) {
        config.minGasPriceWei = String(data.minGasPriceWei);
      }

      if (data.methodAllowlist) {
        config.methodAllowlist = Array.isArray(data.methodAllowlist)
          ? data.methodAllowlist
          : data.methodAllowlist.split(',').map((s: string) => s.trim());
      }

      if (data.methodDenylist) {
        config.methodDenylist = Array.isArray(data.methodDenylist)
          ? data.methodDenylist
          : data.methodDenylist.split(',').map((s: string) => s.trim());
      }

      return config;
    } catch (error: any) {
      if (error.response && error.response.statusCode === 404) {
        console.warn(`Vault config path not found: ${this.config.translatorConfigPath}`);
        return {};
      }
      throw new Error(`Failed to read Vault config: ${error.message}`);
    }
  }

  close() {
    if (this.tokenRenewalInterval) {
      clearInterval(this.tokenRenewalInterval);
    }
  }
}
