import {
  BadRequestException,
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Patch,
  Post,
  Query,
  Res,
  UploadedFile,
  UseGuards,
  UseInterceptors,
  ValidationPipe,
} from '@nestjs/common';
import type { Response } from 'express';
import { FileInterceptor } from '@nestjs/platform-express';
import { AuthGuard } from '@nestjs/passport';
import {
  ApiBearerAuth,
  ApiQuery,
  ApiTags,
} from '@nestjs/swagger';
import type { LoginUserInfo } from '../auth/dto/login.dto';
import { GetUser } from '../auth/get-user.decorator';
import { RequirePermission } from '../auth/decorators/require-permission.decorator';
import { PermissionGuard } from '../auth/guards/permission.guard';
import { Auditable } from '../audit/auditable.decorator';
import { AuditInterceptor } from '../audit/audit.interceptor';
import { PrismaService } from '../prisma/prisma.service';
import { CreateTesCuentaDto, UpdateTesCuentaDto } from './dto/tes-cuenta.dto';
import {
  AnularTesMovimientoDto,
  CreateTesMovimientoDto,
} from './dto/tes-movimiento.dto';
import { UpdateTesMovimientoDto } from './dto/update-tes-movimiento.dto';
import { CreateTesCategoriaDto, UpdateTesCategoriaDto } from './dto/tes-categoria.dto';
import { AcreditarChequeDto, CreateTesChequeDto, FilterTesChequeDto, RechazarChequeDto, UpdateTesChequeDto } from './dto/tes-cheque.dto';
import { AnularTesTransferenciaDto, CreateTesTransferenciaDto, FilterTesTransferenciaDto } from './dto/tes-transferencia.dto';
import {
  ActualizarLineaConciliacionDto,
  AutoConciliarExtractoDto,
  ConciliarLineaDto,
  CrearMovimientoDesdeLineaDto,
  ImportarExtractoDto,
  ImportarExtractoPdfBodyDto,
} from './dto/tes-conciliacion.dto';
import { TesCuentasService } from './services/tes-cuentas.service';
import { TesMovimientosService } from './services/tes-movimientos.service';
import { TesCategoriaService } from './services/tes-categorias.service';
import { TesChequeService } from './services/tes-cheques.service';
import { TesTransferenciasService } from './services/tes-transferencias.service';
import { TesReportesService } from './services/tes-reportes.service';
import { TesConciliacionService } from './services/tes-conciliacion.service';
import { TesReglasService } from './services/tes-reglas.service';

@ApiTags('Tesorería y Bancos')
@Controller({ version: '1', path: 'tes' })
@UseGuards(AuthGuard('jwt'))
@ApiBearerAuth()
export class TesController {
  constructor(
    private readonly tesCuentas: TesCuentasService,
    private readonly tesMovimientos: TesMovimientosService,
    private readonly tesCategorias: TesCategoriaService,
    private readonly tesCheques: TesChequeService,
    private readonly tesTransferencias: TesTransferenciasService,
    private readonly tesReportes: TesReportesService,
    private readonly tesConciliacion: TesConciliacionService,
    private readonly tesReglas: TesReglasService,
    private readonly prisma: PrismaService,
  ) {}

  // ==================== CUENTAS ====================

  @Get('cuentas')
  @RequirePermission('TESORERIA', 'TES_CTA_CUENTA_BANCARIA_VER')
  @UseGuards(PermissionGuard)
  @ApiQuery({ name: 'activo', required: false, description: 'true = sólo cuentas activas' })
  getCuentas(@GetUser() user: LoginUserInfo, @Query('activo') activo?: string) {
    return this.tesCuentas.findAll(user.empresa_id, activo === 'true');
  }

  @Get('cuentas/saldos')
  @RequirePermission('TESORERIA', 'TES_CTA_CUENTA_BANCARIA_VER')
  @UseGuards(PermissionGuard)
  getSaldos(@GetUser() user: LoginUserInfo) {
    return this.tesCuentas.getSaldos(user.empresa_id);
  }

  @Get('cuentas/:id')
  @RequirePermission('TESORERIA', 'TES_CTA_CUENTA_BANCARIA_VER')
  @UseGuards(PermissionGuard)
  getCuenta(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.tesCuentas.findOne(id, user.empresa_id);
  }

