import { randomUUID } from "node:crypto";
import { Body, Controller, Inject, Logger, Post } from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger";
import {
  PaymentEvent,
  PaymentReferenceType,
  PaymentService,
  ProviderMethod,
  paymentRoutingKey,
} from "@edr/types";
import {
  PAYMENT_EVENT_PUBLISHER,
  PaymentEventPublisher,
} from "./publisher/payment-event-publisher";
import { TestPaymentEventDto } from "./dto/test-payment-event.dto";

/**
 * DEV-ONLY test harness. Publishes a synthetic payment event through the real
 * PaymentEventPublisher (RabbitMQ in dev), so the passenger/freight consumer receives it
 * exactly as in production — without creating an intent or going through a booking + provider
 * flow. Registered only when NODE_ENV !== "production" (see OutboxModule); never reachable in prod.
 *
 * Quick check (no body): POST /test/payment-event  -> publishes payment.passenger.succeeded.
 * Real side effects: pass a real bookingId as `referenceId`.
 */
@ApiTags("Dev test (non-production)")
@Controller("test")
export class TestEventsController {
  private readonly logger = new Logger(TestEventsController.name);

  constructor(
    @Inject(PAYMENT_EVENT_PUBLISHER)
    private readonly publisher: PaymentEventPublisher,
  ) {}

  @Post("payment-event")
  @ApiOperation({
    summary:
      "DEV ONLY: publish a synthetic payment event to the broker (passenger/freight consumes it)",
    description:
      "Bypasses intents/booking. Empty body publishes a payment.succeeded for PASSENGER. " +
      "Set referenceId to a real bookingId to trigger the consumer's seat/ticket side effects.",
  })
  async publishTestEvent(
    @Body() dto: TestPaymentEventDto,
  ): Promise<{ published: true; routingKey: string; event: PaymentEvent }> {
    const eventType = dto.eventType ?? "payment.succeeded";
    const service = dto.service ?? PaymentService.PASSENGER;
    const now = new Date().toISOString();

    const base = {
      version: 1 as const,
      eventId: randomUUID(),
      occurredAt: now,
      service,
      intentId: dto.intentId ?? randomUUID(),
      referenceType: dto.referenceType ?? PaymentReferenceType.BOOKING,
      referenceId: dto.referenceId ?? randomUUID(),
      merchantOrderId: dto.merchantOrderId ?? `test-${randomUUID().slice(0, 8)}`,
      provider: dto.provider ?? ProviderMethod.WAAFI,
      amountMinor: dto.amountMinor ?? 10_000,
      currency: dto.currency ?? "ETB",
    };

    const event: PaymentEvent =
      eventType === "payment.failed"
        ? {
            ...base,
            eventType: "payment.failed",
            failureCode: dto.failureCode ?? "TEST_DECLINED",
            failureMessage: dto.failureMessage ?? "Synthetic test failure",
          }
        : {
            ...base,
            eventType: "payment.succeeded",
            providerTxnId: dto.providerTxnId ?? `TEST-${randomUUID().slice(0, 8)}`,
            paidAt: now,
          };

    await this.publisher.publish(event);

    const routingKey = paymentRoutingKey(event.service, event.eventType);
    this.logger.log(
      `published TEST ${event.eventType} (${event.eventId}) ref=${event.referenceId} -> ${routingKey}`,
    );
    return { published: true, routingKey, event };
  }
}
