import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import Stripe from 'stripe';
import {
  PaymentProviderPort,
  PaymentProviderType,
  CreatePaymentParams,
  PaymentSession,
  PaymentResult,
  WebhookResult,
} from '../../../domain/ports/payment-provider.port';

@Injectable()
export class StripeProvider implements PaymentProviderPort {
  readonly providerType = PaymentProviderType.STRIPE;
  private client: Stripe | null = null;
  private webhookSecret: string;

  constructor(private readonly configService: ConfigService) {
    const secretKey = this.configService.get<string>('STRIPE_SECRET_KEY');
    this.webhookSecret = this.configService.get<string>('STRIPE_WEBHOOK_SECRET', '');
    if (secretKey) {
      this.client = new Stripe(secretKey, { apiVersion: '2023-10-16' });
    }
  }

  isConfigured(): boolean {
    return !!this.client;
  }

  async createPaymentSession(params: CreatePaymentParams): Promise<PaymentSession> {
    if (!this.client) throw new Error('Stripe not configured');

    const intent = await this.client.paymentIntents.create({
      amount: params.amount,
      currency: params.currency.toLowerCase(),
      metadata: {
        invoiceId: params.invoiceId,
        tenantId: params.tenantId,
      },
      description: params.description,
    });

    return {
      providerPaymentId: intent.id,
      clientSecret: intent.client_secret ?? undefined,
      expiresAt: new Date(Date.now() + 30 * 60 * 1000), // 30 minutes
    };
  }

  async confirmPayment(providerPaymentId: string): Promise<PaymentResult> {
    if (!this.client) throw new Error('Stripe not configured');
    const intent = await this.client.paymentIntents.retrieve(providerPaymentId);
    return {
      providerPaymentId: intent.id,
      status: intent.status === 'succeeded' ? 'succeeded' : intent.status === 'canceled' ? 'failed' : 'pending',
      amount: intent.amount,
      currency: intent.currency.toUpperCase(),
    };
  }

  async handleWebhook(rawBody: Buffer, headers: Record<string, string>): Promise<WebhookResult> {
    if (!this.client) throw new Error('Stripe not configured');

    const sig = headers['stripe-signature'];
    let event: Stripe.Event;

    try {
      event = this.client.webhooks.constructEvent(rawBody, sig, this.webhookSecret);
    } catch (err) {
      throw new Error(`Stripe webhook signature verification failed: ${err.message}`);
    }

    switch (event.type) {
      case 'payment_intent.succeeded': {
        const intent = event.data.object as Stripe.PaymentIntent;
        return { type: 'payment_succeeded', providerPaymentId: intent.id, metadata: intent.metadata };
      }
      case 'payment_intent.payment_failed': {
        const intent = event.data.object as Stripe.PaymentIntent;
        return { type: 'payment_failed', providerPaymentId: intent.id };
      }
      case 'charge.refunded': {
        const charge = event.data.object as Stripe.Charge;
        return { type: 'refunded', providerPaymentId: charge.payment_intent as string };
      }
      default:
        return { type: 'unknown' };
    }
  }
}
