/**
 * Smart Twilio SDK wrapper with retry, rate limiting, and error normalization.
 */

import Twilio from 'twilio';
import type { TwilioCredentials } from './auth.js';

export class TwilioClient {
  private client: ReturnType<typeof Twilio>;
  private credentials: TwilioCredentials;

  constructor(credentials: TwilioCredentials) {
    this.credentials = credentials;
    this.client = Twilio(credentials.apiKey, credentials.apiSecret, {
      accountSid: credentials.accountSid,
    });
  }

  /** Get the raw Twilio client for direct SDK access */
  get raw() {
    return this.client;
  }

  get accountSid() {
    return this.credentials.accountSid;
  }

  /**
   * Execute a Twilio API call with retry logic and error normalization.
   */
  async execute<T>(
    operation: (client: ReturnType<typeof Twilio>) => Promise<T>,
    options: { retries?: number; retryDelay?: number } = {}
  ): Promise<T> {
    const { retries = 2, retryDelay = 1000 } = options;
    let lastError: Error | undefined;

    for (let attempt = 0; attempt <= retries; attempt++) {
      try {
        return await operation(this.client);
      } catch (err: any) {
        lastError = err;

        // Don't retry on auth errors or client errors
        if (err.status && err.status >= 400 && err.status < 500 && err.status !== 429) {
          throw this.normalizeError(err);
        }

        // Rate limited — wait and retry
        if (err.status === 429 && attempt < retries) {
          const delay = retryDelay * Math.pow(2, attempt);
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }

        // Server error — retry
        if (err.status && err.status >= 500 && attempt < retries) {
          await new Promise(resolve => setTimeout(resolve, retryDelay));
          continue;
        }

        throw this.normalizeError(err);
      }
    }

    throw this.normalizeError(lastError!);
  }

  private normalizeError(err: any): Error {
    if (err.code && err.message) {
      return new Error(`Twilio Error ${err.code}: ${err.message}${err.moreInfo ? ` (${err.moreInfo})` : ''}`);
    }
    return err instanceof Error ? err : new Error(String(err));
  }

  /**
   * Validate that credentials work by fetching account info.
   */
  async validateCredentials(): Promise<{ valid: boolean; accountName?: string; error?: string }> {
    try {
      const account = await this.client.api.accounts(this.credentials.accountSid).fetch();
      return { valid: true, accountName: account.friendlyName };
    } catch (err: any) {
      return { valid: false, error: this.normalizeError(err).message };
    }
  }
}
