// SCB Ledger API Client
// Interface for calling Sovereign Central Bank ledger APIs
// Must be idempotent - uses idempotency-key header

import { logger } from '@/infrastructure/monitoring/logger';
import crypto from 'crypto';

export interface ScbLedgerPostRequest {
  sovereignBankId: string;
  ledgerId: string;
  sourceAccountId: string;
  destinationAccountId: string;
  amount: string;
  currencyCode: string;
  assetType: string;
  referenceId: string;
  metadata?: Record<string, unknown>;
}

export interface ScbLedgerPostResponse {
  transactionId: string;
  ledgerHash: string;
  signature: string;
  timestamp: Date;
}

export interface ScbLedgerClientConfig {
  baseUrl: string; // e.g., 'https://scb-api.example.com'
  apiKey?: string;
  timeout?: number;
  retryConfig?: {
    maxRetries: number;
    retryDelay: number;
  };
}

/**
 * SCB Ledger API Client
 * 
 * REQUIREMENTS:
 * - Must send Idempotency-Key header = referenceId
 * - SCB must return same result for duplicate idempotency keys
 * - Must handle retries with exponential backoff
 */
export class ScbLedgerClient {
  constructor(private config: ScbLedgerClientConfig) {
    this.config = {
      timeout: config.timeout ?? 30000, // 30 seconds
      retryConfig: {
        maxRetries: config.retryConfig?.maxRetries ?? 3,
        retryDelay: config.retryConfig?.retryDelay ?? 1000,
      },
      ...config,
    };
  }

  /**
   * Post entry to SCB ledger (idempotent)
   */
  async postEntry(
    request: ScbLedgerPostRequest
  ): Promise<ScbLedgerPostResponse> {
    const url = `${this.config.baseUrl}/api/v1/ledgers/${request.ledgerId}/entries`;

    logger.info('ScbLedgerClient: Posting to SCB ledger', {
      sovereignBankId: request.sovereignBankId,
      ledgerId: request.ledgerId,
      referenceId: request.referenceId,
      amount: request.amount,
    });

    const payload = {
      sourceAccountId: request.sourceAccountId,
      destinationAccountId: request.destinationAccountId,
      amount: request.amount,
      currencyCode: request.currencyCode,
      assetType: request.assetType,
      metadata: request.metadata || {},
    };

    let lastError: Error | null = null;

    // Retry loop with exponential backoff
    for (let attempt = 0; attempt <= this.config.retryConfig!.maxRetries; attempt++) {
      try {
        const response = await this.makeRequest(url, payload, request.referenceId);

        logger.info('ScbLedgerClient: SCB ledger posted successfully', {
          sovereignBankId: request.sovereignBankId,
          referenceId: request.referenceId,
          transactionId: response.transactionId,
        });

        return response;
      } catch (error) {
        lastError = error instanceof Error ? error : new Error('Unknown error');

        // Don't retry on 4xx errors (client errors)
        if (this.isClientError(lastError)) {
          throw lastError;
        }

        // Retry on 5xx errors or network errors
        if (attempt < this.config.retryConfig!.maxRetries) {
          const delay = this.config.retryConfig!.retryDelay * Math.pow(2, attempt);
          logger.warn('ScbLedgerClient: Retrying after error', {
            attempt: attempt + 1,
            maxRetries: this.config.retryConfig!.maxRetries,
            delay,
            error: lastError.message,
          });

          await this.sleep(delay);
        }
      }
    }

    // All retries exhausted
    throw new Error(
      `SCB ledger API call failed after ${this.config.retryConfig!.maxRetries} retries: ${lastError?.message}`
    );
  }

  /**
   * Make HTTP request to SCB API
   * 
   * TODO: Replace with actual HTTP client (fetch, axios, etc.)
   */
  private async makeRequest(
    url: string,
    payload: any,
    idempotencyKey: string
  ): Promise<ScbLedgerPostResponse> {
    // Example implementation using fetch
    // Replace with your actual HTTP client
    
    const headers: Record<string, string> = {
      'Content-Type': 'application/json',
      'Idempotency-Key': idempotencyKey, // CRITICAL: Must send this header
    };

    if (this.config.apiKey) {
      headers['Authorization'] = `Bearer ${this.config.apiKey}`;
    }

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);

    try {
      const response = await fetch(url, {
        method: 'POST',
        headers,
        body: JSON.stringify(payload),
        signal: controller.signal,
      });

      clearTimeout(timeoutId);

      if (!response.ok) {
        const errorText = await response.text().catch(() => 'Unknown error');
        throw new Error(`SCB API error: ${response.status} ${response.statusText} - ${errorText}`);
      }

      const result = await response.json();

      return {
        transactionId: result.transactionId,
        ledgerHash: result.ledgerHash,
        signature: result.signature,
        timestamp: new Date(result.timestamp || Date.now()),
      };
    } catch (error) {
      clearTimeout(timeoutId);

      if (error instanceof Error && error.name === 'AbortError') {
        throw new Error(`SCB API request timeout after ${this.config.timeout}ms`);
      }

      throw error;
    }
  }

  /**
   * Check if error is a client error (4xx) - don't retry these
   */
  private isClientError(error: Error): boolean {
    // Extract status code from error message if available
    const match = error.message.match(/(\d{3})/);
    if (match) {
      const status = parseInt(match[1], 10);
      return status >= 400 && status < 500;
    }
    return false;
  }

  /**
   * Sleep utility for retry delays
   */
  private sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
}

/**
 * Factory function to create SCB client with default config
 */
export function createScbLedgerClient(
  sovereignBankId: string,
  config?: Partial<ScbLedgerClientConfig>
): ScbLedgerClient {
  // In production, load SCB-specific config from environment or config service
  const defaultConfig: ScbLedgerClientConfig = {
    baseUrl: process.env[`SCB_${sovereignBankId}_API_URL`] || 'https://scb-api.example.com',
    apiKey: process.env[`SCB_${sovereignBankId}_API_KEY`],
    timeout: 30000,
    retryConfig: {
      maxRetries: 3,
      retryDelay: 1000,
    },
  };

  return new ScbLedgerClient({
    ...defaultConfig,
    ...config,
  });
}
