import { BadRequestException, Body, Controller, Delete, Get, Param, Post, Query, Res, UseGuards, UseInterceptors, UploadedFile } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { AuthGuard } from '@nestjs/passport';
import { ApiBearerAuth, ApiConsumes, ApiOperation, ApiQuery, ApiTags, ApiBody } from '@nestjs/swagger';
import type { Response } from 'express';
import * as XLSX from 'xlsx';
import { RequireModule } from 'src/auth/decorators/require-module.decorator';
import { RequirePermission } from 'src/auth/decorators/require-permission.decorator';
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 { CrearAliasTipoGastoDto, GuardarMapeoDto } from './dto/gastos-importador.dto';
import { GastosImportadorService } from './gastos-importador.service';

@ApiTags('Gastos - Importación desde Excel')
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'), ModuleGuard, PermissionGuard)
@RequireModule('TESORERIA')
@Controller({ path: 'gastos/import', version: '1' })
export class GastosImportadorController {
  constructor(private readonly service: GastosImportadorService) {}

  @Post('subir')
  @RequirePermission('COMPRAS', 'TES_GAS_GASTO_IMPORTAR')
  @UseInterceptors(FileInterceptor('archivo', { limits: { fileSize: 10 * 1024 * 1024 } }))
  @ApiConsumes('multipart/form-data')
  @ApiBody({ schema: { type: 'object', properties: { archivo: { type: 'string', format: 'binary' } } } })
  @ApiOperation({ summary: 'Sube un Libro de Compras SET (.xlsx) y crea el job de importación en preview' })
  subir(@UploadedFile() file: Express.Multer.File, @GetUser() user: LoginUserInfo) {
    if (!file) throw new BadRequestException('Falta el archivo (campo "archivo")');
    return this.service.subirArchivo(user.empresa_id, user.id, { originalname: file.originalname, buffer: file.buffer });
  }

  @Post(':jobId/mapeo')
  @RequirePermission('COMPRAS', 'TES_GAS_GASTO_IMPORTAR')
  @ApiOperation({ summary: 'Guarda el mapeo de tipos de gasto y marca el job como mapeado' })
  guardarMapeo(@Param('jobId') jobId: string, @Body() dto: GuardarMapeoDto, @GetUser() user: LoginUserInfo) {
    return this.service.guardarMapeo(user.empresa_id, jobId, user.id, dto);
  }

  @Post(':jobId/confirmar')
  @RequirePermission('COMPRAS', 'TES_GAS_GASTO_IMPORTAR')
  @ApiOperation({ summary: 'Encola el procesamiento asíncrono de las filas válidas del job' })
  confirmar(@Param('jobId') jobId: string, @GetUser() user: LoginUserInfo) {
    return this.service.confirmar(user.empresa_id, jobId);
  }

  @Get('jobs')
  @RequirePermission('COMPRAS', 'TES_GAS_GASTO_VER')
  @ApiOperation({ summary: 'Historial de importaciones de gastos de la empresa' })
  listarJobs(@GetUser() user: LoginUserInfo) {
    return this.service.listarJobs(user.empresa_id);
  }

