// PÚBLICO — sin autenticación JWT. Rate limit aplicado por IP.
import {
  BadRequestException,
  Body,
  Controller,
  Get,
  HttpCode,
  HttpStatus,
  NotFoundException,
  Param,
  Post,
  UseGuards,
} from '@nestjs/common';
import { Throttle, ThrottlerGuard } from '@nestjs/throttler';
import { $Enums } from '@prisma/client';
import { PrismaService } from 'src/prisma/prisma.service';
import { envs } from 'src/config';
import { BancardMockService } from './bancard-mock.service';
import { BancardPostPagoHook } from './bancard-post-pago.hook';
import { ConfirmarPagoMockDto } from './dto/confirmar-pago-mock.dto';
import { IniciarPagoPublicoDto } from './dto/iniciar-pago-publico.dto';
import { PagoLinkService } from './pago-link.service';

export function buildPagoLinkUrl(token: string): string {
  return `${envs.pagoLinkBaseUrl ?? 'https://app.novasis.com'}/pay/ca/${token}`;
}

// Endpoints PÚBLICOS (sin JWT): rate limit por IP para mitigar abuso/enumeración.
@Throttle({ default: { limit: 20, ttl: 60_000 } })
@UseGuards(ThrottlerGuard)
@Controller('pago-publico')
export class PagoPublicoController {
  constructor(
    private readonly prisma: PrismaService,
    private readonly pagoLinkService: PagoLinkService,
    private readonly bancardMock: BancardMockService,
    private readonly postPagoHook: BancardPostPagoHook,
  ) {}

  @Get(':token')
  async getPagoData(@Param('token') token: string) {
    const link = await this.pagoLinkService.findByToken(token);

    const empresa = await this.prisma.empresas.findUnique({
      where: { id: link.empresa_id },
      select: { razon_social: true, nombre_fantasia: true, ruc: true, dv: true, logo: true },
    });

    if (link.origen_modulo !== $Enums.bancard_origen_modulo.compra_asistida) {
      throw new BadRequestException(`Módulo ${link.origen_modulo} no soportado`);
    }

    const ca = await this.prisma.compra_asistida.findUnique({
      where: { id: link.origen_id },
      select: {
        id: true,
        numero: true,
        cotizacion_usd_gs: true,
        servicio_gs: true,
        logistica_gs: true,
        comision_gs: true,
        notas: true,
        fecha_entrega_estimada: true,
        cliente: {
          select: {
            nombre_fantasia: true,
            personas: { select: { razon_social: true, ruc: true, dv: true } },
          },
        },
        items: {
          orderBy: { orden: 'asc' },
          select: { descripcion: true, cantidad: true, precio_usd: true, url_producto: true },
        },
      },
    });

    if (!ca) throw new BadRequestException('Pedido no encontrado');

    const totalUsd     = ca.items.reduce((s, i) => s + Number(i.cantidad) * Number(i.precio_usd), 0);
    const cotiz        = Number(ca.cotizacion_usd_gs);
    const productoGs   = totalUsd * cotiz;
    const servicioGs   = Number(ca.servicio_gs);
    const logisticaGs  = Number(ca.logistica_gs);
    const comisionGs   = Number(ca.comision_gs);

    return {
      token,
      numero: ca.numero,
      monto_total: Number(link.monto_total),
      moneda: link.moneda,
      fecha_expiracion: link.fecha_expiracion,
      empresa: {
        nombre:   empresa?.razon_social ?? empresa?.nombre_fantasia ?? null,
        ruc:      empresa?.ruc ?? null,
        dv:       empresa?.dv ?? null,
        logo_url: empresa?.logo ?? null,
      },
      cliente: {
        nombre: ca.cliente?.personas?.razon_social ?? ca.cliente?.nombre_fantasia ?? null,
        ruc:    ca.cliente?.personas?.ruc ?? null,
      },
      items: ca.items,
      cotizacion_usd_gs: cotiz,
      desglose: {
        total_usd:    totalUsd,
        producto_gs:  productoGs,
        servicio_gs:  servicioGs,
        logistica_gs: logisticaGs,
        comision_gs:  comisionGs,
      },
      notas: ca.notas,
      fecha_entrega_estimada: ca.fecha_entrega_estimada,
    };
  }

  @Post(':token/iniciar-pago')
  @HttpCode(HttpStatus.OK)
  async iniciarPago(
    @Param('token') token: string,
    @Body() body: IniciarPagoPublicoDto,
  ) {
    const link = await this.pagoLinkService.findByToken(token);

    const result = await this.bancardMock.initPayment({
      empresaId:    link.empresa_id,
      monto:        Number(link.monto_total),
      moneda:       link.moneda,
      descripcion:  `Pago ${link.origen_modulo} ${link.origen_id}`,
      origenModulo: link.origen_modulo,
      origenId:     link.origen_id,
    });

    return {
      pagoId:        result.pagoId,
      shopProcessId: result.shopProcessId.toString(),
      metodo:        body.metodo,
    };
  }

  // ⚠️ MOCK — aprueba el pago sin dinero real. Solo para demo/staging. En producción
  // debe quedar deshabilitado (BANCARD_MOCK_ENABLED != true) → responde 404 como si no existiera.
  @Post(':token/confirmar-mock')
  @HttpCode(HttpStatus.OK)
  async confirmarMock(
    @Param('token') token: string,
    @Body() body: ConfirmarPagoMockDto,
  ) {
    if (!envs.bancardMockEnabled) {
      throw new NotFoundException('Cannot POST /pago-publico/:token/confirmar-mock');
    }

    const link = await this.pagoLinkService.findByToken(token);

    await this.bancardMock.confirmarMock(body.pagoId);
    await this.pagoLinkService.marcarPagado(link.id, body.pagoId);
    await this.postPagoHook.procesar(body.pagoId);

    return {
      aprobado:      true,
      ticket:        `MOCK-TKT-${Date.now()}`,
      authorization: 'MOCK-00',
      mensaje:       'Pago aprobado correctamente',
    };
  }

  @Post(':token/cancelar')
  @HttpCode(HttpStatus.OK)
  async cancelar(@Param('token') token: string) {
    const link = await this.pagoLinkService.findByToken(token);

    await this.prisma.pago_link.update({
      where: { id: link.id },
      data:  { estado: $Enums.pago_link_estado.cancelado, updated_at: new Date() },
    });

    return { ok: true };
  }
}