  @Post('cuentas')
  @RequirePermission('TESORERIA', 'TES_CTA_CUENTA_BANCARIA_CREAR')
  @UseGuards(PermissionGuard)
  createCuenta(
    @Body(ValidationPipe) dto: CreateTesCuentaDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.tesCuentas.create(dto, user.empresa_id);
  }

  @Patch('cuentas/:id')
  @RequirePermission('TESORERIA', 'TES_CTA_CUENTA_BANCARIA_EDITAR')
  @UseGuards(PermissionGuard)
  updateCuenta(
    @Param('id') id: string,
    @Body(ValidationPipe) dto: UpdateTesCuentaDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.tesCuentas.update(id, dto, user.empresa_id);
  }

  @Patch('cuentas/:id/toggle-activo')
  @RequirePermission('TESORERIA', 'TES_CTA_CUENTA_BANCARIA_EDITAR')
  @UseGuards(PermissionGuard)
  toggleActivo(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.tesCuentas.toggleActivo(id, user.empresa_id);
  }

  // ==================== MOVIMIENTOS ====================

  @Get('movimientos')
  @RequirePermission('TESORERIA', 'TES_MOV_MOVIMIENTO_VER')
  @UseGuards(PermissionGuard)
  @ApiQuery({ name: 'cuentaId', required: false })
  @ApiQuery({ name: 'tipo', required: false })
  @ApiQuery({ name: 'estado', required: false })
  @ApiQuery({ name: 'fechaDesde', required: false })
  @ApiQuery({ name: 'fechaHasta', required: false })
  @ApiQuery({ name: 'search', required: false, description: 'Descripción, referencia, cuenta o importe' })
  @ApiQuery({ name: 'page', required: false, type: Number })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  getMovimientos(
    @GetUser() user: LoginUserInfo,
    @Query('cuentaId') cuentaId?: string,
    @Query('tipo') tipo?: string,
    @Query('estado') estado?: string,
    @Query('fechaDesde') fechaDesde?: string,
    @Query('fechaHasta') fechaHasta?: string,
    @Query('search') search?: string,
    @Query('page') page?: string,
    @Query('limit') limit?: string,
  ) {
    return this.tesMovimientos.findAll(user.empresa_id, {
      cuentaId,
      tipo,
      estado,
      fechaDesde,
      fechaHasta,
      search,
      page: page ? parseInt(page) : 1,
      limit: limit ? parseInt(limit) : 20,
    });
  }

  @Get('movimientos/extracto')
  @RequirePermission('TESORERIA', 'TES_MOV_MOVIMIENTO_VER')
  @UseGuards(PermissionGuard)
  @ApiQuery({ name: 'cuentaId', required: true })
  @ApiQuery({ name: 'fechaDesde', required: false })
  @ApiQuery({ name: 'fechaHasta', required: false })
  getExtracto(
    @GetUser() user: LoginUserInfo,
    @Query('cuentaId') cuentaId: string,
    @Query('fechaDesde') fechaDesde?: string,
    @Query('fechaHasta') fechaHasta?: string,
  ) {
    return this.tesMovimientos.getExtracto(user.empresa_id, cuentaId, fechaDesde, fechaHasta);
  }

  @Get('movimientos/:id')
  @RequirePermission('TESORERIA', 'TES_MOV_MOVIMIENTO_VER')
  @UseGuards(PermissionGuard)
  getMovimiento(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.tesMovimientos.findOne(id, user.empresa_id);
  }

  @Post('movimientos')
  @RequirePermission('TESORERIA', 'TES_MOV_MOVIMIENTO_CREAR')
  @UseGuards(PermissionGuard)
  createMovimiento(
    @Body(ValidationPipe) dto: CreateTesMovimientoDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.tesMovimientos.create(dto, user.empresa_id, user.id);
  }

  @Post('movimientos/:id/confirmar')
  @RequirePermission('TESORERIA', 'TES_MOV_MOVIMIENTO_CREAR')
  @UseGuards(PermissionGuard)
  confirmarMovimiento(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.tesMovimientos.confirmar(id, user.empresa_id, user.id);
  }

  @Post('movimientos/:id/aprobar')
  @RequirePermission('TESORERIA', 'TES_MOV_MOVIMIENTO_APROBAR')
  @UseGuards(PermissionGuard)
  aprobarMovimiento(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.tesMovimientos.aprobar(id, user.empresa_id, user.id);
  }

