import { BadRequestException, Body, Controller, Param, ParseUUIDPipe, Post, Query, UseGuards, ValidationPipe } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiBearerAuth, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger';
import { RequireModule } from '../auth/decorators/require-module.decorator';
import { RequirePermission } from '../auth/decorators/require-permission.decorator';
import { GetUser } from '../auth/get-user.decorator';
import { ModuleGuard } from '../auth/guards/module.guard';
import { PermissionGuard } from '../auth/guards/permission.guard';
import type { TipoDocumento, TipoEvento } from './middleware-sifen.service';
import { MiddlewareSifenService } from './middleware-sifen.service';
import { EnviarEventoDto } from './dto/enviar-evento.dto';
import { LiberarEventoDto } from './dto/liberar-evento.dto';

const TIPOS_CON_EVENTO: TipoDocumento[] = ['factura', 'nota_credito', 'nota_remision'];

function validarTipoConEvento(tipoDocumento: string): TipoDocumento {
  if (!TIPOS_CON_EVENTO.includes(tipoDocumento as TipoDocumento)) {
    throw new BadRequestException(`Tipo de documento sin eventos SIFEN: ${tipoDocumento}`);
  }
  return tipoDocumento as TipoDocumento;
}

@ApiTags('Middleware SIFEN')
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'), ModuleGuard, PermissionGuard)
@RequireModule('FISCAL')
@Controller('middleware-sifen')
export class MiddlewareSifenController {
  constructor(private readonly middlewareSifenService: MiddlewareSifenService) {}

  @Post('sincronizar-estado/:tipoDocumento')
  @RequirePermission('FISCAL', 'FIS_SIF_SIFEN_PROCESAR')
  @ApiOperation({ summary: 'Sincronizar estado de documentos enviados a SIFEN' })
  @ApiParam({ name: 'tipoDocumento', enum: ['factura', 'nota_credito', 'nota_remision', 'nota_debito'] })
  @ApiQuery({ name: 'nro_lote', required: false, description: 'Numero de lote especifico a consultar' })
  async sincronizarEstado(
    @GetUser() user: any,
    @Param('tipoDocumento') tipoDocumento: TipoDocumento,
    @Query('nro_lote') nroLote?: string,
  ) {
    // "Sincronizar SIFEN" de la pantalla sincroniza documentos Y eventos. Antes
    // pasaba 'documento' fijo: un evento trabado no se podía destrabar desde la
    // interfaz por más que el usuario apretara el botón, sólo lo tomaba el job.
    const documentos = await this.middlewareSifenService.sincronizarSifen(
      user.empresa_id as string,
      tipoDocumento,
      'documento',
      nroLote,
    );
    // Las notas de débito no tienen columnas de evento: pedirles el modo evento
    // explota en el service.
    if (tipoDocumento === 'nota_debito') return documentos;

    const eventos = await this.middlewareSifenService.sincronizarSifen(
      user.empresa_id as string,
      tipoDocumento,
      'evento',
    );
    return {
      actualizados: documentos.actualizados + eventos.actualizados,
      detalles: [...documentos.detalles, ...eventos.detalles],
    };
  }

  // Acciones de las alertas de eventos trabados (docs/plan-alertas-empresa.md).
  // Declaradas antes de `evento/:tipoEvento/:tipoDocumento` por claridad de ruteo.
  @Post('evento/reconsultar/:tipoDocumento/:id')
  @RequirePermission('FISCAL', 'FIS_SIF_SIFEN_VER')
  @ApiOperation({ summary: 'Reconsultar en el middleware el evento pendiente de un documento' })
  @ApiParam({ name: 'tipoDocumento', enum: ['factura', 'nota_credito', 'nota_remision'] })
  reconsultarEvento(
    @GetUser() user: any,
    @Param('tipoDocumento') tipoDocumento: string,
    @Param('id', ParseUUIDPipe) id: string,
  ) {
    return this.middlewareSifenService.reconsultarEvento(user.empresa_id as string, validarTipoConEvento(tipoDocumento), id);
  }

  @Post('evento/reenviar/:tipoDocumento/:id')
  @RequirePermission('FISCAL', 'FIS_SIF_EVENTO_LIBERAR')
  @ApiOperation({ summary: 'Liberar (si SIFEN no lo tiene) y reenviar el evento trabado de un documento' })
  @ApiParam({ name: 'tipoDocumento', enum: ['factura', 'nota_credito', 'nota_remision'] })
  reenviarEvento(
    @GetUser() user: any,
    @Param('tipoDocumento') tipoDocumento: string,
    @Param('id', ParseUUIDPipe) id: string,
  ) {
    return this.middlewareSifenService.reenviarEvento(user.empresa_id as string, validarTipoConEvento(tipoDocumento), id, user.id as string);
  }

  @Post('evento/reenviar-inutilizaciones')
  @RequirePermission('FISCAL', 'FIS_SIF_EVENTO_LIBERAR')
  @ApiOperation({ summary: 'Reenviar las inutilizaciones (EINU) trabadas por haberse enviado sin timbrado' })
  reenviarInutilizaciones(@GetUser() user: any) {
    return this.middlewareSifenService.reenviarInutilizacionesPendientes(user.empresa_id as string, user.id as string);
  }

  @Post('evento/liberar/:tipoDocumento/:id')
  @RequirePermission('FISCAL', 'FIS_SIF_EVENTO_LIBERAR')
  @ApiOperation({ summary: 'Liberar un evento SIFEN trabado (SIFEN no lo tiene o es incoherente)' })
  @ApiParam({ name: 'tipoDocumento', enum: ['factura', 'nota_credito', 'nota_remision'] })
  liberarEvento(
    @GetUser() user: any,
    @Param('tipoDocumento') tipoDocumento: string,
    @Param('id', ParseUUIDPipe) id: string,
    @Body(ValidationPipe) dto: LiberarEventoDto,
  ) {
    return this.middlewareSifenService.liberarEvento(
      user.empresa_id as string,
      validarTipoConEvento(tipoDocumento),
      id,
      dto.motivo,
      user.id as string,
    );
  }

  @Post('evento/:tipoEvento/:tipoDocumento')
  @RequirePermission('FISCAL', 'FIS_SIF_SIFEN_PROCESAR')
  @ApiOperation({ summary: 'Enviar evento SIFEN (inutilizacion, cancelacion, nominacion)' })
  @ApiParam({ name: 'tipoEvento', enum: ['inutilizacion', 'cancelacion', 'nominacion'] })
  @ApiParam({ name: 'tipoDocumento', enum: ['factura', 'nota_credito', 'nota_remision', 'nota_debito'] })
  async enviarEvento(
    @GetUser() user: any,
    @Param('tipoEvento') tipoEvento: TipoEvento,
    @Param('tipoDocumento') tipoDocumento: TipoDocumento,
    @Body() body: EnviarEventoDto,
  ) {
    return this.middlewareSifenService.enviarEvento(
      user.empresa_id as string,
      tipoDocumento,
      tipoEvento,
      body.documentoId,
      body.motivo,
      body.datosCliente,
    );
  }
}
