import { Injectable, Logger } from '@nestjs/common';
import { PaymentRepository } from '../../infrastructure/repositories/payment.repository';
import { InvoiceRepository } from '../../infrastructure/repositories/invoice.repository';
import { SubscriptionRepository } from '../../infrastructure/repositories/subscription.repository';
import { PaymentStatus } from '../../domain/entities/payment.entity';
import { InvoiceStatus } from '../../domain/entities/invoice.entity';
import { SubscriptionLifecycleService } from '../../domain/services/subscription-lifecycle.service';
import { PaymentProviderType, WebhookResult } from '../../domain/ports/payment-provider.port';
import { PaymentProviderRegistry } from '../../infrastructure/payment-providers/payment-provider.registry';

@Injectable()
export class HandlePaymentWebhookUseCase {
  private readonly logger = new Logger(HandlePaymentWebhookUseCase.name);

  constructor(
    private readonly providerRegistry: PaymentProviderRegistry,
    private readonly paymentRepo: PaymentRepository,
    private readonly invoiceRepo: InvoiceRepository,
    private readonly subscriptionRepo: SubscriptionRepository,
    private readonly lifecycle: SubscriptionLifecycleService,
  ) {}

  async execute(provider: PaymentProviderType, rawBody: Buffer, headers: Record<string, string>): Promise<void> {
    const paymentProvider = this.providerRegistry.get(provider);
    if (!paymentProvider) {
      this.logger.warn(`Webhook received for unconfigured provider: ${provider}`);
      return;
    }

    let result: WebhookResult;
    try {
      result = await paymentProvider.handleWebhook(rawBody, headers);
    } catch (err) {
      this.logger.error(`Webhook signature verification failed for ${provider}: ${err.message}`);
      throw err;
    }

    if (!result || result.type === 'unknown') return;

    if (result.type === 'payment_succeeded') {
      await this.handlePaymentSucceeded(result.providerPaymentId, result.metadata);
    } else if (result.type === 'payment_failed') {
      await this.handlePaymentFailed(result.providerPaymentId);
    } else if (result.type === 'refunded') {
      await this.handleRefund(result.providerPaymentId);
    }
  }

  private async handlePaymentSucceeded(providerPaymentId: string, metadata?: Record<string, any>) {
    const payment = await this.paymentRepo.findByProviderPaymentId(providerPaymentId);
    if (!payment) {
      this.logger.warn(`Payment not found for provider ID: ${providerPaymentId}`);
      return;
    }

    payment.status = PaymentStatus.SUCCEEDED;
    payment.paidAt = new Date();
    await this.paymentRepo.savePayment(payment);

    // Mark invoice as paid
    const invoice = await this.invoiceRepo.findById(payment.invoiceId ?? '');
    if (invoice) {
      invoice.status = InvoiceStatus.PAID;
      invoice.paidAt = new Date();
      await this.invoiceRepo.save(invoice);
    }

    // Activate/renew subscription
    const subscription = await this.subscriptionRepo.findByTenantId(payment.tenantId);
    if (subscription) {
      const activated = this.lifecycle.activate(subscription);
      await this.subscriptionRepo.save(activated);
      this.logger.log(`Subscription activated for tenant ${payment.tenantId}`);
    }
  }

  private async handlePaymentFailed(providerPaymentId: string) {
    const payment = await this.paymentRepo.findByProviderPaymentId(providerPaymentId);
    if (!payment) return;

    payment.status = PaymentStatus.FAILED;
    await this.paymentRepo.savePayment(payment);

    // Mark subscription as past_due
    const subscription = await this.subscriptionRepo.findByTenantId(payment.tenantId);
    if (subscription) {
      const pastDue = this.lifecycle.markPastDue(subscription);
      await this.subscriptionRepo.save(pastDue);
      this.logger.warn(`Subscription marked past_due for tenant ${payment.tenantId}`);
    }
  }

  private async handleRefund(providerPaymentId: string) {
    const payment = await this.paymentRepo.findByProviderPaymentId(providerPaymentId);
    if (!payment) return;

    payment.status = PaymentStatus.REFUNDED;
    await this.paymentRepo.savePayment(payment);
    this.logger.log(`Refund processed for payment ${providerPaymentId}`);
  }
}