  @Post('movimientos/:id/anular')
  @RequirePermission('TESORERIA', 'TES_MOV_MOVIMIENTO_ANULAR')
  @UseGuards(PermissionGuard)
  anularMovimiento(
    @Param('id') id: string,
    @Body(ValidationPipe) dto: AnularTesMovimientoDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.tesMovimientos.anular(id, dto, user.empresa_id, user.id);
  }

  @Patch('movimientos/:id')
  @RequirePermission('TESORERIA', 'TES_MOV_MOVIMIENTO_EDITAR')
  @UseGuards(PermissionGuard)
  @Auditable('tes_movimiento')
  @UseInterceptors(AuditInterceptor)
  actualizarMovimiento(
    @Param('id') id: string,
    @Body(ValidationPipe) dto: UpdateTesMovimientoDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.tesMovimientos.actualizar(id, dto, user.empresa_id, user.id);
  }

  @Delete('movimientos/:id')
  @RequirePermission('TESORERIA', 'TES_MOV_MOVIMIENTO_ELIMINAR')
  @UseGuards(PermissionGuard)
  @Auditable('tes_movimiento')
  @UseInterceptors(AuditInterceptor)
  eliminarMovimiento(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.tesMovimientos.eliminar(id, user.empresa_id, user.id);
  }

  // ==================== CATEGORIAS ====================

  @Get('categorias')
  @RequirePermission('TESORERIA', 'TES_CAT_CATEGORIA_VER')
  @UseGuards(PermissionGuard)
  getCategorias(@GetUser() user: LoginUserInfo) {
    return this.tesCategorias.findAll(user.empresa_id);
  }

  @Post('categorias')
  @RequirePermission('TESORERIA', 'TES_CAT_CATEGORIA_CREAR')
  @UseGuards(PermissionGuard)
  createCategoria(
    @Body(ValidationPipe) dto: CreateTesCategoriaDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.tesCategorias.create(dto, user.empresa_id);
  }

  @Patch('categorias/:id')
  @RequirePermission('TESORERIA', 'TES_CAT_CATEGORIA_EDITAR')
  @UseGuards(PermissionGuard)
  updateCategoria(
    @Param('id') id: string,
    @Body(ValidationPipe) dto: UpdateTesCategoriaDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.tesCategorias.update(id, dto, user.empresa_id);
  }

  @Delete('categorias/:id')
  @RequirePermission('TESORERIA', 'TES_CAT_CATEGORIA_ELIMINAR')
  @UseGuards(PermissionGuard)
  deleteCategoria(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.tesCategorias.remove(id, user.empresa_id);
  }

  @Patch('categorias/:id/cuenta-contable')
  @RequirePermission('TESORERIA', 'TES_CAT_CATEGORIA_EDITAR')
  @UseGuards(PermissionGuard)
  upsertMapeoContable(
    @Param('id') id: string,
    @Body() body: { cuenta_contable_id: string | null },
    @GetUser() user: LoginUserInfo,
  ) {
    return this.tesCategorias.upsertMapeoContable(id, body.cuenta_contable_id, user.empresa_id);
  }

  // ==================== CHEQUES ====================

  @Get('cheques')
  @RequirePermission('TESORERIA', 'TES_CHQ_CHEQUE_VER')
  @UseGuards(PermissionGuard)
  @ApiQuery({ name: 'tipo', required: false })
  @ApiQuery({ name: 'estado', required: false })
  @ApiQuery({ name: 'cuenta_id', required: false })
  @ApiQuery({ name: 'proximos_vencer', required: false })
  getCheques(
    @GetUser() user: LoginUserInfo,
    @Query('tipo') tipo?: string,
    @Query('estado') estado?: string,
    @Query('cuenta_id') cuenta_id?: string,
    @Query('proximos_vencer') proximos_vencer?: string,
  ) {
    return this.tesCheques.findAll(user.empresa_id, { tipo, estado, cuenta_id, proximos_vencer } as any);
  }

  @Post('cheques')
  @RequirePermission('TESORERIA', 'TES_CHQ_CHEQUE_CREAR')
  @UseGuards(PermissionGuard)
  createCheque(@Body(ValidationPipe) dto: CreateTesChequeDto, @GetUser() user: LoginUserInfo) {
    return this.tesCheques.create(dto, user.empresa_id, user.id);
  }

  @Patch('cheques/:id')
  @RequirePermission('TESORERIA', 'TES_CHQ_CHEQUE_EDITAR')
  @UseGuards(PermissionGuard)
  updateCheque(@Param('id') id: string, @Body(ValidationPipe) dto: UpdateTesChequeDto, @GetUser() user: LoginUserInfo) {
    return this.tesCheques.update(id, dto, user.empresa_id, user.id);
  }