  // Nota: esta ruta estática debe ir ANTES de @Get(':jobId') — si no, Express
  // intentaría resolver "plantilla" como un jobId.
  @Get('plantilla')
  @RequirePermission('COMPRAS', 'TES_GAS_GASTO_IMPORTAR')
  @ApiOperation({
    summary: 'Descargar plantilla Excel de ejemplo (Libro de Compras SET)',
    description: 'Mismas 19 columnas que reconoce el importador, con filas de ejemplo (contado, crédito, tramos de IVA mixtos).',
  })
  descargarPlantilla(@Res() res: Response) {
    const headers = [
      'RUC / Nº de Identificacion del Informado',
      'Tipo de Identificación del Informado',
      'Nombre o Razón Social del Informado',
      'Tipo de Registro',
      'Tipo de Comprobante',
      'Fecha de Emisión',
      'Periodo de Emisión',
      'Condicion de la Operacion',
      'Timbrado del Comprobante',
      'Numero de Comprobante',
      'CDC',
      'Monto Gravado 10%',
      'IVA 10%',
      'Monto Gravado 5%',
      'IVA 5%',
      'Monto No Gravado / Exento ',
      'Total Comprobante',
      'TIPO CUENTA CONTABLE- GASTO',
      'TIPO CUENTA CONTABLE- CONTRAPARTIDA PASIVA',
    ];
    const filasEjemplo = [
      // Contado, 10% simple
      ['80011111', 'RUC', 'ESTACION DE SERVICIO EJEMPLO SA', 'COMPRAS', 'FACTURA', '05/05/2026', '05/2026', 'CONTADO', '12345678', '001-001-0000123', '', 220000, 20000, 0, 0, 0, 220000, 'COMBUSTIBLE Y LUBRICANTES', 'ACREEDORES VARIOS'],
      // Crédito, 10% simple
      ['4567890', 'RUC', 'JUAN PEREZ GONZALEZ', 'COMPRAS', 'FACTURA', '10/05/2026', '05/2026', 'CREDITO', '87654321', '001-001-0000045', '', 1650000, 150000, 0, 0, 0, 1650000, 'HONORARIOS PROFESIONALES', 'ACREEDORES VARIOS'],
      // Contado, tramos de IVA mixtos (10% + 5% + exento en la misma factura)
      ['80022222', 'RUC', 'COMERCIAL EJEMPLO SRL', 'COMPRAS', 'FACTURA', '15/05/2026', '05/2026', 'CONTADO', '11223344', '002-001-0000789', '', 110000, 10000, 52500, 2500, 30000, 192500, 'GASTOS GENERALES', 'ACREEDORES VARIOS'],
      // Contado, exento
      ['3456789', 'RUC', 'MARIA LOPEZ BENITEZ', 'COMPRAS', 'FACTURA', '20/05/2026', '05/2026', 'CONTADO', '99887766', '001-002-0000234', '', 0, 0, 0, 0, 180000, 180000, 'GASTOS PARA VIATICOS', 'ACREEDORES VARIOS'],
    ];
    const wb = XLSX.utils.book_new();
    const ws = XLSX.utils.aoa_to_sheet([headers, ...filasEjemplo]);
    XLSX.utils.book_append_sheet(wb, ws, 'Libro Compras');
    const buffer = XLSX.write(wb, { type: 'buffer', bookType: 'xlsx' });
    res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    res.setHeader('Content-Disposition', 'attachment; filename="plantilla_importacion_gastos.xlsx"');
    res.send(buffer);
  }

  @Get(':jobId')
  @RequirePermission('COMPRAS', 'TES_GAS_GASTO_VER')
  @ApiOperation({ summary: 'Detalle y estado del job (usado para polling durante el procesamiento)' })
  getJob(@Param('jobId') jobId: string, @GetUser() user: LoginUserInfo) {
    return this.service.getJob(user.empresa_id, jobId);
  }

  @Get(':jobId/filas')
  @RequirePermission('COMPRAS', 'TES_GAS_GASTO_VER')
  @ApiQuery({ name: 'estado', required: false })
  @ApiOperation({ summary: 'Lista las filas del job, opcionalmente filtradas por estado' })
  listarFilas(@Param('jobId') jobId: string, @GetUser() user: LoginUserInfo, @Query('estado') estado?: string) {
    return this.service.listarFilas(user.empresa_id, jobId, estado);
  }

  @Get('alias')
  @RequirePermission('COMPRAS', 'TES_GAS_GASTO_IMPORTAR')
  @ApiOperation({ summary: 'Lista los alias de tipo de gasto aprendidos para la empresa' })
  listarAlias(@GetUser() user: LoginUserInfo) {
    return this.service.listarAlias(user.empresa_id);
  }

  @Post('alias')
  @RequirePermission('COMPRAS', 'TES_GAS_GASTO_IMPORTAR')
  @ApiOperation({ summary: 'Crea/actualiza manualmente un alias de tipo de gasto (fuera del wizard de mapeo)' })
  crearAlias(@Body() dto: CrearAliasTipoGastoDto, @GetUser() user: LoginUserInfo) {
    return this.service.crearAlias(user.empresa_id, user.id, dto);
  }

  @Delete('alias/:id')
  @RequirePermission('COMPRAS', 'TES_GAS_GASTO_IMPORTAR')
  @ApiOperation({ summary: 'Elimina un alias de tipo de gasto aprendido' })
  eliminarAlias(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.service.eliminarAlias(user.empresa_id, id);
  }
}
