// Payment Processor Service
// Integration with Stripe and Braintree for IRU subscription payments

import prisma from '@/shared/database/prisma';
import { notificationService } from '@/core/iru/notifications/notification.service';
import { logger } from '@/infrastructure/monitoring/logger';
import { retryWithBackoff } from '@/shared/utils/retry';
import { stripeCircuitBreaker, braintreeCircuitBreaker } from '@/shared/utils/circuit-breaker';

export interface PaymentRequest {
  subscriptionId: string;
  amount: number;
  currency: string;
  paymentMethod: 'stripe' | 'braintree';
  paymentMethodId: string;
  description?: string;
}

export interface PaymentResult {
  success: boolean;
  transactionId?: string;
  paymentIntentId?: string;
  status: string;
  amount: number;
  currency: string;
  error?: string;
}

export class PaymentProcessorService {
  /**
   * Process payment
   */
  async processPayment(request: PaymentRequest): Promise<PaymentResult> {
    if (request.paymentMethod === 'stripe') {
      return this.processStripePayment(request);
    } else if (request.paymentMethod === 'braintree') {
      return this.processBraintreePayment(request);
    } else {
      throw new Error(`Unsupported payment method: ${request.paymentMethod}`);
    }
  }

  /**
   * Process Stripe payment
   */
  private async processStripePayment(request: PaymentRequest): Promise<PaymentResult> {
    const stripeSecretKey = process.env.STRIPE_SECRET_KEY || '';
    const stripeApiBase = 'https://api.stripe.com/v1';

    try {
      // Create payment intent with retry and circuit breaker
      const paymentIntentResponse = await stripeCircuitBreaker.execute(async () => {
        return await retryWithBackoff(
          async () => {
            return await fetch(`${stripeApiBase}/payment_intents`, {
              method: 'POST',
              headers: {
                'Authorization': `Bearer ${stripeSecretKey}`,
                'Content-Type': 'application/x-www-form-urlencoded',
              },
              body: new URLSearchParams({
                amount: (request.amount * 100).toString(), // Convert to cents
                currency: request.currency.toLowerCase(),
                payment_method: request.paymentMethodId,
                confirmation_method: 'manual',
                confirm: 'true',
                description: request.description || `IRU Subscription ${request.subscriptionId}`,
              }),
            });
          },
          {
            maxRetries: 3,
            initialDelayMs: 1000,
            onRetry: (attempt, error) => {
              logger.warn('Stripe API retry', {
                attempt,
                error: error.message,
              });
            },
          }
        );
      });

      if (!paymentIntentResponse.ok) {
        const error = await paymentIntentResponse.json();
        throw new Error(`Stripe API error: ${error.error?.message || 'Unknown error'}`);
      }

      const paymentIntent = await paymentIntentResponse.json();

      return {
        success: paymentIntent.status === 'succeeded',
        paymentIntentId: paymentIntent.id,
        transactionId: paymentIntent.id,
        status: paymentIntent.status,
        amount: request.amount,
        currency: request.currency,
      };
    } catch (error) {
      return {
        success: false,
        status: 'failed',
        amount: request.amount,
        currency: request.currency,
        error: error instanceof Error ? error.message : 'Unknown error',
      };
    }
  }

