import {
  BadRequestException,
  Body,
  Controller,
  Get,
  Param,
  Patch,
  Post,
  Put,
  Query,
  Res,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import type { Response } from 'express';
import { AuthGuard } from '@nestjs/passport';
import { FileInterceptor } from '@nestjs/platform-express';
import { ApiBearerAuth, ApiBody, ApiConsumes, ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
import { GetUser } from 'src/auth/get-user.decorator';
import type { LoginUserInfo } from 'src/auth/dto/login.dto';
import { RequireModule, RequirePermission } from 'src/auth/decorators';
import { ModuleGuard } from 'src/auth/guards/module.guard';
import { PermissionGuard } from 'src/auth/guards/permission.guard';
import { RecibosService } from './recibos.service';
import { AnularReciboDto, CreateReciboDto, PreviewReciboDto } from './dto/create-recibo.dto';

@ApiTags('recibos-multi')
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'), ModuleGuard, PermissionGuard)
@RequireModule('COBRANZAS')
@Controller({ path: 'recibos-multi', version: '1' })
export class RecibosController {
  constructor(private readonly recibosService: RecibosService) {}

  @Get()
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_VER')
  @ApiOperation({ summary: 'Listar recibos multi-factura con filtros y paginación' })
  @ApiQuery({ name: 'cliente_id',  required: false })
  @ApiQuery({ name: 'cobrador_id', required: false })
  @ApiQuery({ name: 'estado',      required: false })
  @ApiQuery({ name: 'desde',       required: false })
  @ApiQuery({ name: 'hasta',       required: false })
  @ApiQuery({ name: 'search',      required: false })
  @ApiQuery({ name: 'page',        required: false })
  @ApiQuery({ name: 'limit',       required: false })
  listar(
    @GetUser() user: LoginUserInfo,
    @Query('cliente_id')  cliente_id?:  string,
    @Query('cobrador_id') cobrador_id?: string,
    @Query('estado')      estado?:      string,
    @Query('desde')       desde?:       string,
    @Query('hasta')       hasta?:       string,
    @Query('search')      search?:      string,
    @Query('busqueda')    busqueda?:    string,
    @Query('page')        page?:        string,
    @Query('limit')       limit?:       string,
  ) {
    return this.recibosService.listar(user.empresa_id, {
      cliente_id,
      cobrador_id,
      estado,
      desde,
      hasta,
      search: search ?? busqueda,
      page:  page  ? parseInt(page,  10) : undefined,
      limit: limit ? parseInt(limit, 10) : undefined,
    });
  }

  @Get('cliente/:clienteId/pendientes')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_CREAR')
  @ApiOperation({ summary: 'Facturas con saldo pendiente del cliente (para wizard del recibo)' })
  facturasPendientes(@GetUser() user: LoginUserInfo, @Param('clienteId') clienteId: string) {
    return this.recibosService.facturasPendientesCliente(user.empresa_id, clienteId);
  }

  @Get('factura/:facturaId/ultimo-pago')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_VER')
  @ApiOperation({ summary: 'Último recibo emitido aplicado a la factura, con detalle de medios de pago' })
  ultimoPagoFactura(@GetUser() user: LoginUserInfo, @Param('facturaId') facturaId: string) {
    return this.recibosService.ultimoPagoFactura(user.empresa_id, facturaId);
  }

  @Get('cliente/:clienteId/nc-disponibles')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_CREAR')
  @ApiOperation({ summary: 'Notas de crédito con saldo disponible del cliente' })
  ncDisponibles(@GetUser() user: LoginUserInfo, @Param('clienteId') clienteId: string) {
    return this.recibosService.ncDisponiblesCliente(user.empresa_id, clienteId);
  }

  @Patch('nc/:ncId/marcar-consumida')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_CREAR')
  @ApiOperation({ summary: 'Marca una NC como consumida (saldo disponible = 0) — para NC migradas ya usadas fuera del sistema' })
  marcarNcConsumida(@GetUser() user: LoginUserInfo, @Param('ncId') ncId: string) {
    return this.recibosService.marcarNcConsumida(user.empresa_id, ncId);
  }

  @Get('cliente/:clienteId/compras-compensables')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_CREAR')
  @ApiOperation({ summary: 'Facturas de compra (acuerdos comerciales) que el cliente emitió a la empresa, compensables contra su deuda' })
  comprasCompensables(@GetUser() user: LoginUserInfo, @Param('clienteId') clienteId: string) {
    return this.recibosService.comprasCompensablesCliente(user.empresa_id, clienteId);
  }

  @Get('gastos-disponibles')
  @RequirePermission('COBRANZAS', 'COB_REC_GASTOS')
  @ApiOperation({ summary: 'Gastos con CxP pendiente disponibles para descontar del cobro. Requiere flag empresa habilitado.' })
  @ApiQuery({ name: 'moneda', required: false, description: 'Filtrar por código de moneda (ej: PYG, USD)' })
  @ApiQuery({ name: 'search', required: false, description: 'Buscar por proveedor, RUC o número de factura' })
  @ApiQuery({ name: 'cliente_id', required: false, description: 'Cliente del cobro: prioriza y marca (es_del_cliente) los gastos cuyo proveedor tenga el mismo titular/RUC' })
  @ApiQuery({ name: 'limit', required: false })
  gastosDisponibles(
    @GetUser() user: LoginUserInfo,
    @Query('moneda') moneda?: string,
    @Query('search') search?: string,
    @Query('cliente_id') clienteId?: string,
    @Query('limit')  limit?: string,
  ) {
    return this.recibosService.gastosDisponibles(user.empresa_id, {
      moneda,
      search,
      clienteId,
      limit: limit ? parseInt(limit, 10) : undefined,
    });
  }

  @Get('cliente/:clienteId/saldos-favor')
  @RequirePermission('COBRANZAS', 'COB_SF_SALDO_VER')
  @ApiOperation({ summary: 'Saldos a favor disponibles del cliente' })
  saldosFavor(@GetUser() user: LoginUserInfo, @Param('clienteId') clienteId: string) {
    return this.recibosService.saldosFavorCliente(user.empresa_id, clienteId);
  }

  @Get('saldos-favor')
  @RequirePermission('COBRANZAS', 'COB_SF_SALDO_VER')
  @ApiOperation({ summary: 'Listar todos los saldos a favor con movimientos (admin)' })
  @ApiQuery({ name: 'cliente_id', required: false })
  @ApiQuery({ name: 'solo_disponibles', required: false, description: 'true para excluir agotados/anulados' })
  saldosFavorAll(
    @GetUser() user: LoginUserInfo,
    @Query('cliente_id') cliente_id?: string,
    @Query('solo_disponibles') solo_disponibles?: string,
  ) {
    return this.recibosService.listarSaldosFavor(user.empresa_id, {
      cliente_id,
      solo_disponibles: solo_disponibles === 'true',
    });
  }

  @Get('nc/:notaCreditoId/aplicaciones')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_VER')
  @ApiOperation({ summary: 'Recibos donde se aplicó la nota de crédito' })
  aplicacionesDeNC(@GetUser() user: LoginUserInfo, @Param('notaCreditoId') notaCreditoId: string) {
    return this.recibosService.aplicacionesDeNC(user.empresa_id, notaCreditoId);
  }

  @Get('config')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_VER')
  @ApiOperation({ summary: 'Obtener configuración de recibos multi de la empresa (incluye nc_modo_default)' })
  obtenerConfig(@GetUser() user: LoginUserInfo) {
    return this.recibosService.obtenerConfig(user.empresa_id);
  }

  @Put('config')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_CREAR')
  @ApiOperation({ summary: 'Actualizar configuración de recibos multi (nc_modo_default, diferencia_max_ajuste, numeracion_formato)' })
  actualizarConfig(
    @GetUser() user: LoginUserInfo,
    @Body() body: { nc_modo_default?: string; diferencia_max_ajuste?: number; numeracion_formato?: string; permitir_gastos_en_recibo?: boolean; tope_gastos_pct?: number | null },
  ) {
    return this.recibosService.actualizarConfig(user.empresa_id, body);
  }

  @Post('preview')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_CREAR')
  @ApiOperation({ summary: 'Vista previa de imputación FIFO sin persistir (para el wizard)' })
  preview(@GetUser() user: LoginUserInfo, @Body() dto: PreviewReciboDto) {
    return this.recibosService.preview(user.empresa_id, dto, user.id);
  }

  @Post()
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_CREAR')
  @ApiOperation({ summary: 'Crear recibo multi-factura (transacción atómica)' })
  crear(@GetUser() user: LoginUserInfo, @Body() dto: CreateReciboDto) {
    return this.recibosService.crear(user.empresa_id, dto, user);
  }

  @Get(':id')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_VER')
  @ApiOperation({ summary: 'Detalle completo de un recibo' })
  detalle(@GetUser() user: LoginUserInfo, @Param('id') id: string) {
    return this.recibosService.detalle(user.empresa_id, id);
  }

  @Get(':id/pdf')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_VER')
  @ApiOperation({ summary: 'Generar PDF A4 del recibo multi-factura' })
  @ApiQuery({ name: 'tipo', required: false, enum: ['view', 'base64'], description: 'Formato de respuesta' })
  async pdf(
    @GetUser() user: LoginUserInfo,
    @Param('id') id: string,
    @Query('tipo') tipo: 'view' | 'base64' = 'view',
    @Res() res: Response,
  ) {
    const result = await this.recibosService.pdfA4Multi(user.empresa_id, id, { tipo_retorno: tipo });
    if (tipo === 'base64') {
      return res.json({ status: 'success', pdf_base64: result.base64 });
    }
    res.setHeader('Content-Type', 'application/pdf');
    res.setHeader('Content-Disposition', `inline; filename="recibo-${id}.pdf"`);
    return res.end(result.pdfBuffer);
  }

  @Post(':id/anular')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_ANULAR')
  @ApiOperation({ summary: 'Anular recibo con reversa total automática' })
  anular(
    @GetUser() user: LoginUserInfo,
    @Param('id') id: string,
    @Body() dto: AnularReciboDto,
  ) {
    return this.recibosService.anular(user.empresa_id, id, dto, user.id);
  }

  @Post('comprobante-retencion/upload')
  @RequirePermission('COBRANZAS', 'COB_REC_RECIBO_CREAR')
  @ApiOperation({ summary: 'Subir comprobante de retención (PDF/imagen) a Spaces y devolver URL pública' })
  @ApiConsumes('multipart/form-data')
  @ApiBody({
    schema: {
      type: 'object',
      properties: { file: { type: 'string', format: 'binary' } },
      required: ['file'],
    },
  })
  @UseInterceptors(FileInterceptor('file'))
  uploadComprobanteRetencion(
    @GetUser() user: LoginUserInfo,
    @UploadedFile() file: Express.Multer.File,
  ) {
    if (!file?.buffer) throw new BadRequestException('Archivo inválido');
    return this.recibosService.uploadComprobanteRetencion(user.empresa_id, file);
  }

  // Nota: libro de retenciones y marcar declaradas viven en RetencionesController
  // bajo el módulo RETENCIONES (permisos independientes a COBROS).
}
