import { Body, Controller, Delete, Get, Param, Post, Put, Query, Res, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import type { Response } from 'express';
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 { ApproveDto, MarkAcceptedDto, RejectApprovalDto } from './dto/aprobacion.dto';
import { CreatePresupuestoDto } from './dto/create-presupuesto.dto';
import { FacturarDirectoDto } from './dto/facturar-directo.dto';
import { PresupuestoItemDto } from './dto/presupuesto-item.dto';
import { QueryPresupuestosDto } from './dto/query-presupuestos.dto';
import { ReorderItemsDto } from './dto/reorder-items.dto';
import { SendPresupuestoEmailDto } from './dto/send-email.dto';
import { UpdatePresupuestoConfigDto } from './dto/update-config.dto';
import { UpdatePresupuestoDto } from './dto/update-presupuesto.dto';
import { PresupuestosAprobacionService } from './presupuestos-aprobacion.service';
import { PresupuestosConversionService } from './presupuestos-conversion.service';
import { PresupuestosDashboardService } from './presupuestos-dashboard.service';
import { PresupuestosPdfService } from './presupuestos-pdf.service';
import { PresupuestosPublicoService } from './presupuestos-publico.service';
import { PresupuestosService } from './presupuestos.service';

// Acceso por módulo/privilegio (M43/Fase 7): el `PermissionGuard` valida que la
// empresa tenga el módulo `PRESUPUESTOS` activo en su suscripción y que el perfil
// del usuario tenga el privilegio. Provisionar con `pnpm provision:presupuestos`.
// El portal público (`pub/presupuesto/:token`) NO pasa por estos guards.
@ApiTags('Presupuestos')
@Controller({ path: 'presupuestos', version: '1' })
@UseGuards(AuthGuard('jwt'), ModuleGuard, PermissionGuard)
@RequireModule('VENTAS')
@ApiBearerAuth()
export class PresupuestosController {
  constructor(
    private readonly presupuestosService: PresupuestosService,
    private readonly presupuestosPdfService: PresupuestosPdfService,
    private readonly presupuestosPublicoService: PresupuestosPublicoService,
    private readonly aprobacionService: PresupuestosAprobacionService,
    private readonly conversionService: PresupuestosConversionService,
    private readonly dashboardService: PresupuestosDashboardService,
  ) {}

  // ── Configuración (M36) ─────────────────────────────────────────────────────

  @Get('config')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_VER')
  @ApiOperation({ summary: 'Obtener parámetros globales del módulo' })
  getConfig(@GetUser() user: LoginUserInfo) {
    return this.presupuestosService.getConfig(user.empresa_id);
  }

  @Put('config')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_EDITAR')
  @ApiOperation({ summary: 'Actualizar parámetros globales del módulo' })
  updateConfig(@Body() dto: UpdatePresupuestoConfigDto, @GetUser() user: LoginUserInfo) {
    return this.presupuestosService.updateConfig(user.empresa_id, dto);
  }

  // ── CRUD ──────────────────────────────────────────────────────────────────

  @Post()
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_CREAR')
  @ApiOperation({ summary: 'Crear presupuesto (estado DRAFT)' })
  create(@Body() dto: CreatePresupuestoDto, @GetUser() user: LoginUserInfo) {
    return this.presupuestosService.create(dto, user.empresa_id, user.id, user.username);
  }

  @Get()
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_VER')
  @ApiOperation({ summary: 'Listar presupuestos con filtros y paginación' })
  async findAll(@Query() query: QueryPresupuestosDto, @GetUser() user: LoginUserInfo) {
    const { items, total, page, limit, lastPage, stats, montosPorMoneda } = await this.presupuestosService.findAll(query, user.empresa_id, user.id);
    return { message: 'Presupuestos obtenidos exitosamente', data: items, total, page, limit, lastPage, stats, montosPorMoneda };
  }

  @Get('aceptados')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_VER')
  @ApiOperation({ summary: 'Presupuestos ACEPTADOS no convertidos (para Orden de Venta) — M42' })
  aceptados(@GetUser() user: LoginUserInfo, @Query('cliente_id') cliente_id?: string) {
    return this.conversionService.getAceptados(user.empresa_id, cliente_id);
  }

  @Get('dashboard')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_VER')
  @ApiOperation({ summary: 'Pipeline comercial: resumen, embudo, por estado y serie mensual — M43' })
  dashboard(@GetUser() user: LoginUserInfo, @Query('desde') desde?: string, @Query('hasta') hasta?: string) {
    return this.dashboardService.getDashboard(user.empresa_id, desde, hasta, user.id);
  }

  @Get(':id')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_VER')
  @ApiOperation({ summary: 'Obtener presupuesto completo' })
  async findOne(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    const data = await this.presupuestosService.findOne(id, user.empresa_id);
    return { message: 'Presupuesto obtenido exitosamente', data };
  }

  @Get(':id/pdf')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_IMPRIMIR')
  @ApiOperation({ summary: 'Generar y descargar el PDF del presupuesto' })
  async pdf(
    @Param('id') id: string,
    @GetUser() user: LoginUserInfo,
    @Res() res: Response,
    @Query('download') download?: string,
  ) {
    const buffer = await this.presupuestosPdfService.generatePdf(id, user.empresa_id, 'view');
    const disposition = download === '1' || download === 'true' ? 'attachment' : 'inline';
    res.set({
      'Content-Type': 'application/pdf',
      'Content-Disposition': `${disposition}; filename="presupuesto-${id}.pdf"`,
      'Content-Length': String(buffer.length),
    });
    res.send(buffer);
  }

  // ── Workflow de aprobación interna (M41) ──────────────────────────────────────

  @Post(':id/submit-review')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_EDITAR')
  @ApiOperation({ summary: 'Enviar a aprobación interna (auto-aprueba si el monto está bajo el umbral)' })
  submitReview(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.aprobacionService.submitReview(id, user.empresa_id, user.id);
  }

  @Post(':id/approve')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_APROBAR')
  @ApiOperation({ summary: 'Aprobar un presupuesto en revisión' })
  approve(@Param('id') id: string, @Body() dto: ApproveDto, @GetUser() user: LoginUserInfo) {
    return this.aprobacionService.approve(id, user.empresa_id, user.id, dto.comentario);
  }

  @Post(':id/reject-approval')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_APROBAR')
  @ApiOperation({ summary: 'Rechazar la aprobación (el presupuesto vuelve a Borrador)' })
  rejectApproval(@Param('id') id: string, @Body() dto: RejectApprovalDto, @GetUser() user: LoginUserInfo) {
    return this.aprobacionService.rejectApproval(id, user.empresa_id, user.id, dto.motivo);
  }

  @Post(':id/mark-accepted')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_APROBAR')
  @ApiOperation({ summary: 'Registrar la aceptación del cliente recibida fuera del portal (teléfono, visita, WhatsApp)' })
  markAccepted(@Param('id') id: string, @Body() dto: MarkAcceptedDto, @GetUser() user: LoginUserInfo) {
    return this.aprobacionService.markAccepted(id, user.empresa_id, user.id, dto);
  }

  // ── Conversión a ventas (M42) ─────────────────────────────────────────────────

  @Post(':id/convert-to-order')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_FACTURAR')
  @ApiOperation({ summary: 'Convertir un presupuesto ACEPTADO en Orden de Venta confirmada' })
  convertToOrder(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.conversionService.convertToOrder(id, user.empresa_id, user.id);
  }

  @Post(':id/facturar-directo')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_FACTURAR')
  @ApiOperation({ summary: 'Enlazar una factura ya emitida y cerrar el presupuesto, sin Orden de Venta' })
  facturarDirecto(@Param('id') id: string, @Body() dto: FacturarDirectoDto, @GetUser() user: LoginUserInfo) {
    return this.conversionService.facturarDirecto(id, user.empresa_id, user.id, dto.factura_id);
  }

  // ── Envío por email (M40) ─────────────────────────────────────────────────────

  @Post(':id/send-email')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_EDITAR')
  @ApiOperation({ summary: 'Enviar el presupuesto por email al cliente (PDF adjunto + portal)' })
  sendEmail(
    @Param('id') id: string,
    @Body() dto: SendPresupuestoEmailDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.presupuestosPublicoService.sendEmail(id, dto, user.empresa_id, user.id);
  }

  @Post(':id/generate-link')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_EDITAR')
  @ApiOperation({ summary: 'Generar enlace público para compartir manualmente (WhatsApp, chat, etc.)' })
  generateLink(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.presupuestosPublicoService.generateLink(id, user.empresa_id, user.id);
  }

  @Put(':id')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_EDITAR')
  @ApiOperation({ summary: 'Editar presupuesto (sólo en DRAFT)' })
  update(@Param('id') id: string, @Body() dto: UpdatePresupuestoDto, @GetUser() user: LoginUserInfo) {
    return this.presupuestosService.update(id, dto, user.empresa_id);
  }

  @Delete(':id')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_ELIMINAR')
  @ApiOperation({ summary: 'Cancelar presupuesto (soft delete → CANCELLED)' })
  remove(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.presupuestosService.remove(id, user.empresa_id, user.id);
  }

  // ── Ítems ───────────────────────────────────────────────────────────────────

  @Post(':id/items')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_EDITAR')
  @ApiOperation({ summary: 'Agregar ítem al presupuesto' })
  addItem(@Param('id') id: string, @Body() item: PresupuestoItemDto, @GetUser() user: LoginUserInfo) {
    return this.presupuestosService.addItem(id, item, user.empresa_id);
  }

  @Put(':id/items/:itemId')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_EDITAR')
  @ApiOperation({ summary: 'Editar ítem (recalcula totales)' })
  updateItem(
    @Param('id') id: string,
    @Param('itemId') itemId: string,
    @Body() item: PresupuestoItemDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.presupuestosService.updateItem(id, itemId, item, user.empresa_id);
  }

  @Delete(':id/items/:itemId')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_EDITAR')
  @ApiOperation({ summary: 'Eliminar ítem del presupuesto' })
  deleteItem(@Param('id') id: string, @Param('itemId') itemId: string, @GetUser() user: LoginUserInfo) {
    return this.presupuestosService.deleteItem(id, itemId, user.empresa_id);
  }

  @Post(':id/reorder-items')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_EDITAR')
  @ApiOperation({ summary: 'Reordenar ítems' })
  reorderItems(@Param('id') id: string, @Body() dto: ReorderItemsDto, @GetUser() user: LoginUserInfo) {
    return this.presupuestosService.reorderItems(id, dto.item_ids, user.empresa_id);
  }

  // ── Versionado ──────────────────────────────────────────────────────────────

  @Post(':id/new-version')
  @RequirePermission('VENTAS', 'VEN_PRE_PRESUPUESTO_CREAR')
  @ApiOperation({ summary: 'Crear nueva versión del presupuesto' })
  newVersion(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.presupuestosService.newVersion(id, user.empresa_id, user.id, user.username);
  }
}
