import { Buffer } from "buffer";
import Stripe from "stripe";
import type { Request, Response } from "express";

export interface StripeWebhookHandler {
  handle(event: Stripe.Event): Promise<void>;
}

export class StripeWebhookController {
  private stripe: Stripe;
  private webhookSecret: string;

  constructor(stripe: Stripe, webhookSecret: string) {
    this.stripe = stripe;
    this.webhookSecret = webhookSecret;
  }

  async handleWebhook(req: Request, res: Response): Promise<void> {
    const buf = Buffer.from(req.body, "utf8");
    const signature = req.headers["stripe-signature"] as string;

    try {
      const event = await this.stripe.webhooks.constructEventAsync(buf, signature, this.webhookSecret);
      await this.processEvent(event);
      res.json({ received: true, event: event.type });
    } catch (err) {
      console.error('Stripe webhook error:', err);
      res.status(400).json({
        received: true,
        error: {
          message: "Webhook processing failed",
          type: "WebhookError",
        },
      });
    }
  }

  private async processEvent(event: Stripe.Event): Promise<void> {
    console.log(`Processing webhook event: ${event.type}`);

    switch (event.type) {
      case "customer.created":
        await this.handleCustomerCreated(event.data.object as Stripe.Customer);
        break;
      case "customer.updated":
        await this.handleCustomerUpdated(event.data.object as Stripe.Customer);
        break;
      case "customer.deleted":
        await this.handleCustomerDeleted(event.data.object as Stripe.Customer);
        break;
      case "subscription.created":
        await this.handleSubscriptionCreated(event.data.object as Stripe.Subscription);
        break;
      case "subscription.updated":
        await this.handleSubscriptionUpdated(event.data.object as Stripe.Subscription);
        break;
      case "subscription.deleted":
        await this.handleSubscriptionDeleted(event.data.object as Stripe.Subscription);
        break;
      case "invoice.payment_succeeded":
        await this.handleInvoicePaymentSucceeded(event.data.object as Stripe.Invoice);
        break;
      case "invoice.payment_failed":
        await this.handleInvoicePaymentFailed(event.data.object as Stripe.Invoice);
        break;
      case "payment_intent.succeeded":
        await this.handlePaymentIntentSucceeded(event.data.object as Stripe.PaymentIntent);
        break;
      case "payment_intent.payment_failed":
        await this.handlePaymentIntentFailed(event.data.object as Stripe.PaymentIntent);
        break;
      case "checkout.session.completed":
        await this.handleCheckoutSessionCompleted(event.data.object as Stripe.Checkout.Session);
        break;
      default:
        console.log(`Unhandled event type: ${event.type}`);
    }
  }

  private async handleCustomerCreated(customer: Stripe.Customer): Promise<void> {
    console.log(`Customer created: ${customer.id} (${customer.email})`);
  }

  private async handleCustomerUpdated(customer: Stripe.Customer): Promise<void> {
    console.log(`Customer updated: ${customer.id}`);
  }

  private async handleCustomerDeleted(customer: Stripe.Customer): Promise<void> {
    console.log(`Customer deleted: ${customer.id}`);
  }

  private async handleSubscriptionCreated(subscription: Stripe.Subscription): Promise<void> {
    console.log(`Subscription created: ${subscription.id} for customer ${subscription.customer}`);
  }

  private async handleSubscriptionUpdated(subscription: Stripe.Subscription): Promise<void> {
    console.log(`Subscription updated: ${subscription.id}`);
  }

  private async handleSubscriptionDeleted(subscription: Stripe.Subscription): Promise<void> {
    console.log(`Subscription deleted: ${subscription.id}`);
  }

  private async handleInvoicePaymentSucceeded(invoice: Stripe.Invoice): Promise<void> {
    console.log(`Invoice payment succeeded: ${invoice.id} for ${invoice.amount_paid} ${invoice.currency}`);
  }

  private async handleInvoicePaymentFailed(invoice: Stripe.Invoice): Promise<void> {
    console.log(`Invoice payment failed: ${invoice.id} for ${invoice.amount_due} ${invoice.currency}`);
  }

  private async handlePaymentIntentSucceeded(paymentIntent: Stripe.PaymentIntent): Promise<void> {
    console.log(`Payment intent succeeded: ${paymentIntent.id} for ${paymentIntent.amount} ${paymentIntent.currency}`);
  }

  private async handlePaymentIntentFailed(paymentIntent: Stripe.PaymentIntent): Promise<void> {
    console.log(`Payment intent failed: ${paymentIntent.id} for ${paymentIntent.amount} ${paymentIntent.currency}`);
  }

  private async handleCheckoutSessionCompleted(session: Stripe.Checkout.Session): Promise<void> {
    console.log(`Checkout session completed: ${session.id} for customer ${session.customer}`);
  }
}

export const createStripeWebhookController = (
  stripe: Stripe,
  webhookSecret: string
): StripeWebhookController => {
  return new StripeWebhookController(stripe, webhookSecret);
};
