import { Body, Controller, Get, Param, Post, Query, Res, UseGuards, ValidationPipe } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { Response } from 'express';
import { RequireModule, RequirePermission } from 'src/auth/decorators';
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 { ContabilidadConciliacionService } from './contabilidad-conciliacion.service';
import { RecontabilizacionService } from './recontabilizacion.service';
import { SaludContableService } from './salud-contable.service';
import { SugerenciaCategoriaService } from './sugerencia-categoria.service';

@ApiTags('Conciliación Contable')
@Controller({ path: 'contabilidad/conciliacion', version: '1' })
@UseGuards(AuthGuard('jwt'), ModuleGuard, PermissionGuard)
@RequireModule('CONTABILIDAD')
@ApiBearerAuth()
export class ContabilidadConciliacionController {
  constructor(
    private readonly service: ContabilidadConciliacionService,
    private readonly recontabilizacion: RecontabilizacionService,
    private readonly salud: SaludContableService,
    private readonly sugerencias: SugerenciaCategoriaService,
  ) {}

  @Post('categorias/sugerir')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_PENDIENTES_VER')
  @ApiOperation({ summary: 'IA: sugiere la categoría de los movimientos de tesorería con categoría genérica (no modifica nada)' })
  sugerirCategorias(@GetUser() user: LoginUserInfo, @Body(ValidationPipe) body: { limite?: number } = {}) {
    return this.sugerencias.sugerir(user.empresa_id, { limite: body?.limite, usuarioId: user.id });
  }

  @Post('categorias/aplicar')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_REGENERAR')
  @ApiOperation({ summary: 'Cambia la categoría de los movimientos indicados y rehace su asiento' })
  aplicarCategorias(
    @GetUser() user: LoginUserInfo,
    @Body(ValidationPipe) body: { items: Array<{ detalle_id: string; categoria_id: string }> },
  ) {
    return this.sugerencias.aplicar(user.empresa_id, body?.items ?? [], user.id);
  }

  @Get('salud')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_PENDIENTES_VER')
  @ApiOperation({ summary: 'Controles de salud: si tesorería y contabilidad cuadran, y qué corregir (solo lectura)' })
  saludContable(@GetUser() user: LoginUserInfo) {
    return this.salud.evaluar(user.empresa_id);
  }

  @Get('registry')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_PENDIENTES_VER')
  @ApiOperation({ summary: 'Lista los orígenes contabilizables declarados' })
  registry() {
    return this.service.listarRegistry();
  }

  @Get('estado')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_PENDIENTES_VER')
  @ApiOperation({ summary: 'Estado del módulo Contabilidad para la empresa' })
  estado(@GetUser() user: LoginUserInfo) {
    return this.service.estadoModuloContabilidad(user.empresa_id);
  }

  @Get('resumen')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_PENDIENTES_VER')
  @ApiOperation({
    summary: 'Resumen de asientos pendientes agrupado por origen',
  })
  resumen(
    @GetUser() user: LoginUserInfo,
    @Query('desde') desde?: string,
    @Query('hasta') hasta?: string,
    @Query('origen_tipos') origenTipos?: string,
  ) {
    const tipos = origenTipos
      ? origenTipos.split(',').map((t) => t.trim()).filter(Boolean)
      : undefined;
    return this.service.resumen(user.empresa_id, {
      desde,
      hasta,
      origen_tipos: tipos,
    });
  }

  @Get('resumen/pdf')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_PENDIENTES_VER')
  @ApiOperation({ summary: 'PDF del reporte de conciliación (msv-kude)' })
  async resumenPDF(
    @GetUser() user: LoginUserInfo,
    @Res() res: Response,
    @Query('desde') desde?: string,
    @Query('hasta') hasta?: string,
    @Query('origen_tipos') origenTipos?: string,
  ) {
    const tipos = origenTipos
      ? origenTipos.split(',').map((t) => t.trim()).filter(Boolean)
      : undefined;
    const { pdfBuffer, filename } = await this.service.generarPDFReporte(user.empresa_id, {
      desde,
      hasta,
      origen_tipos: tipos,
    });
    res.set({
      'Content-Type': 'application/pdf',
      'Content-Disposition': `inline; filename="${filename}"`,
      'Content-Length': String(pdfBuffer.length),
    });
    res.end(pdfBuffer);
  }

  @Get('motivos')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_PENDIENTES_VER')
  @ApiOperation({ summary: 'Catálogo de motivos posibles con su resolución' })
  motivos() {
    return this.service.listarMotivos();
  }

  @Get('diagnostico/:origenTipo/:id')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_PENDIENTES_VER')
  @ApiOperation({ summary: 'Diagnóstico de un registro pendiente' })
  diagnostico(
    @GetUser() user: LoginUserInfo,
    @Param('origenTipo') origenTipo: string,
    @Param('id') id: string,
  ) {
    return this.service.diagnosticarRegistro(user.empresa_id, origenTipo, id);
  }