  @Post('cheques/:id/depositar')
  @RequirePermission('TESORERIA', 'TES_CHQ_CHEQUE_DEPOSITAR')
  @UseGuards(PermissionGuard)
  depositarCheque(@Param('id') id: string, @Body(ValidationPipe) dto: AcreditarChequeDto, @GetUser() user: LoginUserInfo) {
    return this.tesCheques.depositar(id, dto, user.empresa_id, user.id);
  }

  @Post('cheques/:id/debitar')
  @RequirePermission('TESORERIA', 'TES_CHQ_CHEQUE_DEPOSITAR')
  @UseGuards(PermissionGuard)
  debitarCheque(@Param('id') id: string, @Body(ValidationPipe) dto: AcreditarChequeDto, @GetUser() user: LoginUserInfo) {
    return this.tesCheques.debitar(id, dto, user.empresa_id, user.id);
  }

  @Post('cheques/:id/rechazar')
  @RequirePermission('TESORERIA', 'TES_CHQ_CHEQUE_ANULAR')
  @UseGuards(PermissionGuard)
  rechazarCheque(@Param('id') id: string, @Body(ValidationPipe) dto: RechazarChequeDto, @GetUser() user: LoginUserInfo) {
    return this.tesCheques.rechazar(id, dto, user.empresa_id, user.id);
  }

  @Post('cheques/:id/deshacer-deposito')
  @RequirePermission('TESORERIA', 'TES_CHQ_CHEQUE_ANULAR')
  @UseGuards(PermissionGuard)
  // Deshace un depósito/débito cargado por error: anula el movimiento (no crea
  // reversa) y devuelve el cheque a la cartera.
  deshacerDepositoCheque(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.tesCheques.deshacerDeposito(id, user.empresa_id, user.id);
  }

  @Post('cheques/:id/devolver')
  @RequirePermission('TESORERIA', 'TES_CHQ_CHEQUE_ANULAR')
  @UseGuards(PermissionGuard)
  devolverCheque(@Param('id') id: string, @Body(ValidationPipe) dto: RechazarChequeDto, @GetUser() user: LoginUserInfo) {
    return this.tesCheques.devolver(id, dto, user.empresa_id, user.id);
  }

  @Post('cheques/:id/anular')
  @RequirePermission('TESORERIA', 'TES_CHQ_CHEQUE_ANULAR')
  @UseGuards(PermissionGuard)
  anularCheque(@Param('id') id: string, @Body(ValidationPipe) dto: RechazarChequeDto, @GetUser() user: LoginUserInfo) {
    return this.tesCheques.anular(id, dto, user.empresa_id, user.id);
  }

  @Delete('cheques/:id')
  @RequirePermission('TESORERIA', 'TES_CHQ_CHEQUE_ELIMINAR')
  @UseGuards(PermissionGuard)
  deleteCheque(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.tesCheques.remove(id, user.empresa_id, user.id);
  }

  // ==================== BANCOS ====================

  @Get('bancos')
  getBancos(@GetUser() user: LoginUserInfo) {
    return this.prisma.bancos.findMany({
      where: {
        OR: [{ empresa_id: null }, { empresa_id: user.empresa_id }],
        activo: true,
      },
      orderBy: { descripcion: 'asc' },
    });
  }

  // ==================== CONFIG ====================

  @Get('config')
  @RequirePermission('TESORERIA', 'TES_PAR_PARAMETRO_VER')
  @UseGuards(PermissionGuard)
  getConfig(@GetUser() user: LoginUserInfo) {
    return this.prisma.tes_config.findUnique({
      where: { empresa_id: user.empresa_id },
    });
  }

  @Patch('config')
  @RequirePermission('TESORERIA', 'TES_PAR_PARAMETRO_CONFIGURAR')
  @UseGuards(PermissionGuard)
  async updateConfig(@Body() dto: Record<string, any>, @GetUser() user: LoginUserInfo) {
    const existing = await this.prisma.tes_config.findUnique({
      where: { empresa_id: user.empresa_id },
    });

    if (existing) {
      return this.prisma.tes_config.update({
        where: { empresa_id: user.empresa_id },
        data: dto,
      });
    }

    return this.prisma.tes_config.create({
      data: {
        empresa_id: user.empresa_id,
        ...dto,
      },
    });
  }

