import { Body, Controller, Get, Param, Patch, Post, Put, Query, Res, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiBearerAuth, ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
import type { LoginUserInfo } from 'src/auth/dto/login.dto';
import { GetUser } from 'src/auth/get-user.decorator';
import { RequireModule, RequirePermission } from 'src/auth/decorators';
import { ModuleGuard } from 'src/auth/guards/module.guard';
import { PermissionGuard } from 'src/auth/guards/permission.guard';
import {
  AnularOrdenPagoProveedorDto,
  CreateOrdenPagoProveedorDto,
} from './dto/create-orden-pago-proveedor.dto';
import { UpdateConfigComprasDto } from './dto/update-config-compras.dto';
import { RechazarOrdenPagoProveedorDto } from './dto/workflow-orden-pago-proveedor.dto';
import { PagosProveedorService } from './pagos-proveedor.service';
import { CuentasPagarReportesService } from './cuentas-pagar-reportes.service';
import { PlanPagosService } from './plan-pagos.service';
import { ExtractoProveedorService } from './extracto-proveedor.service';
import { ProgramarPagosDto } from './dto/programar-pagos.dto';

@ApiTags('Pagos Proveedor')
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'), ModuleGuard, PermissionGuard)
@RequireModule('COMPRAS')
@Controller({ path: 'pagos-proveedor', version: '1' })
export class PagosProveedorController {
  constructor(
    private readonly pagosProveedorService: PagosProveedorService,
    private readonly cuentasPagarReportes: CuentasPagarReportesService,
    private readonly planPagos: PlanPagosService,
    private readonly extractoProveedor: ExtractoProveedorService,
  ) {}

  @Post()
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_CREAR')
  @ApiOperation({ summary: 'Crear orden de pago a proveedor' })
  create(@Body() dto: CreateOrdenPagoProveedorDto, @GetUser() user: LoginUserInfo) {
    return this.pagosProveedorService.create(dto, user.empresa_id, user.id);
  }

  @Get()
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_VER')
  @ApiOperation({ summary: 'Listar órdenes de pago a proveedores' })
  @ApiQuery({ name: 'page', required: false })
  @ApiQuery({ name: 'limit', required: false })
  @ApiQuery({ name: 'fecha_desde', required: false })
  @ApiQuery({ name: 'fecha_hasta', required: false })
  @ApiQuery({ name: 'proveedor_id', required: false })
  @ApiQuery({ name: 'compra_id', required: false })
  @ApiQuery({ name: 'origen', required: false, description: 'manual | compra_auto' })
  @ApiQuery({ name: 'origen_tipo', required: false, description: 'compra | gasto' })
  @ApiQuery({ name: 'estado', required: false })
  @ApiQuery({ name: 'busqueda', required: false })
  findAll(
    @GetUser() user: LoginUserInfo,
    @Query('page') page?: string,
    @Query('limit') limit?: string,
    @Query('fecha_desde') fecha_desde?: string,
    @Query('fecha_hasta') fecha_hasta?: string,
    @Query('proveedor_id') proveedor_id?: string,
    @Query('compra_id') compra_id?: string,
    @Query('origen') origen?: string,
    @Query('origen_tipo') origen_tipo?: string,
    @Query('estado') estado?: string,
    @Query('busqueda') busqueda?: string,
  ) {
    return this.pagosProveedorService.findAll(user.empresa_id, {
      page,
      limit,
      fecha_desde,
      fecha_hasta,
      proveedor_id,
      compra_id,
      origen,
      origen_tipo,
      estado,
      busqueda,
    });
  }

  @Get('dashboard')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_VER')
  @ApiOperation({ summary: 'Resumen dashboard de órdenes de pago a proveedores' })
  @ApiQuery({ name: 'fecha_desde', required: false })
  @ApiQuery({ name: 'fecha_hasta', required: false })
  @ApiQuery({ name: 'origen', required: false, description: 'manual | compra_auto' })
  @ApiQuery({ name: 'origen_tipo', required: false, description: 'compra | gasto' })
  getDashboard(
    @GetUser() user: LoginUserInfo,
    @Query('fecha_desde') fecha_desde?: string,
    @Query('fecha_hasta') fecha_hasta?: string,
    @Query('origen') origen?: string,
    @Query('origen_tipo') origen_tipo?: string,
  ) {
    return this.pagosProveedorService.getDashboard(user.empresa_id, {
      fecha_desde,
      fecha_hasta,
      origen,
      origen_tipo,
    });
  }

  @Get('config')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_VER')
  @ApiOperation({ summary: 'Obtener configuración AP (config_compras) de la empresa' })
  getConfig(@GetUser() user: LoginUserInfo) {
    return this.pagosProveedorService.getConfigCompras(user.empresa_id);
  }

  @Put('config')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_CREAR')
  @ApiOperation({ summary: 'Actualizar configuración AP (config_compras) de la empresa' })
  updateConfig(@GetUser() user: LoginUserInfo, @Body() dto: UpdateConfigComprasDto) {
    return this.pagosProveedorService.updateConfigCompras(user.empresa_id, dto);
  }

  @Get('cuentas-pendientes')
  @RequirePermission('COMPRAS', 'CMP_CXP_CUENTA_PAGAR_VER')
  @ApiOperation({ summary: 'Obtener cuentas por pagar pendientes de un proveedor' })
  @ApiQuery({ name: 'proveedor_id', required: true })
  getCuentasPendientes(@GetUser() user: LoginUserInfo, @Query('proveedor_id') proveedorId: string) {
    return this.pagosProveedorService.getCuentasPendientes(user.empresa_id, proveedorId);
  }

  @Get('cuentas-pagar')
  @RequirePermission('COMPRAS', 'CMP_CXP_CUENTA_PAGAR_VER')
  @ApiOperation({ summary: 'Listar cuentas por pagar' })
  @ApiQuery({ name: 'page', required: false })
  @ApiQuery({ name: 'limit', required: false })
  @ApiQuery({ name: 'fecha_tipo', required: false, description: 'vencimiento | emision' })
  @ApiQuery({ name: 'fecha_desde', required: false, description: 'Filtro por tipo de fecha desde (YYYY-MM-DD)' })
  @ApiQuery({ name: 'fecha_hasta', required: false, description: 'Filtro por tipo de fecha hasta (YYYY-MM-DD)' })
  @ApiQuery({ name: 'proveedor_id', required: false })
  @ApiQuery({ name: 'compra_id', required: false })
  @ApiQuery({ name: 'origen_tipo', required: false, description: 'compra | gasto' })
  @ApiQuery({ name: 'estado', required: false, description: 'pendiente | parcial | pagada | vencida | anulada' })
  @ApiQuery({ name: 'busqueda', required: false, description: 'Factura, razón social o RUC' })
  findCuentasPagar(
    @GetUser() user: LoginUserInfo,
    @Query('page') page?: string,
    @Query('limit') limit?: string,
    @Query('fecha_tipo') fecha_tipo?: string,
    @Query('fecha_desde') fecha_desde?: string,
    @Query('fecha_hasta') fecha_hasta?: string,
    @Query('proveedor_id') proveedor_id?: string,
    @Query('compra_id') compra_id?: string,
    @Query('origen_tipo') origen_tipo?: string,
    @Query('estado') estado?: string,
    @Query('busqueda') busqueda?: string,
  ) {
    return this.pagosProveedorService.findCuentasPagar(user.empresa_id, {
      page,
      limit,
      fecha_tipo,
      fecha_desde,
      fecha_hasta,
      proveedor_id,
      compra_id,
      origen_tipo,
      estado,
      busqueda,
    });
  }

  @Get('cuentas-pagar/dashboard')
  @RequirePermission('COMPRAS', 'CMP_CXP_CUENTA_PAGAR_VER')
  @ApiOperation({ summary: 'Resumen dashboard de cuentas por pagar' })
  @ApiQuery({ name: 'fecha_tipo', required: false, description: 'vencimiento | emision' })
  @ApiQuery({ name: 'fecha_desde', required: false, description: 'Filtro por tipo de fecha desde (YYYY-MM-DD)' })
  @ApiQuery({ name: 'fecha_hasta', required: false, description: 'Filtro por tipo de fecha hasta (YYYY-MM-DD)' })
  @ApiQuery({ name: 'origen_tipo', required: false, description: 'compra | gasto' })
  getCuentasPagarDashboard(
    @GetUser() user: LoginUserInfo,
    @Query('fecha_tipo') fecha_tipo?: string,
    @Query('fecha_desde') fecha_desde?: string,
    @Query('fecha_hasta') fecha_hasta?: string,
    @Query('origen_tipo') origen_tipo?: string,
  ) {
    return this.pagosProveedorService.getCuentasPagarDashboard(user.empresa_id, {
      fecha_tipo,
      fecha_desde,
      fecha_hasta,
      origen_tipo,
    });
  }

  @Patch(':id/anular')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_ANULAR')
  @ApiOperation({ summary: 'Anular orden de pago y revertir impacto en cuentas por pagar' })
  anular(
    @Param('id') id: string,
    @Body() dto: AnularOrdenPagoProveedorDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.pagosProveedorService.anular(id, dto, user.empresa_id, user.id);
  }

  @Patch(':id/enviar-aprobacion')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_CREAR')
  @ApiOperation({ summary: 'Enviar orden de pago a aprobación' })
  enviarAprobacion(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.pagosProveedorService.enviarAprobacion(id, user.empresa_id, user.id);
  }

  @Patch(':id/aprobar')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_APROBAR')
  @ApiOperation({ summary: 'Aprobar orden de pago pendiente de aprobación' })
  aprobar(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.pagosProveedorService.aprobar(id, user.empresa_id, user.id);
  }

  @Patch(':id/rechazar')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_APROBAR')
  @ApiOperation({ summary: 'Rechazar orden de pago pendiente de aprobación' })
  rechazar(@Param('id') id: string, @Body() dto: RechazarOrdenPagoProveedorDto, @GetUser() user: LoginUserInfo) {
    return this.pagosProveedorService.rechazar(id, dto.motivo, user.empresa_id, user.id);
  }

  @Post(':id/ejecutar')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_CREAR')
  @ApiOperation({ summary: 'Ejecutar una orden BORRADOR y aplicar el pago en cuentas por pagar' })
  ejecutar(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.pagosProveedorService.ejecutar(id, user.empresa_id, user.id);
  }

  @Post(':id/reintentar-contabilidad')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_CREAR')
  @ApiOperation({ summary: 'Reintentar integración contable de la orden de pago (estado PAGADO)' })
  reintentarContabilidad(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.pagosProveedorService.reintentarContabilidad(id, user.empresa_id);
  }

  @Post(':id/reintentar-tesoreria')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_CREAR')
  @ApiOperation({ summary: 'Reintentar el registro del egreso en Tesorería de la orden de pago (estado PAGADO/APLICADO)' })
  reintentarTesoreria(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.pagosProveedorService.reintentarTesoreria(id, user.empresa_id, user.id);
  }

  @Get(':id')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_VER')
  @ApiOperation({ summary: 'Obtener detalle de orden de pago a proveedor' })
  findOne(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.pagosProveedorService.findOne(id, user.empresa_id);
  }

  @Get('cuentas-pagar/reportes/detalle')
  @RequirePermission('COMPRAS', 'CMP_CXP_CUENTA_PAGAR_VER')
  @ApiOperation({ summary: 'Cuentas a pagar: detalle por proveedor con cuotas, pagos y antigüedad (Excel)' })
  @ApiQuery({ name: 'proveedor_ids', required: false, description: 'IDs separados por coma. Vacío = todos' })
  @ApiQuery({ name: 'solo_vencidas', required: false })
  async reporteDetalleCuentasPagar(
    @GetUser() user: LoginUserInfo,
    @Res() res: any,
    @Query('proveedor_ids') proveedorIds?: string,
    @Query('solo_vencidas') soloVencidas?: string,
  ) {
    const buffer = await this.cuentasPagarReportes.detalleXlsx(user.empresa_id, user.id, {
      proveedorIds: this.listaIds(proveedorIds),
      soloVencidas: soloVencidas === 'true' || soloVencidas === '1',
    });
    return this.enviarXlsx(res, buffer, 'cuentas-pagar-detalle');
  }

  @Get('cuentas-pagar/reportes/plan-pagos')
  @RequirePermission('COMPRAS', 'CMP_CXP_CUENTA_PAGAR_VER')
  @ApiOperation({ summary: 'Plan de pagos a proveedores: saldo pendiente por mes de vencimiento (Excel)' })
  @ApiQuery({ name: 'meses', required: false, description: 'Meses a mostrar desde el actual (1-12, por defecto 3)' })
  @ApiQuery({ name: 'proveedor_ids', required: false, description: 'IDs separados por coma. Vacío = todos' })
  async reportePlanPagos(
    @GetUser() user: LoginUserInfo,
    @Res() res: any,
    @Query('meses') meses?: string,
    @Query('proveedor_ids') proveedorIds?: string,
  ) {
    const buffer = await this.cuentasPagarReportes.planPagosXlsx(user.empresa_id, user.id, {
      meses: meses ? parseInt(meses, 10) || undefined : undefined,
      proveedorIds: this.listaIds(proveedorIds),
    });
    return this.enviarXlsx(res, buffer, 'plan-pagos-proveedores');
  }

  @Get('cuentas-pagar/plan-pagos')
  @RequirePermission('COMPRAS', 'CMP_PP_PLAN_PAGOS_VER')
  @ApiOperation({ summary: 'Plan de pagos: cada cuota pendiente en el período (semana, quincena o mes, según la empresa) en que se programó pagarla' })
  @ApiQuery({ name: 'periodos', required: false, description: 'Períodos a mostrar desde el actual (1-24, por defecto 6)' })
  @ApiQuery({ name: 'proveedor_ids', required: false, description: 'IDs separados por coma. Vacío = todos' })
  planPagosProgramado(@GetUser() user: LoginUserInfo, @Query('periodos') periodos?: string, @Query('proveedor_ids') proveedorIds?: string) {
    return this.planPagos.plan(user.empresa_id, user.id, { periodos: this.entero(periodos), proveedorIds: this.listaIds(proveedorIds) });
  }

  @Get('cuentas-pagar/reportes/plan-pagos-programado')
  @RequirePermission('COMPRAS', 'CMP_PP_PLAN_PAGOS_EXPORTAR')
  @ApiOperation({ summary: 'Plan de pagos por período de pago (Excel)' })
  @ApiQuery({ name: 'periodos', required: false })
  @ApiQuery({ name: 'proveedor_ids', required: false })
  async reportePlanPagosProgramado(
    @GetUser() user: LoginUserInfo,
    @Res() res: any,
    @Query('periodos') periodos?: string,
    @Query('proveedor_ids') proveedorIds?: string,
  ) {
    const buffer = await this.planPagos.planXlsx(user.empresa_id, user.id, { periodos: this.entero(periodos), proveedorIds: this.listaIds(proveedorIds) });
    return this.enviarXlsx(res, buffer, 'plan-pagos-programado');
  }

  @Patch('cuentas-pagar/programacion')
  @RequirePermission('COMPRAS', 'CMP_PP_PLAN_PAGOS_PROGRAMAR')
  @ApiOperation({ summary: 'Programar (o quitar) la fecha de pago de un lote de cuotas' })
  programarPagos(@Body() dto: ProgramarPagosDto, @GetUser() user: LoginUserInfo) {
    return this.planPagos.programar(user.empresa_id, user.id, dto.cuenta_pagar_ids, dto.fecha ?? null);
  }

  @Get('cuentas-pagar/extracto/:proveedorId')
  @RequirePermission('COMPRAS', 'CMP_CXP_CUENTA_PAGAR_VER')
  @ApiOperation({ summary: 'Extracto de cuenta del proveedor: movimientos con débito, crédito y saldo corrido' })
  @ApiQuery({ name: 'desde', required: false, description: 'YYYY-MM-DD (por defecto, 1 de enero del año en curso)' })
  @ApiQuery({ name: 'hasta', required: false, description: 'YYYY-MM-DD (por defecto, hoy)' })
  extracto(@Param('proveedorId') proveedorId: string, @GetUser() user: LoginUserInfo, @Query('desde') desde?: string, @Query('hasta') hasta?: string) {
    return this.extractoProveedor.extracto(user.empresa_id, proveedorId, user.id, { desde, hasta });
  }

  @Get('cuentas-pagar/extracto/:proveedorId/excel')
  @RequirePermission('COMPRAS', 'CMP_CXP_CUENTA_PAGAR_VER')
  @ApiOperation({ summary: 'Extracto de cuenta del proveedor (Excel)' })
  async extractoExcel(
    @Param('proveedorId') proveedorId: string,
    @GetUser() user: LoginUserInfo,
    @Res() res: any,
    @Query('desde') desde?: string,
    @Query('hasta') hasta?: string,
  ) {
    const buffer = await this.extractoProveedor.extractoXlsx(user.empresa_id, proveedorId, user.id, { desde, hasta });
    return this.enviarXlsx(res, buffer, 'extracto-proveedor');
  }

  @Get('cuentas-pagar/extracto/:proveedorId/pdf')
  @RequirePermission('COMPRAS', 'CMP_CXP_CUENTA_PAGAR_VER')
  @ApiOperation({ summary: 'Extracto de cuenta del proveedor (PDF, una moneda)' })
  @ApiQuery({ name: 'moneda', required: false, description: 'Código de moneda (por defecto, la primera con movimientos)' })
  async extractoPdf(
    @Param('proveedorId') proveedorId: string,
    @GetUser() user: LoginUserInfo,
    @Res() res: any,
    @Query('desde') desde?: string,
    @Query('hasta') hasta?: string,
    @Query('moneda') moneda?: string,
  ) {
    const buffer = await this.extractoProveedor.extractoPdf(user.empresa_id, proveedorId, user.id, { desde, hasta, moneda });
    res.set({ 'Content-Type': 'application/pdf', 'Content-Disposition': 'inline; filename="extracto-proveedor.pdf"', 'Content-Length': buffer.length });
    return res.send(buffer);
  }

  private entero(valor?: string): number | undefined {
    return valor ? parseInt(valor, 10) || undefined : undefined;
  }

  private listaIds(valor?: string): string[] | undefined {
    const ids = (valor ?? '').split(',').map((s) => s.trim()).filter(Boolean);
    return ids.length ? ids : undefined;
  }

  private enviarXlsx(res: any, buffer: Buffer, nombre: string) {
    const hoy = new Date().toISOString().slice(0, 10);
    res.set({
      'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      'Content-Disposition': `attachment; filename="${nombre}-${hoy}.xlsx"`,
      'Content-Length': buffer.length,
    });
    return res.send(buffer);
  }

  @Get('cuentas-pagar/estado-cuenta/:proveedorId')
  @RequirePermission('COMPRAS', 'CMP_CXP_CUENTA_PAGAR_VER')
  @ApiOperation({ summary: 'Estado de cuenta de un proveedor (JSON)' })
  getEstadoCuentaProveedor(@Param('proveedorId') proveedorId: string, @GetUser() user: LoginUserInfo) {
    return this.pagosProveedorService.getEstadoCuentaProveedor(user.empresa_id, proveedorId, user.id);
  }

  @Get('cuentas-pagar/estado-cuenta/:proveedorId/pdf')
  @RequirePermission('COMPRAS', 'CMP_CXP_CUENTA_PAGAR_VER')
  @ApiOperation({ summary: 'Generar PDF de estado de cuenta de un proveedor' })
  @ApiQuery({ name: 'tipo', required: false, enum: ['view', 'base64'] })
  @ApiQuery({ name: 'incluir_todas', required: false, description: 'Incluir cuotas no vencidas' })
  async getEstadoCuentaProveedorPdf(
    @Param('proveedorId') proveedorId: string,
    @GetUser() user: LoginUserInfo,
    @Query('tipo') tipo?: string,
    @Query('incluir_todas') incluirTodas?: string,
    @Res() res?: any,
  ) {
    const result = await this.pagosProveedorService.generateEstadoCuentaProveedorPdf(user.empresa_id, proveedorId, {
      tipo_retorno: tipo || 'view',
      incluir_todas: incluirTodas === 'true' || incluirTodas === '1',
      usuarioId: user.id,
    });
    if (result.base64) return res.json({ status: 'success', pdf_base64: result.base64 });
    res.set({
      'Content-Type': result.contentType,
      'Content-Disposition': `inline; filename="${result.fileName}"`,
      'Content-Length': result.pdfBuffer.length,
    });
    return res.send(result.pdfBuffer);
  }

  @Get(':id/pdf')
  @RequirePermission('COMPRAS', 'CMP_OP_ORDEN_PAGO_VER')
  @ApiOperation({ summary: 'Generar PDF A4 de una orden de pago a proveedor' })
  async getPdf(@Param('id') id: string, @GetUser() user: LoginUserInfo, @Res() res: any) {
    const result = await this.pagosProveedorService.generatePdf(id, user.empresa_id);

    res.set({
      'Content-Type': 'application/pdf',
      'Content-Disposition': `inline; filename="${result.filename}"`,
      'Content-Length': result.pdfBuffer.length,
    });

    return res.send(result.pdfBuffer);
  }
}
