import { Injectable, Logger } from "@nestjs/common";
import {
  WaafiProvider,
  WaafiWebhookHeaders,
  WaafiWebhookPayload,
} from "@edr/payment-providers";
import { WebhookProcessorService } from "../webhook-processor.service";

/** Reject webhooks whose timestamp is older than this (replay protection). */
const WAAFI_REPLAY_WINDOW_SECONDS = 300;

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

  constructor(
    private readonly provider: WaafiProvider,
    private readonly processor: WebhookProcessorService,
  ) {}

  async handle(
    payload: WaafiWebhookPayload,
    rawBody: string,
    headers: WaafiWebhookHeaders,
  ): Promise<void> {
    // Unsigned validation ping sent on registration — acknowledge without verifying or persisting.
    if (payload.event === "webhook.test") {
      this.logger.log("Waafi webhook.test ping received");
      return;
    }

    const { payment } = payload;
    const eventId = headers["x-webhook-event-id"];
    const timestamp = headers["x-webhook-timestamp"];
    const signature = headers["x-webhook-signature"];

    const signatureValid =
      this.isFresh(timestamp) &&
      this.provider.verifyWebhookSignature(
        rawBody,
        signature,
        timestamp,
        eventId,
      );

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

    await this.processor.process({
      provider: this.provider.method,
      // X-Webhook-Event-Id is unique per event; fall back to a derived id if absent.
      externalEventId: eventId ?? `${payment.transaction_id}_${payment.status}`,
      merchantOrderId: payment.reference_id,
      providerTxnId: payment.transaction_id,
      signatureValid,
      rawStatus: payment.status,
      payload: payload as unknown as Record<string, unknown>,
      result: {
        status: mapped,
        providerTxnId: payment.transaction_id,
        paidAt: this.parseDate(payment.date),
        failureCode: payment.status,
        failureMessage: payment.description,
      },
    });
  }

  /** True when the webhook timestamp (unix seconds) is within the replay window. */
  private isFresh(timestamp: string | undefined): boolean {
    if (!timestamp) return false;
    const ts = parseInt(timestamp, 10);
    if (Number.isNaN(ts)) return false;
    const now = Math.floor(Date.now() / 1000);
    return Math.abs(now - ts) <= WAAFI_REPLAY_WINDOW_SECONDS;
  }

  /** Parse Waafi's "YYYY-MM-DD HH:mm:ss" payment date; undefined when unparseable. */
  private parseDate(raw: string | undefined): Date | undefined {
    if (!raw) return undefined;
    const d = new Date(raw);
    return Number.isNaN(d.getTime()) ? undefined : d;
  }
}