  /**
   * Process Braintree payment
   */
  private async processBraintreePayment(request: PaymentRequest): Promise<PaymentResult> {
    const braintreeMerchantId = process.env.BRAINTREE_MERCHANT_ID || '';
    const braintreePublicKey = process.env.BRAINTREE_PUBLIC_KEY || '';
    const braintreePrivateKey = process.env.BRAINTREE_PRIVATE_KEY || '';
    const braintreeApiBase = process.env.BRAINTREE_API_BASE || 'https://api.braintreegateway.com';

    try {
      // Get Braintree client token (would typically be done on frontend)
      // For server-side, we'll use the payment method nonce directly

      const saleResponse = await braintreeCircuitBreaker.execute(async () => {
        return await retryWithBackoff(
          async () => {
            return await fetch(`${braintreeApiBase}/merchants/${braintreeMerchantId}/transactions`, {
        method: 'POST',
        headers: {
          'Authorization': `Basic ${Buffer.from(`${braintreePublicKey}:${braintreePrivateKey}`).toString('base64')}`,
          'Content-Type': 'application/xml',
        },
              body: this.buildBraintreeTransactionXML(request),
            });
          },
          {
            maxRetries: 3,
            initialDelayMs: 1000,
            onRetry: (attempt, error) => {
              logger.warn('Braintree API retry', {
                attempt,
                error: error.message,
              });
            },
          }
        );
      });

      if (!saleResponse.ok) {
        throw new Error(`Braintree API error: ${saleResponse.status} ${saleResponse.statusText}`);
      }

      const transactionData = await this.parseBraintreeResponse(await saleResponse.text());

      return {
        success: transactionData.status === 'authorized' || transactionData.status === 'settled',
        transactionId: transactionData.id,
        status: transactionData.status,
        amount: request.amount,
        currency: request.currency,
      };
    } catch (error) {
      return {
        success: false,
        status: 'failed',
        amount: request.amount,
        currency: request.currency,
        error: error instanceof Error ? error.message : 'Unknown error',
      };
    }
  }

  /**
   * Build Braintree transaction XML
   */
  private buildBraintreeTransactionXML(request: PaymentRequest): string {
    return `<?xml version="1.0" encoding="UTF-8"?>
<transaction>
  <type>sale</type>
  <amount>${request.amount}</amount>
  <payment-method-nonce>${request.paymentMethodId}</payment-method-nonce>
  <options>
    <submit-for-settlement>true</submit-for-settlement>
  </options>
</transaction>`;
  }

  /**
   * Parse Braintree XML response
   */
  private async parseBraintreeResponse(xml: string): Promise<any> {
    // Simplified XML parsing - in production, use proper XML parser
    const idMatch = xml.match(/<id>([^<]+)<\/id>/);
    const statusMatch = xml.match(/<status>([^<]+)<\/status>/);

    return {
      id: idMatch ? idMatch[1] : '',
      status: statusMatch ? statusMatch[1] : 'unknown',
    };
  }

  /**
   * Handle payment webhook
   */
  async handleWebhook(provider: 'stripe' | 'braintree', payload: any, signature: string): Promise<void> {
    if (provider === 'stripe') {
      return this.handleStripeWebhook(payload, signature);
    } else {
      return this.handleBraintreeWebhook(payload, signature);
    }
  }

  /**
   * Handle Stripe webhook
   */
  private async handleStripeWebhook(payload: any, signature: string): Promise<void> {
    const stripeWebhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
    if (!stripeWebhookSecret) {
      throw new Error('STRIPE_WEBHOOK_SECRET environment variable is not set');
    }

    // Verify webhook signature using Stripe's signature verification
    const isValid = await this.verifyStripeSignature(payload, signature, stripeWebhookSecret);
    if (!isValid) {
      throw new Error('Invalid Stripe webhook signature');
    }

    // Process webhook event
    const event = payload;
    if (event.type === 'payment_intent.succeeded') {
      const paymentIntent = event.data.object;
      await this.updateSubscriptionPaymentStatus(paymentIntent.metadata?.subscriptionId, 'paid', {
        paymentIntentId: paymentIntent.id,
        amount: paymentIntent.amount / 100, // Convert from cents
        currency: paymentIntent.currency,
      });
    } else if (event.type === 'payment_intent.payment_failed') {
      const paymentIntent = event.data.object;
      await this.updateSubscriptionPaymentStatus(paymentIntent.metadata?.subscriptionId, 'failed', {
        paymentIntentId: paymentIntent.id,
        error: paymentIntent.last_payment_error?.message,
      });
    }
  }

  /**
   * Verify Stripe webhook signature
   */
  private async verifyStripeSignature(payload: any, signature: string, secret: string): Promise<boolean> {
    // Stripe signature verification
    // In production, use stripe library: stripe.webhooks.constructEvent(payload, signature, secret)
    // For now, basic validation
    if (!signature || !secret) {
      return false;
    }

    // Import crypto for HMAC verification
    const crypto = await import('crypto');
    const elements = signature.split(',');
    const signatureHash = elements.find((el) => el.startsWith('v1='))?.split('=')[1];
    
    if (!signatureHash) {
      return false;
    }

    // Reconstruct signed payload
    const timestamp = elements.find((el) => el.startsWith('t='))?.split('=')[1];
    const signedPayload = `${timestamp}.${JSON.stringify(payload)}`;
    const expectedSignature = crypto
      .createHmac('sha256', secret)
      .update(signedPayload)
      .digest('hex');

    return crypto.timingSafeEqual(Buffer.from(signatureHash), Buffer.from(expectedSignature));
  }