  // ==================== TRANSFERENCIAS ====================

  @Get('transferencias')
  @RequirePermission('TESORERIA', 'TES_TRF_TRANSFERENCIA_VER')
  @UseGuards(PermissionGuard)
  getTransferencias(@Query() filters: FilterTesTransferenciaDto, @GetUser() user: LoginUserInfo) {
    return this.tesTransferencias.findAll(user.empresa_id, filters);
  }

  @Post('transferencias')
  @RequirePermission('TESORERIA', 'TES_TRF_TRANSFERENCIA_CREAR')
  @UseGuards(PermissionGuard)
  createTransferencia(@Body(ValidationPipe) dto: CreateTesTransferenciaDto, @GetUser() user: LoginUserInfo) {
    return this.tesTransferencias.create(dto, user.empresa_id, user.id);
  }

  @Post('transferencias/:id/anular')
  @RequirePermission('TESORERIA', 'TES_TRF_TRANSFERENCIA_ANULAR')
  @UseGuards(PermissionGuard)
  anularTransferencia(@Param('id') id: string, @Body(ValidationPipe) dto: AnularTesTransferenciaDto, @GetUser() user: LoginUserInfo) {
    return this.tesTransferencias.anular(id, dto, user.empresa_id, user.id);
  }

  // ==================== REPORTES ====================

  @Get('reportes/posicion-caja')
  @RequirePermission('TESORERIA', 'TES_REP_LIBRO_CAJA_VER')
  @UseGuards(PermissionGuard)
  posicionCaja(@GetUser() user: LoginUserInfo) {
    return this.tesReportes.posicionCaja(user.empresa_id);
  }

  @Get('reportes/cash-flow-real')
  @RequirePermission('TESORERIA', 'TES_REP_FLUJO_CAJA_VER')
  @UseGuards(PermissionGuard)
  @ApiQuery({ name: 'fecha_desde', required: true })
  @ApiQuery({ name: 'fecha_hasta', required: true })
  @ApiQuery({ name: 'cuenta_id', required: false })
  @ApiQuery({ name: 'moneda', required: false })
  cashFlowReal(
    @GetUser() user: LoginUserInfo,
    @Query('fecha_desde') fecha_desde: string,
    @Query('fecha_hasta') fecha_hasta: string,
    @Query('cuenta_id') cuenta_id?: string,
    @Query('moneda') moneda?: string,
  ) {
    return this.tesReportes.cashFlowReal(user.empresa_id, { fecha_desde, fecha_hasta, cuenta_id, moneda });
  }

  @Get('reportes/cash-flow-proyectado')
  @RequirePermission('TESORERIA', 'TES_REP_FLUJO_CAJA_VER')
  @UseGuards(PermissionGuard)
  @ApiQuery({ name: 'fecha_desde', required: true })
  @ApiQuery({ name: 'fecha_hasta', required: true })
  cashFlowProyectado(
    @GetUser() user: LoginUserInfo,
    @Query('fecha_desde') fecha_desde: string,
    @Query('fecha_hasta') fecha_hasta: string,
  ) {
    return this.tesReportes.cashFlowProyectado(user.empresa_id, { fecha_desde, fecha_hasta });
  }

  @Get('reportes/extracto-cuenta')
  @RequirePermission('TESORERIA', 'TES_REP_LIBRO_BANCO_VER')
  @UseGuards(PermissionGuard)
  @ApiQuery({ name: 'cuenta_id', required: true })
  @ApiQuery({ name: 'fecha_desde', required: true })
  @ApiQuery({ name: 'fecha_hasta', required: true })
  extractoCuenta(
    @GetUser() user: LoginUserInfo,
    @Query('cuenta_id') cuenta_id: string,
    @Query('fecha_desde') fecha_desde: string,
    @Query('fecha_hasta') fecha_hasta: string,
  ) {
    return this.tesReportes.extractoCuenta(user.empresa_id, { cuenta_id, fecha_desde, fecha_hasta });
  }

