import {
  Body,
  Controller,
  Get,
  Logger,
  Param,
  ParseUUIDPipe,
  Post,
  Put,
  Query,
  UseGuards,
} from '@nestjs/common';
import { envs } from 'src/config';
import { AuthGuard } from '@nestjs/passport';
import { RequireModule } from 'src/auth/decorators/require-module.decorator';
import { RequirePermission } from 'src/auth/decorators/require-permission.decorator';
import type { LoginUserInfo } from 'src/auth/dto/login.dto';
import { GetUser } from 'src/auth/get-user.decorator';
import { ModuleGuard } from 'src/auth/guards/module.guard';
import { PermissionGuard } from 'src/auth/guards/permission.guard';
import { CrearNovasisPayCobroDto } from './dto/crear-novasis-pay-cobro.dto';
import { ListarNovasisPayCobrosDto } from './dto/listar-novasis-pay-cobros.dto';
import { UpsertNovasisPayConfigDto } from './dto/upsert-novasis-pay-config.dto';
import { NovasisPayClient } from './novasis-pay.client';
import { NovasisPayConfigService } from './novasis-pay-config.service';
import { NovasisPayCobrosService } from './novasis-pay-cobros.service';

@Controller('novasis-pay')
@UseGuards(AuthGuard('jwt'), ModuleGuard, PermissionGuard)
@RequireModule('NOVASIS_PAY')
export class NovasisPayController {
  private readonly logger = new Logger(NovasisPayController.name);

  constructor(
    private readonly configService: NovasisPayConfigService,
    private readonly client: NovasisPayClient,
    private readonly cobrosService: NovasisPayCobrosService,
  ) {}

  @Get('config')
  @RequirePermission('NOVASIS_PAY', 'NOV_CFG_CUENTA_VER')
  getConfig(@GetUser() user: LoginUserInfo) {
    return this.configService.getConfig(user.empresa_id);
  }

  @Put('config')
  @RequirePermission('NOVASIS_PAY', 'NOV_CFG_CUENTA_EDITAR')
  async upsertConfig(
    @Body() dto: UpsertNovasisPayConfigDto,
    @GetUser() user: LoginUserInfo,
  ) {
    const saved = await this.configService.upsertConfig(user.empresa_id, dto);
    // Best-effort: si la cuenta quedó activa y con secret pero sin webhook_secret,
    // registramos el endpoint de webhook del ERP en el gateway (para tiempo real).
    // No rompemos el guardado si falla (queda el auto-sync como respaldo).
    if (saved.activo && saved.tiene_secret_key && !saved.tiene_webhook_secret) {
      try {
        await this.registrarWebhookEnGateway(user.empresa_id);
        return this.configService.getConfig(user.empresa_id);
      } catch (e) {
        // error (no warn): así queda en logs/error-*.log y no pasa desapercibido.
        this.logger.error(
          `No se pudo registrar el webhook en el gateway (empresa=${user.empresa_id}): ${
            (e as Error).message
          }`,
        );
      }
    }
    return saved;
  }

  /**
   * Registra (o re-registra) el endpoint de webhook del ERP en el gateway y guarda
   * el secret devuelto. Útil si cambió la URL pública o si querés forzar el alta.
   */
  @Post('config/register-webhook')
  @RequirePermission('NOVASIS_PAY', 'NOV_CFG_CUENTA_EDITAR')
  async registerWebhook(@GetUser() user: LoginUserInfo) {
    await this.registrarWebhookEnGateway(user.empresa_id);
    return this.configService.getConfig(user.empresa_id);
  }

  private async registrarWebhookEnGateway(empresaId: string) {
    // URL pública COMPLETA del receptor de webhooks de este backend del ERP
    // (incluye prefijo 'api' y versión 'v1'). El gateway le postea tal cual.
    const url = envs.novasisPayWebhookUrl?.replace(/\/+$/, '');
    if (!url) {
      throw new Error(
        'Falta NOVASIS_PAY_WEBHOOK_URL (URL pública del receptor de webhooks de este ERP) para registrar el webhook en el gateway.',
      );
    }
    const { secret } = await this.client.registerWebhookEndpoint(empresaId, url);
    await this.configService.upsertConfig(empresaId, { webhook_secret: secret });
    this.logger.log(`Webhook del ERP registrado en el gateway: ${url}`);
  }

  @Post('config/test-connection')
  @RequirePermission('NOVASIS_PAY', 'NOV_CFG_CUENTA_EDITAR')
  testConnection(@GetUser() user: LoginUserInfo) {
    return this.client.testConnection(user.empresa_id);
  }

  /**
   * Catálogo público de providers del gateway. No requiere config activa:
   * sirve para que el form de configuración pueda llenar el dropdown
   * "provider preferido" antes incluso de tener la cuenta lista.
   */
  @Get('providers')
  @RequirePermission('NOVASIS_PAY', 'NOV_CFG_CUENTA_VER')
  listProviders(@GetUser() user: LoginUserInfo) {
    return this.client.getProviders(user.empresa_id);
  }

  /**
   * Medios de pago (platformIds) de un provider — ej. los QR de Dpago. Lo usa
   * el modal de Nuevo Cobro para poblar el selector cuando se elige "QR directo".
   */
  @Get('providers/:provider/payment-methods')
  @RequirePermission('NOVASIS_PAY', 'NOV_COBROS_VER')
  async listPaymentMethods(
    @Param('provider') provider: string,
    @Query('country') country: string | undefined,
    @GetUser() user: LoginUserInfo,
  ) {
    const methods = await this.client.listProviderPaymentMethods(
      user.empresa_id,
      provider,
      country ?? 'PY',
    );
    return { methods };
  }

  // ── Cobros ────────────────────────────────────────────────

  @Post('cobros')
  @RequirePermission('NOVASIS_PAY', 'NOV_COBROS_CREAR')
  crearCobro(
    @Body() dto: CrearNovasisPayCobroDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.cobrosService.create(user.empresa_id, user.id, dto);
  }

  @Get('cobros')
  @RequirePermission('NOVASIS_PAY', 'NOV_COBROS_VER')
  listarCobros(
    @Query() query: ListarNovasisPayCobrosDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.cobrosService.list(user.empresa_id, query);
  }

  @Get('cobros/:id')
  @RequirePermission('NOVASIS_PAY', 'NOV_COBROS_VER')
  obtenerCobro(
    @Param('id', new ParseUUIDPipe()) id: string,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.cobrosService.findOne(user.empresa_id, id);
  }

  /** Sincroniza estado consultando el intent al gateway (útil sin webhook). */
  @Post('cobros/:id/sync')
  @RequirePermission('NOVASIS_PAY', 'NOV_COBROS_VER')
  sincronizarCobro(
    @Param('id', new ParseUUIDPipe()) id: string,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.cobrosService.sync(user.empresa_id, id);
  }
}
