import { Body, Controller, Get, Param, Post, UseGuards } from "@nestjs/common";
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
import { Throttle } from "@nestjs/throttler";
import { ApiKeyGuard } from "../common/auth/api-key.guard";
import { MerchantAuth } from "../common/auth/auth-context.decorator";
import type { ApiKeyAuthContext } from "../common/auth/auth.types";
import { ApiErrorResponse } from "../common/swagger/api-error.schema";
import { CheckoutSessionService } from "./checkout-session.service";
import {
  CheckoutSessionDto,
  CreatedCheckoutSessionDto,
  PublicCheckoutSessionDto,
} from "./dto/checkout-session.dto";
import {
  ConfirmPublicCheckoutSessionDto,
  PublicCheckoutConfirmResultDto,
} from "./dto/confirm-public-checkout-session.dto";
import { CreateCheckoutSessionDto } from "./dto/create-checkout-session.dto";
import { PublicPaymentMethodDto } from "./dto/public-payment-method.dto";

@ApiTags("Checkout Sessions")
@ApiBearerAuth("bearerAuth")
@ApiResponse({ status: 401, description: "API key faltante, inválida o revocada.", type: ApiErrorResponse })
@ApiResponse({ status: 403, description: "Merchant inactivo.", type: ApiErrorResponse })
@ApiResponse({ status: 429, description: "Rate limit excedido.", type: ApiErrorResponse })
@Controller("checkout-sessions")
@UseGuards(ApiKeyGuard)
export class CheckoutSessionController {
  constructor(private readonly service: CheckoutSessionService) {}

  @Post()
  @ApiOperation({
    operationId: "checkoutSessionsCreate",
    summary: "Crear checkout session",
    description:
      "Crea una sesión de hosted checkout (URL con `publicToken`) y un `clientSecret` para drop-in SDK. " +
      "Un payment intent solo puede tener una sesión asociada.",
  })
  @ApiResponse({ status: 201, type: CreatedCheckoutSessionDto })
  @ApiResponse({ status: 400, description: "Intent no pertenece al merchant o ya tiene sesión asociada.", type: ApiErrorResponse })
  create(@MerchantAuth() auth: ApiKeyAuthContext, @Body() dto: CreateCheckoutSessionDto) {
    return this.service.create(auth.merchantId, dto);
  }

  @Get(":id")
  @ApiOperation({
    operationId: "checkoutSessionsRetrieve",
    summary: "Obtener checkout session por ID",
    description: "El `clientSecret` **no** se incluye en esta respuesta (solo se devuelve al crear).",
  })
  @ApiResponse({ status: 200, type: CheckoutSessionDto })
  @ApiResponse({ status: 404, description: "No encontrada.", type: ApiErrorResponse })
  findOne(@MerchantAuth() auth: ApiKeyAuthContext, @Param("id") id: string) {
    return this.service.findOne(auth.merchantId, id);
  }
}

@ApiTags("Checkout Sessions · Public")
@ApiResponse({ status: 404, description: "Sesión inexistente, expirada, bloqueada o merchant inactivo. Respuesta uniforme para evitar enumeración.", type: ApiErrorResponse })
@ApiResponse({ status: 429, description: "Rate limit excedido.", type: ApiErrorResponse })
@Controller("public/checkout-sessions")
export class PublicCheckoutSessionController {
  constructor(private readonly service: CheckoutSessionService) {}

  @Get(":publicToken")
  @Throttle({ default: { limit: 30, ttl: 60_000 } })
  @ApiOperation({
    operationId: "publicCheckoutSessionsRetrieve",
    summary: "Obtener checkout session por publicToken (sin auth)",
    description:
      "Endpoint público consumido por el hosted checkout. Devuelve únicamente información segura de mostrar al comprador " +
      "(monto, moneda, descripción, nombre del merchant). Sesiones expiradas, bloqueadas o de merchants inactivos devuelven 404 uniforme.",
  })
  @ApiResponse({ status: 200, type: PublicCheckoutSessionDto })
  retrieveByToken(@Param("publicToken") publicToken: string) {
    return this.service.retrieveByPublicToken(publicToken);
  }

  @Get(":publicToken/payment-methods")
  @Throttle({ default: { limit: 30, ttl: 60_000 } })
  @ApiOperation({
    operationId: "publicCheckoutSessionsPaymentMethods",
    summary: "Listar medios de pago disponibles (sin auth)",
    description:
      "Devuelve los medios de pago que el comprador puede elegir para esta sesión, según el provider ruteado. " +
      "Cada uno trae su `platformId` para enviar en el confirm. Lista vacía ⇒ el provider no expone selección inline " +
      "(se completa vía redirect/hosted checkout).",
  })
  @ApiResponse({ status: 200, type: [PublicPaymentMethodDto] })
  paymentMethodsByToken(@Param("publicToken") publicToken: string) {
    return this.service.listPaymentMethodsByPublicToken(publicToken);
  }

  @Post(":publicToken/confirm")
  @Throttle({ default: { limit: 5, ttl: 60_000 } })
  @ApiOperation({
    operationId: "publicCheckoutSessionsConfirm",
    summary: "Confirmar pago desde hosted checkout",
    description:
      "Confirma el payment intent asociado a esta sesión. Devuelve el estado resultante y eventualmente la URL de redirección del provider. " +
      "Tras 5 intentos fallidos consecutivos, la sesión queda bloqueada y todos los endpoints públicos devuelven 404.",
  })
  @ApiResponse({ status: 201, type: PublicCheckoutConfirmResultDto })
  confirmByToken(
    @Param("publicToken") publicToken: string,
    @Body() dto: ConfirmPublicCheckoutSessionDto,
  ) {
    return this.service.confirmByPublicToken(publicToken, dto);
  }
}