  @Get('reportes/cartera-cheques')
  @RequirePermission('TESORERIA', 'TES_CHQ_CHEQUE_VER')
  @UseGuards(PermissionGuard)
  @ApiQuery({ name: 'tipo_cheque', required: false })
  @ApiQuery({ name: 'estado', required: false })
  @ApiQuery({ name: 'cuenta_id', required: false })
  @ApiQuery({ name: 'fecha_desde', required: false })
  @ApiQuery({ name: 'fecha_hasta', required: false })
  carteraCheques(
    @GetUser() user: LoginUserInfo,
    @Query('tipo_cheque') tipo_cheque?: string,
    @Query('estado') estado?: string,
    @Query('cuenta_id') cuenta_id?: string,
    @Query('fecha_desde') fecha_desde?: string,
    @Query('fecha_hasta') fecha_hasta?: string,
  ) {
    return this.tesReportes.carteraCheques(user.empresa_id, { tipo_cheque, estado, cuenta_id, fecha_desde, fecha_hasta });
  }

  private static readonly XLSX_HEADERS = {
    'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  };

  @Get('reportes/posicion-caja/excel')
  @RequirePermission('TESORERIA', 'TES_REP_REPORTE_EXPORTAR')
  @UseGuards(PermissionGuard)
  async posicionCajaExcel(@GetUser() user: LoginUserInfo, @Res() res: Response) {
    const buf = await this.tesReportes.posicionCajaExcel(user.empresa_id);
    res.set({ ...TesController.XLSX_HEADERS, 'Content-Disposition': 'attachment; filename="posicion-caja.xlsx"' });
    res.send(buf);
  }

  @Get('reportes/cash-flow-real/excel')
  @RequirePermission('TESORERIA', 'TES_REP_REPORTE_EXPORTAR')
  @UseGuards(PermissionGuard)
  async cashFlowRealExcel(
    @GetUser() user: LoginUserInfo,
    @Res() res: Response,
    @Query('fecha_desde') fecha_desde: string,
    @Query('fecha_hasta') fecha_hasta: string,
    @Query('cuenta_id') cuenta_id?: string,
    @Query('moneda') moneda?: string,
  ) {
    const buf = await this.tesReportes.cashFlowRealExcel(user.empresa_id, { fecha_desde, fecha_hasta, cuenta_id, moneda });
    res.set({ ...TesController.XLSX_HEADERS, 'Content-Disposition': `attachment; filename="cash-flow-real-${fecha_desde}.xlsx"` });
    res.send(buf);
  }

  @Get('reportes/cash-flow-proyectado/excel')
  @RequirePermission('TESORERIA', 'TES_REP_REPORTE_EXPORTAR')
  @UseGuards(PermissionGuard)
  async cashFlowProyectadoExcel(
    @GetUser() user: LoginUserInfo,
    @Res() res: Response,
    @Query('fecha_desde') fecha_desde: string,
    @Query('fecha_hasta') fecha_hasta: string,
  ) {
    const buf = await this.tesReportes.cashFlowProyectadoExcel(user.empresa_id, { fecha_desde, fecha_hasta });
    res.set({ ...TesController.XLSX_HEADERS, 'Content-Disposition': `attachment; filename="cash-flow-proyectado-${fecha_desde}.xlsx"` });
    res.send(buf);
  }

  @Get('reportes/extracto-cuenta/excel')
  @RequirePermission('TESORERIA', 'TES_REP_REPORTE_EXPORTAR')
  @UseGuards(PermissionGuard)
  async extractoCuentaExcel(
    @GetUser() user: LoginUserInfo,
    @Res() res: Response,
    @Query('cuenta_id') cuenta_id: string,
    @Query('fecha_desde') fecha_desde: string,
    @Query('fecha_hasta') fecha_hasta: string,
  ) {
    const buf = await this.tesReportes.extractoCuentaExcel(user.empresa_id, { cuenta_id, fecha_desde, fecha_hasta });
    res.set({ ...TesController.XLSX_HEADERS, 'Content-Disposition': `attachment; filename="extracto-${fecha_desde}.xlsx"` });
    res.send(buf);
  }

  @Get('reportes/cartera-cheques/excel')
  @RequirePermission('TESORERIA', 'TES_REP_REPORTE_EXPORTAR')
  @UseGuards(PermissionGuard)
  async carteraChequesExcel(
    @GetUser() user: LoginUserInfo,
    @Res() res: Response,
    @Query('tipo_cheque') tipo_cheque?: string,
    @Query('estado') estado?: string,
    @Query('cuenta_id') cuenta_id?: string,
    @Query('fecha_desde') fecha_desde?: string,
    @Query('fecha_hasta') fecha_hasta?: string,
  ) {
    const buf = await this.tesReportes.carteraChequesExcel(user.empresa_id, { tipo_cheque, estado, cuenta_id, fecha_desde, fecha_hasta });
    res.set({ ...TesController.XLSX_HEADERS, 'Content-Disposition': 'attachment; filename="cartera-cheques.xlsx"' });
    res.send(buf);
  }

  // ==================== REGLAS ====================

  @Get('reglas')
  @RequirePermission('TESORERIA', 'TES_REG_REGLA_CONCILIACION_VER')
  @UseGuards(PermissionGuard)
  getReglas(@GetUser() user: LoginUserInfo) {
    return this.tesReglas.findAll(user.empresa_id);
  }

  @Get('reglas/:tipoOperacion')
  @RequirePermission('TESORERIA', 'TES_REG_REGLA_CONCILIACION_VER')
  @UseGuards(PermissionGuard)
  getRegla(@GetUser() user: LoginUserInfo, @Param('tipoOperacion') tipoOperacion: string) {
    return this.tesReglas.getRegla(user.empresa_id, tipoOperacion);
  }

  @Post('reglas')
  @RequirePermission('TESORERIA', 'TES_REG_REGLA_CONCILIACION_EDITAR')
  @UseGuards(PermissionGuard)
  upsertRegla(
    @GetUser() user: LoginUserInfo,
    @Body() body: { tipo_operacion: string; medio_pago_id?: string | null; tipo_tesoreria?: string | null; cuenta_id?: string | null; modo?: string; activo?: boolean },
  ) {
    return this.tesReglas.upsert(user.empresa_id, body);
  }

  @Delete('reglas/:id')
  @RequirePermission('TESORERIA', 'TES_REG_REGLA_CONCILIACION_ELIMINAR')
  @UseGuards(PermissionGuard)
  deleteRegla(@GetUser() user: LoginUserInfo, @Param('id') id: string) {
    return this.tesReglas.remove(user.empresa_id, id);
  }

  // ==================== CONCILIACIÓN ====================

  @Get('conciliacion/extractos')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_VER')
  @UseGuards(PermissionGuard)
  @ApiQuery({ name: 'cuenta_id', required: false })
  getExtractos(@GetUser() user: LoginUserInfo, @Query('cuenta_id') cuenta_id?: string) {
    return this.tesConciliacion.getExtractos(user.empresa_id, cuenta_id);
  }

  @Post('conciliacion/extractos')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_PROCESAR')
  @UseGuards(PermissionGuard)
  importarExtracto(@GetUser() user: LoginUserInfo, @Body(ValidationPipe) body: ImportarExtractoDto) {
    return this.tesConciliacion.importarExtracto(user.empresa_id, body, user.id);
  }

  @Post('conciliacion/importar-pdf')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_PROCESAR')
  @UseGuards(PermissionGuard)
  @UseInterceptors(FileInterceptor('file', { limits: { fileSize: 15 * 1024 * 1024 } }))
  importarExtractoPdf(
    @GetUser() user: LoginUserInfo,
    @UploadedFile() file: Express.Multer.File,
    @Body(ValidationPipe) body: ImportarExtractoPdfBodyDto,
  ) {
    if (!file) {
      throw new BadRequestException('Se requiere un archivo PDF');
    }
    const filename = String(file.originalname || '').toLowerCase();
    const isPdf = file.mimetype === 'application/pdf' || filename.endsWith('.pdf');
    if (!isPdf) {
      throw new BadRequestException('El archivo debe ser PDF');
    }
    return this.tesConciliacion.importarExtractoPdf(
      user.empresa_id,
      file.buffer,
      {
        cuenta_id: body.cuenta_id,
        periodo_desde: body.periodo_desde || undefined,
        periodo_hasta: body.periodo_hasta || undefined,
        saldo_inicial: this.parseOptionalNumber(body.saldo_inicial, 'saldo_inicial'),
        saldo_final: this.parseOptionalNumber(body.saldo_final, 'saldo_final'),
        archivo_nombre: file.originalname,
      },
      user.id,
    );
  }

  @Get('conciliacion/extractos/:id')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_VER')
  @UseGuards(PermissionGuard)
  getExtractoDetalle(@GetUser() user: LoginUserInfo, @Param('id') id: string) {
    return this.tesConciliacion.getExtractoDetalle(user.empresa_id, id);
  }

  @Post('conciliacion/extractos/:id/auto-conciliar')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_PROCESAR')
  @UseGuards(PermissionGuard)
  autoConciliarExtracto(
    @GetUser() user: LoginUserInfo,
    @Param('id') id: string,
    @Body(ValidationPipe) body: AutoConciliarExtractoDto,
  ) {
    return this.tesConciliacion.autoConciliarExtracto(user.empresa_id, id, body, user.id);
  }

  @Delete('conciliacion/extractos/:id')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_PROCESAR')
  @UseGuards(PermissionGuard)
  eliminarExtracto(@GetUser() user: LoginUserInfo, @Param('id') id: string) {
    return this.tesConciliacion.eliminarExtracto(user.empresa_id, id);
  }

  @Get('conciliacion/movimientos-sin-conciliar')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_VER')
  @UseGuards(PermissionGuard)
  @ApiQuery({ name: 'cuenta_id', required: true })
  @ApiQuery({ name: 'fecha_desde', required: true })
  @ApiQuery({ name: 'fecha_hasta', required: true })
  getMovimientosSinConciliar(
    @GetUser() user: LoginUserInfo,
    @Query('cuenta_id') cuenta_id: string,
    @Query('fecha_desde') fecha_desde: string,
    @Query('fecha_hasta') fecha_hasta: string,
  ) {
    return this.tesConciliacion.getMovimientosSinConciliar(user.empresa_id, cuenta_id, fecha_desde, fecha_hasta);
  }

  @Post('conciliacion/lineas/:lineaId/conciliar')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_PROCESAR')
  @UseGuards(PermissionGuard)
  conciliarLinea(
    @GetUser() user: LoginUserInfo,
    @Param('lineaId') lineaId: string,
    @Body(ValidationPipe) body: ConciliarLineaDto,
  ) {
    return this.tesConciliacion.conciliarLinea(user.empresa_id, lineaId, body.movimiento_id);
  }

  @Post('conciliacion/lineas/:lineaId/desconciliar')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_PROCESAR')
  @UseGuards(PermissionGuard)
  desconciliarLinea(@GetUser() user: LoginUserInfo, @Param('lineaId') lineaId: string) {
    return this.tesConciliacion.desconciliarLinea(user.empresa_id, lineaId);
  }

  @Post('conciliacion/lineas/:lineaId/sin-correspondencia')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_PROCESAR')
  @UseGuards(PermissionGuard)
  marcarSinCorrespondencia(@GetUser() user: LoginUserInfo, @Param('lineaId') lineaId: string) {
    return this.tesConciliacion.marcarSinCorrespondencia(user.empresa_id, lineaId);
  }

  @Delete('conciliacion/lineas/:lineaId/sin-correspondencia')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_PROCESAR')
  @UseGuards(PermissionGuard)
  desmarcarSinCorrespondencia(@GetUser() user: LoginUserInfo, @Param('lineaId') lineaId: string) {
    return this.tesConciliacion.desmarcarSinCorrespondencia(user.empresa_id, lineaId);
  }

  @Post('conciliacion/lineas/:lineaId/crear-movimiento')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_PROCESAR')
  @UseGuards(PermissionGuard)
  crearMovimientoDesdeLinea(
    @GetUser() user: LoginUserInfo,
    @Param('lineaId') lineaId: string,
    @Body(ValidationPipe) body: CrearMovimientoDesdeLineaDto,
  ) {
    return this.tesConciliacion.crearMovimientoDesdeLinea(user.empresa_id, lineaId, body, user.id);
  }

  @Patch('conciliacion/lineas/:lineaId')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_PROCESAR')
  @UseGuards(PermissionGuard)
  actualizarLinea(
    @GetUser() user: LoginUserInfo,
    @Param('lineaId') lineaId: string,
    @Body(ValidationPipe) body: ActualizarLineaConciliacionDto,
  ) {
    return this.tesConciliacion.actualizarLinea(user.empresa_id, lineaId, body);
  }

  @Get('conciliacion/extractos/:id/diferencias')
  @RequirePermission('TESORERIA', 'TES_CONC_CONCILIACION_VER')
  @UseGuards(PermissionGuard)
  getDiferencias(@GetUser() user: LoginUserInfo, @Param('id') id: string) {
    return this.tesConciliacion.getDiferencias(user.empresa_id, id);
  }

  private parseOptionalNumber(raw: string | undefined, field: string): number | undefined {
    if (raw === undefined || raw === null || raw === '') {
      return undefined;
    }
    const parsed = Number(String(raw).replace(/\./g, '').replace(',', '.'));
    if (!Number.isFinite(parsed)) {
      throw new BadRequestException(`Campo ${field} inválido`);
    }
    return parsed;
  }
}
