import {
  Body,
  Controller,
  Get,
  HttpException,
  HttpStatus,
  Param,
  Patch,
  Post,
  Query,
  Res,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { FileInterceptor } from '@nestjs/platform-express';
import {
  ApiBearerAuth,
  ApiBody,
  ApiConsumes,
  ApiCreatedResponse,
  ApiParam,
  ApiQuery,
} from '@nestjs/swagger';
import type { Response } from 'express';
import type { LoginUserInfo } from 'src/auth/dto/login.dto';
import { GetUser } from 'src/auth/get-user.decorator';
import { CreateComprobantePagoRecibidoDto } from './dto/create-comprobante-pago-recibido.dto';
import {
  body_comprobante_pago_recibido,
  response_comprobante_pago_recibido,
} from './dto/doc-swagger';
import { SuscripcionesService } from './suscripciones.service';

@Controller({ path: 'suscripciones', version: '1' })
export class SuscripcionesController {
  constructor(private readonly suscripcionesService: SuscripcionesService) {}

  // ============================================================
  // Endpoints del Subsidiario
  // ============================================================

  /**
   * Vista previa del primer vencimiento, para mostrarlo en el alta de empresa
   * antes de guardar.
   *
   * Vive en el backend a propósito: la regla del primer ciclo (el piso de días
   * y la alineación al día de cobro) ya se calcula acá, y duplicarla en el
   * front haría que el formulario prometa una fecha distinta a la que después
   * se guarda apenas alguien cambie el piso.
   */
  @Get('preview-primer-cobro')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiQuery({ name: 'fecha_inicio', required: true, example: '2026-09-07' })
  @ApiQuery({ name: 'dia_cobro', required: false, example: 5 })
  previewPrimerCobro(
    @Query('fecha_inicio') fechaInicio: string,
    @Query('dia_cobro') diaCobro?: string,
  ) {
    return this.suscripcionesService.previewPrimerCobro(fechaInicio, diaCobro);
  }

  /**
   * Obtener la suscripción actual de la empresa del usuario.
   */
  @Get('mi-suscripcion')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  async getMiSuscripcion(@GetUser() user: LoginUserInfo) {
    return this.suscripcionesService.getMiSuscripcion(user.empresa_id);
  }

  /**
   * Listado plano de submódulos activos de la empresa actual,
   * combinando los incluidos por módulo raíz y los addons contratados.
   */
  @Get('mis-submodulos-activos')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  async getMisSubmodulosActivos(@GetUser() user: LoginUserInfo) {
    return this.suscripcionesService.getSubmodulosActivos(user.empresa_id);
  }

  /**
   * Obtener historial de pagos de la empresa del usuario.
   */
  @Get('mis-pagos')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiQuery({ name: 'limit', required: false, type: Number })
  async getHistorialPagos(
    @GetUser() user: LoginUserInfo,
    @Query('limit') limit?: string,
  ) {
    const lim = limit ? parseInt(limit, 10) : 6;
    return this.suscripcionesService.getHistorialPagos(user.empresa_id, lim);
  }

  /**
   * Subir comprobante de pago para una suscripción.
   */
  @Post(':suscripcionId/comprobantes')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiConsumes('multipart/form-data')
  @ApiParam({
    name: 'suscripcionId',
    type: 'string',
    required: true,
    description: 'ID de la suscripción',
  })
  @ApiBody(body_comprobante_pago_recibido)
  @ApiCreatedResponse(response_comprobante_pago_recibido)
  @UseInterceptors(FileInterceptor('file'))
  async createComprobante(
    @Param('suscripcionId') suscripcionId: string,
    @UploadedFile() file: Express.Multer.File,
    @Body() dto: CreateComprobantePagoRecibidoDto,
    @GetUser() user: LoginUserInfo,
  ) {
    try {
      if (!file?.buffer)
        throw new HttpException(
          { success: false, message: 'Archivo inválido' },
          HttpStatus.BAD_REQUEST,
        );

      const allowedMimeTypes = new Set([
        'image/png',
        'image/jpeg',
        'application/pdf',
      ]);
      if (!file.mimetype || !allowedMimeTypes.has(file.mimetype)) {
        throw new HttpException(
          {
            success: false,
            message: 'Tipo de archivo no permitido. Solo PNG, JPG/JPEG y PDF.',
          },
          HttpStatus.BAD_REQUEST,
        );
      }

      const maxBytes = 10 * 1024 * 1024;
      if (file.size > maxBytes) {
        throw new HttpException(
          {
            success: false,
            message: 'El archivo excede el tamaño máximo permitido (10MB)',
          },
          HttpStatus.BAD_REQUEST,
        );
      }

      await this.suscripcionesService.createComprobantePagoRecibido(
        suscripcionId,
        user.empresa_id,
        file.buffer,
        file.mimetype,
        dto.observacion,
        dto.monto ? parseFloat(dto.monto) : undefined,
      );

      return {
        success: true,
        message: 'Comprobante registrado exitosamente',
        data: [],
      };
    } catch (error) {
      const message =
        error instanceof Error
          ? error.message
          : 'Error al registrar comprobante';
      const status = message.includes('no encontrada')
        ? HttpStatus.NOT_FOUND
        : HttpStatus.BAD_REQUEST;
      throw new HttpException(
        { success: false, message, error: 'Error' },
        status,
      );
    }
  }

  /**
   * Descargar/ver el documento de un comprobante de pago.
   */
  @Get('comprobantes/:id/documento')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiParam({ name: 'id', type: 'string', description: 'ID del comprobante' })
  async getDocumentoComprobante(
    @Param('id') id: string,
    @GetUser() user: LoginUserInfo,
    @Res() res: Response,
  ) {
    const { buffer, mimeType } =
      await this.suscripcionesService.getDocumentoComprobante(
        id,
        user.empresa_id,
      );
    res.set({
      'Content-Type': mimeType,
      'Content-Disposition': `inline; filename="comprobante-${id}"`,
      'Content-Length': buffer.length.toString(),
    });
    res.end(buffer);
  }

  // ============================================================
  // Endpoints del Holding
  // ============================================================

  /**
   * Listar comprobantes de pago de subsidiarios (para holding).
   */
  @Get('holding/comprobantes')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiQuery({ name: 'estado', required: false, type: String })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  @ApiQuery({ name: 'page', required: false, type: Number })
  @ApiQuery({ name: 'busqueda', required: false, type: String })
  async getComprobantesPendientes(
    @GetUser() user: LoginUserInfo,
    @Query('estado') estado?: string,
    @Query('limit') limit?: string,
    @Query('page') page?: string,
    @Query('busqueda') busqueda?: string,
  ) {
    const lim = limit ? parseInt(limit, 10) : undefined;
    const pg = page ? parseInt(page, 10) : undefined;
    return this.suscripcionesService.getComprobantesPendientesHolding(
      user.empresa_id,
      { estado, limit: lim, page: pg, busqueda },
    );
  }

  /**
   * Ver documento de comprobante (acceso holding).
   */
  @Get('holding/comprobantes/:id/documento')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiParam({ name: 'id', type: 'string', description: 'ID del comprobante' })
  async getDocumentoComprobanteHolding(
    @Param('id') id: string,
    @GetUser() user: LoginUserInfo,
    @Res() res: Response,
  ) {
    const { buffer, mimeType } =
      await this.suscripcionesService.getDocumentoComprobante(
        id,
        user.empresa_id,
        true,
      );
    res.set({
      'Content-Type': mimeType,
      'Content-Disposition': `inline; filename="comprobante-${id}"`,
      'Content-Length': buffer.length.toString(),
    });
    res.end(buffer);
  }

  /**
   * Aprobar un comprobante de pago (holding).
   */
  @Patch('comprobantes/:id/validar')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiParam({ name: 'id', type: 'string', description: 'ID del comprobante' })
  async validarComprobante(
    @Param('id') id: string,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.suscripcionesService.validarComprobante(
      id,
      user.id,
      user.empresa_id,
    );
  }

  /**
   * Rechazar un comprobante de pago (holding).
   */
  @Patch('comprobantes/:id/rechazar')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiParam({ name: 'id', type: 'string', description: 'ID del comprobante' })
  async rechazarComprobante(
    @Param('id') id: string,
    @Body() body: { motivo: string },
    @GetUser() user: LoginUserInfo,
  ) {
    if (!body.motivo || body.motivo.trim().length === 0) {
      throw new HttpException(
        { success: false, message: 'El motivo de rechazo es requerido' },
        HttpStatus.BAD_REQUEST,
      );
    }
    return this.suscripcionesService.rechazarComprobante(
      id,
      user.id,
      user.empresa_id,
      body.motivo.trim(),
    );
  }

  /**
   * Listar addons disponibles del plan + estado actual en la suscripción (holding).
   */
  @Get('holding/:suscripcionId/addons')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiParam({ name: 'suscripcionId', type: 'string' })
  async getAddonsSuscripcionHolding(
    @Param('suscripcionId') suscripcionId: string,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.suscripcionesService.getAddonsSuscripcionHolding(
      suscripcionId,
      user.empresa_id,
    );
  }

  /**
   * Activar/desactivar addons + override de precio para una suscripción (holding).
   * Body: { addons: [{ submodulo_id, active, precio? }] }
   */
  @Patch('holding/:suscripcionId/addons')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiParam({ name: 'suscripcionId', type: 'string' })
  async updateAddonsSuscripcionHolding(
    @Param('suscripcionId') suscripcionId: string,
    @Body()
    body: {
      addons: Array<{ submodulo_id: string; active: boolean; precio?: number | null }>;
    },
    @GetUser() user: LoginUserInfo,
  ) {
    return this.suscripcionesService.updateAddonsSuscripcionHolding(
      suscripcionId,
      user.empresa_id,
      body,
      user.id,
    );
  }

  /**
   * Listar submódulos no-addon de los módulos incluidos en la suscripción
   * con su estado de inclusión/exclusión por empresa (holding).
   */
  @Get('holding/:suscripcionId/submodulos-incluidos')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiParam({ name: 'suscripcionId', type: 'string' })
  async getSubmodulosIncluidosSuscripcionHolding(
    @Param('suscripcionId') suscripcionId: string,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.suscripcionesService.getSubmodulosIncluidosSuscripcionHolding(
      suscripcionId,
      user.empresa_id,
    );
  }

  /**
   * Incluir/excluir submódulos no-addon de una suscripción (holding).
   * Body: { items: [{ submodulo_id, active }] }
   */
  @Patch('holding/:suscripcionId/submodulos-incluidos')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiParam({ name: 'suscripcionId', type: 'string' })
  async updateSubmodulosIncluidosSuscripcionHolding(
    @Param('suscripcionId') suscripcionId: string,
    @Body()
    body: { items: Array<{ submodulo_id: string; active: boolean }> },
    @GetUser() user: LoginUserInfo,
  ) {
    return this.suscripcionesService.updateSubmodulosIncluidosSuscripcionHolding(
      suscripcionId,
      user.empresa_id,
      body,
      user.id,
    );
  }

  /**
   * Registrar pago manual para un subsidiario (holding).
   */
  @Post('holding/pago-manual')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  async registrarPagoManual(
    @Body()
    body: { suscripcion_id: string; monto?: number; observacion?: string },
    @GetUser() user: LoginUserInfo,
  ) {
    if (!body.suscripcion_id) {
      throw new HttpException(
        { success: false, message: 'suscripcion_id es requerido' },
        HttpStatus.BAD_REQUEST,
      );
    }
    return this.suscripcionesService.registrarPagoManual(
      user.empresa_id,
      body.suscripcion_id,
      user.id,
      { monto: body.monto, observacion: body.observacion },
    );
  }

  // ============================================================
  // Cron manual
  // ============================================================

  /**
   * Ejecutar verificación de vencimientos manualmente.
   */
  @Post('cron/verificar-vencimientos')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  async verificarVencimientosManual() {
    return this.suscripcionesService.verificarVencimientos();
  }
}
