import { createHmac, timingSafeEqual } from 'node:crypto';

import { Injectable, Logger } from '@nestjs/common';

import { PrismaService } from '../../prisma/prisma.service';
import { isStripeBilling } from '../billing-provider';

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

  constructor(private readonly prisma: PrismaService) {}

  handleWebhook(rawBody: string, signatureHeader?: string): { received: boolean; mode: string } {
    if (!isStripeBilling()) {
      this.logger.debug('Stripe webhook ignored — BILLING_PROVIDER is not stripe');
      return { received: true, mode: 'ignored' };
    }

    const webhookSecret = process.env['STRIPE_WEBHOOK_SECRET'];
    if (!webhookSecret) {
      this.logger.warn('Stripe billing enabled but STRIPE_WEBHOOK_SECRET is not set');
      return { received: true, mode: 'unconfigured' };
    }

    if (!signatureHeader || !this.verifySignature(rawBody, signatureHeader, webhookSecret)) {
      this.logger.warn('Stripe webhook signature verification failed');
      return { received: false, mode: 'invalid_signature' };
    }

    const event = JSON.parse(rawBody) as {
      id?: string;
      type?: string;
      data?: { object?: Record<string, unknown> };
    };

    if (!event.id || !event.type) {
      return { received: false, mode: 'invalid_payload' };
    }

    void this.processEventIdempotently(event.id, event.type, event.data?.object ?? {});

    return { received: true, mode: 'processed' };
  }

  private verifySignature(
    payload: string,
    signatureHeader: string,
    secret: string,
  ): boolean {
    const parts = Object.fromEntries(
      signatureHeader.split(',').map((part) => {
        const [key, value] = part.split('=');
        return [key, value];
      }),
    ) as Record<string, string | undefined>;

    const timestamp = parts['t'];
    const signature = parts['v1'];

    if (!timestamp || !signature) {
      return false;
    }

    const signedPayload = `${timestamp}.${payload}`;
    const expected = createHmac('sha256', secret).update(signedPayload).digest('hex');
    const expectedBuffer = Buffer.from(expected, 'utf8');
    const providedBuffer = Buffer.from(signature, 'utf8');

    if (expectedBuffer.length !== providedBuffer.length) {
      return false;
    }

    return timingSafeEqual(expectedBuffer, providedBuffer);
  }

  private async processEventIdempotently(
    eventId: string,
    eventType: string,
    object: Record<string, unknown>,
  ): Promise<void> {
    const idempotencyKey = `stripe:${eventId}`;

    const existing = await this.prisma.integrationOperation.findUnique({
      where: { idempotencyKey },
    });

    if (existing) {
      return;
    }

    this.logger.log({ eventId, eventType }, 'Stripe webhook received');

    await this.prisma.integrationOperation.create({
      data: {
        installationId: await this.resolveStripeInstallationId(),
        idempotencyKey,
        operation: `stripe:${eventType}`,
        externalRef: eventId,
        status: 'COMPLETED',
        result: {
          eventType,
          objectId: object['id'] ?? null,
          note: 'Stripe fallback handler — extend for direct billing without WHMCS',
        },
      },
    });
  }

  private async resolveStripeInstallationId(): Promise<string> {
    const installation = await this.prisma.whmcsInstallation.upsert({
      where: { integrationId: 'stripe-fallback' },
      create: {
        integrationId: 'stripe-fallback',
        name: 'Stripe fallback billing',
        apiSecret: 'stripe-fallback-not-used-for-hmac',
        isActive: true,
        metadata: { source: 'stripe-webhook' },
      },
      update: {},
      select: { id: true },
    });

    return installation.id;
  }
}
