import { Injectable, Logger } from '@nestjs/common';
import { Prisma, PaymentIntentStatus, PaymentMethodType } from '@prisma/client';
import { PrismaService } from '../../../common/prisma.service';
import { PaymentsService } from '../payments.service';
import { CbeBirrProvider } from '../providers/cbe-birr.provider';

export interface CbeBirrWebhookPayload {
  merchantId: string;
  merchantOrderId: string;
  orderId: string;
  status: string;
  transactionId?: string;
  amount?: string;
  currency?: string;
  paidAt?: string;
  signature: string;
  [key: string]: unknown;
}

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

  constructor(
    private readonly prisma: PrismaService,
    private readonly provider: CbeBirrProvider,
    private readonly payments: PaymentsService,
  ) {}

  async handle(payload: CbeBirrWebhookPayload): Promise<void> {
    const merchantOrderId = payload.merchantOrderId;
    const externalEventId = `${payload.orderId}_${payload.status}`;
    const signatureValid = this.provider.verifyWebhookSignature(
      payload as unknown as Record<string, unknown>,
    );

    const eventRow = await this.persistEvent({
      externalEventId,
      merchantOrderId,
      providerTxnId: payload.transactionId ?? payload.orderId,
      signatureValid,
      status: payload.status,
      payload,
    });

    if (!eventRow) {
      this.logger.log(`CBE Birr webhook duplicate: ${externalEventId} — short-circuit OK`);
      return;
    }

    if (!signatureValid) {
      this.logger.warn(`CBE Birr webhook signature invalid for merchantOrderId=${merchantOrderId}`);
      await this.markProcessed(eventRow.id, 'signature-invalid');
      return;
    }

    const intent = await this.prisma.paymentIntent.findUnique({
      where: { merchantOrderId },
    });
    if (!intent) {
      this.logger.warn(`CBE Birr webhook: no PaymentIntent for merchantOrderId=${merchantOrderId}`);
      await this.markProcessed(eventRow.id, 'intent-not-found');
      return;
    }

    const mapped = this.provider.mapWebhookStatus(payload.status);

    try {
      if (mapped === PaymentIntentStatus.SUCCEEDED) {
        await this.payments.finalizePaymentSuccess({
          intentId: intent.id,
          providerTxnId: payload.transactionId ?? payload.orderId,
          paidAt: payload.paidAt ? new Date(payload.paidAt) : undefined,
        });
      } else if (mapped === PaymentIntentStatus.FAILED) {
        await this.payments.markPaymentFailed({
          intentId: intent.id,
          failureCode: payload.status,
        });
      } else {
        await this.prisma.paymentIntent.update({
          where: { id: intent.id },
          data: { status: mapped, providerTxnId: payload.transactionId ?? undefined },
        });
      }
      await this.markProcessed(eventRow.id);
    } catch (err) {
      const message = err instanceof Error ? err.message : String(err);
      this.logger.error(`CBE Birr webhook processing failed for ${merchantOrderId}: ${message}`);
      await this.markProcessed(eventRow.id, `processing-error: ${message}`);
      throw err;
    }
  }

  private async persistEvent(input: {
    externalEventId: string;
    merchantOrderId: string;
    providerTxnId?: string;
    signatureValid: boolean;
    status: string;
    payload: CbeBirrWebhookPayload;
  }): Promise<{ id: string } | null> {
    try {
      return await this.prisma.paymentWebhookEvent.create({
        data: {
          provider: PaymentMethodType.CBE_BIRR,
          externalEventId: input.externalEventId,
          merchantOrderId: input.merchantOrderId,
          providerTxnId: input.providerTxnId,
          signatureValid: input.signatureValid,
          status: input.status,
          payload: input.payload as unknown as Prisma.InputJsonValue,
        },
        select: { id: true },
      });
    } catch (err) {
      if (
        err instanceof Prisma.PrismaClientKnownRequestError &&
        err.code === 'P2002'
      ) {
        return null;
      }
      throw err;
    }
  }

  private async markProcessed(eventId: string, processingError?: string): Promise<void> {
    await this.prisma.paymentWebhookEvent.update({
      where: { id: eventId },
      data: { processedAt: new Date(), processingError },
    });
  }
}