  /**
   * Handle Braintree webhook
   */
  private async handleBraintreeWebhook(payload: any, signature: string): Promise<void> {
    const braintreeWebhookSecret = process.env.BRAINTREE_WEBHOOK_SECRET;
    if (!braintreeWebhookSecret) {
      throw new Error('BRAINTREE_WEBHOOK_SECRET environment variable is not set');
    }

    // Verify webhook signature
    const isValid = await this.verifyBraintreeSignature(payload, signature, braintreeWebhookSecret);
    if (!isValid) {
      throw new Error('Invalid Braintree webhook signature');
    }

    // Process webhook event
    const kind = payload.kind;
    if (kind === 'transaction_settled' || kind === 'transaction_settlement_confirmed') {
      const transaction = payload.transaction;
      await this.updateSubscriptionPaymentStatus(transaction.customFields?.subscriptionId, 'paid', {
        transactionId: transaction.id,
        amount: parseFloat(transaction.amount),
        currency: transaction.currencyIsoCode,
      });
    } else if (kind === 'transaction_failed') {
      const transaction = payload.transaction;
      await this.updateSubscriptionPaymentStatus(transaction.customFields?.subscriptionId, 'failed', {
        transactionId: transaction.id,
        error: transaction.status,
      });
    }
  }

  /**
   * Verify Braintree webhook signature
   */
  private async verifyBraintreeSignature(payload: any, signature: string, secret: string): Promise<boolean> {
    // Braintree signature verification
    if (!signature || !secret) {
      return false;
    }

    const crypto = await import('crypto');
    const expectedSignature = crypto
      .createHmac('sha256', secret)
      .update(JSON.stringify(payload))
      .digest('hex');

    return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
  }

  /**
   * Update subscription payment status
   */
  private async updateSubscriptionPaymentStatus(
    subscriptionId: string | undefined,
    status: 'paid' | 'failed',
    metadata: any
  ): Promise<void> {
    if (!subscriptionId) {
      logger.warn('No subscription ID in payment webhook metadata', { metadata });
      return;
    }

    try {
      const subscription = await prisma.iruSubscription.findUnique({
        where: { subscriptionId },
        include: {
          inquiry: {
            select: {
              contactEmail: true,
            },
          },
        },
      });

      if (!subscription) {
        logger.error('Subscription not found for payment webhook', { subscriptionId });
        return;
      }

      // Update subscription payment status
      await prisma.iruSubscription.update({
        where: { subscriptionId },
        data: {
          iruGrantFeePaid: status === 'paid',
          iruGrantFeePaidAt: status === 'paid' ? new Date() : undefined,
          paymentReference: metadata.transactionId || metadata.paymentIntentId,
          metadata: {
            ...((subscription.metadata as any) || {}),
            paymentStatus: status,
            paymentMetadata: metadata,
            paymentUpdatedAt: new Date(),
          },
          updatedAt: new Date(),
        },
      });

      // Send notification
      const contactEmail = subscription.inquiry?.contactEmail;
      if (contactEmail) {
        await notificationService.sendNotification({
          recipient: contactEmail,
          recipientType: 'email',
          template: status === 'paid' ? 'payment-success' : 'payment-failed',
          variables: {
            subscriptionId,
            amount: metadata.amount,
            currency: metadata.currency,
            transactionId: metadata.transactionId || metadata.paymentIntentId,
          },
        });
      }

      logger.info('Subscription payment status updated', {
        subscriptionId,
        status,
        transactionId: metadata.transactionId || metadata.paymentIntentId,
      });
    } catch (error) {
      logger.error('Failed to update subscription payment status', {
        subscriptionId,
        status,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }
}

export const paymentProcessorService = new PaymentProcessorService();