  @Post('regenerar-todo')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_REGENERAR')
  @ApiOperation({ summary: 'Regenera todos los orígenes activos que tengan pendientes' })
  regenerarTodo(
    @GetUser() user: LoginUserInfo,
    @Body(ValidationPipe) body: { desde?: string; hasta?: string; incluir_no_aplicables?: boolean } = {},
  ) {
    return this.service.regenerarTodos(
      user.empresa_id,
      { desde: body?.desde, hasta: body?.hasta, incluir_no_aplicables: body?.incluir_no_aplicables === true },
      user.id,
    );
  }

  @Post('re-seed-registry')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_REGENERAR')
  @ApiOperation({
    summary: 'Re-sincroniza el registry desde el seed hardcodeado (idempotente)',
  })
  async reSeedRegistry() {
    await this.service.sincronizarRegistry();
    return { status: 'ok', message: 'Registry sincronizado' };
  }

  @Post('regenerar/:origenTipo/:id')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_REGENERAR')
  @ApiOperation({ summary: 'Regenera el asiento de un único registro pendiente' })
  regenerarUno(
    @GetUser() user: LoginUserInfo,
    @Param('origenTipo') origenTipo: string,
    @Param('id') id: string,
  ) {
    return this.service.regenerarUno(user.empresa_id, origenTipo, id, user.id);
  }

  @Post('regenerar/:origenTipo')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_REGENERAR')
  @ApiOperation({ summary: 'Regenera en lote los pendientes de un origen (máx 500)' })
  regenerarLote(
    @GetUser() user: LoginUserInfo,
    @Param('origenTipo') origenTipo: string,
    @Body(ValidationPipe) body: { ids?: string[]; desde?: string; hasta?: string; incluir_no_aplicables?: boolean } = {},
  ) {
    return this.service.regenerarLote(
      user.empresa_id,
      {
        origen_tipo: origenTipo,
        ids: body?.ids,
        desde: body?.desde,
        hasta: body?.hasta,
        incluir_no_aplicables: body?.incluir_no_aplicables === true,
      },
      user.id,
    );
  }

  @Post('recontabilizar/:origenTipo/simular')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_PENDIENTES_VER')
  @ApiOperation({
    summary: 'Compara los asientos automáticos de un origen con los que generaría hoy el mapeo (no escribe nada)',
  })
  simularRecontabilizacion(
    @GetUser() user: LoginUserInfo,
    @Param('origenTipo') origenTipo: string,
    @Body(ValidationPipe) body: { desde?: string; hasta?: string } = {},
  ) {
    return this.recontabilizacion.simular(user.empresa_id, origenTipo, { desde: body?.desde, hasta: body?.hasta });
  }

  @Post('recontabilizar/:origenTipo/aplicar')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_REGENERAR')
  @ApiOperation({
    summary: 'Reemplaza el asiento de los documentos indicados por el que genera hoy el mapeo (máx 500)',
  })
  aplicarRecontabilizacion(
    @GetUser() user: LoginUserInfo,
    @Param('origenTipo') origenTipo: string,
    @Body(ValidationPipe) body: { ids: string[]; quitar_sobrantes?: boolean },
  ) {
    return this.recontabilizacion.aplicar(user.empresa_id, origenTipo, body?.ids ?? [], user.id, {
      quitarSobrantes: body?.quitar_sobrantes === true,
    });
  }

  @Post('diagnostico-lote/:origenTipo')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_PENDIENTES_VER')
  @ApiOperation({ summary: 'Diagnóstico en lote (mismo origen)' })
  diagnosticoLote(
    @GetUser() user: LoginUserInfo,
    @Param('origenTipo') origenTipo: string,
    @Body(ValidationPipe) body: { ids: string[] },
  ) {
    return this.service.diagnosticarLote(user.empresa_id, origenTipo, body?.ids ?? []);
  }

  @Get('detalle/:origenTipo')
  @RequirePermission('CONTABILIDAD', 'CONT_CONC_PENDIENTES_VER')
  @ApiOperation({
    summary: 'Detalle paginado de registros pendientes para un origen',
  })
  detalle(
    @GetUser() user: LoginUserInfo,
    @Param('origenTipo') origenTipo: string,
    @Query('desde') desde?: string,
    @Query('hasta') hasta?: string,
    @Query('limit') limit?: string,
    @Query('solo_aplicables') soloAplicables?: string,
  ) {
    return this.service.detalle(user.empresa_id, origenTipo, {
      desde,
      hasta,
      limit: limit ? Number(limit) : undefined,
      solo_aplicables: soloAplicables === 'true',
    });
  }
}
