import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  ParseUUIDPipe,
  Patch,
  Post,
  Put,
  Query,
  UseGuards,
  ValidationPipe,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiBearerAuth, ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
import { inm_certificado_estado, inm_imputacion_origen } from '@prisma/client';
import { RequirePermission } from 'src/auth/decorators/require-permission.decorator';
import { RequireSubmodulo } from 'src/auth/decorators/require-submodulo.decorator';
import type { LoginUserInfo } from 'src/auth/dto/login.dto';
import { GetUser } from 'src/auth/get-user.decorator';
import { PermissionGuard } from 'src/auth/guards/permission.guard';
import { SubmoduloGuard } from 'src/auth/guards/submodulo.guard';
import { CertificadosObraInmService } from './certificados-obra.service';
import { CostosObraInmService } from './costos-obra.service';
import {
  AnularCertificadoDto,
  CreateCertificadoDto,
  CreateImputacionDto,
  CreatePartidaDto,
  DevolverFondoDto,
  LotePartidasDto,
  UpdateCertificadoDto,
  UpdatePartidaDto,
  VincularFacturaDto,
} from './dto/obra.dto';
import { PresupuestoObraInmService } from './presupuesto-obra.service';

@ApiTags('Inmobiliario · Obra')
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'), SubmoduloGuard)
@RequireSubmodulo('INM_OBRA')
@Controller({ path: 'inmobiliario/obra', version: '1' })
export class ObraInmController {
  constructor(
    private readonly presupuesto: PresupuestoObraInmService,
    private readonly certificados: CertificadosObraInmService,
    private readonly costos: CostosObraInmService,
  ) {}

  // ---------------------------------------------------------------- presupuesto

  @Get('proyectos/:id/presupuesto')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_PRESUPUESTO_VER')
  @ApiOperation({ summary: 'Presupuesto contra ejecutado, por partida y por rubro' })
  resumen(@Param('id', ParseUUIDPipe) id: string, @GetUser() user: LoginUserInfo) {
    return this.presupuesto.resumen(id, user.empresa_id);
  }

  @Get('proyectos/:id/costo-unidades')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_COSTO_VER')
  @ApiOperation({ summary: 'Costo por unidad prorrateado por superficie' })
  costoUnidades(@Param('id', ParseUUIDPipe) id: string, @GetUser() user: LoginUserInfo) {
    return this.presupuesto.costoPorUnidad(id, user.empresa_id);
  }

  @Get('proyectos/:id/partidas')
  @ApiOperation({
    summary: 'Partidas activas (id, código, nombre) para los selectores',
    description:
      'Sólo exige el submódulo: lo usan certificados y costos, y el guard de permisos admite un único privilegio.',
  })
  partidas(@Param('id', ParseUUIDPipe) id: string, @GetUser() user: LoginUserInfo) {
    return this.presupuesto.listarPartidas(id, user.empresa_id);
  }

  @Post('proyectos/:id/partidas')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_PRESUPUESTO_CREAR')
  crearPartida(
    @Param('id', ParseUUIDPipe) id: string,
    @Body(ValidationPipe) dto: CreatePartidaDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.presupuesto.crearPartida(id, dto, user.empresa_id, user.id);
  }

  @Post('proyectos/:id/partidas/lote')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_PRESUPUESTO_CREAR')
  @ApiOperation({ summary: 'Carga varias partidas de una vez (pegadas desde una planilla)' })
  crearLote(
    @Param('id', ParseUUIDPipe) id: string,
    @Body(ValidationPipe) dto: LotePartidasDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.presupuesto.crearLote(id, dto, user.empresa_id, user.id);
  }

  @Patch('partidas/:id')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_PRESUPUESTO_EDITAR')
  actualizarPartida(
    @Param('id', ParseUUIDPipe) id: string,
    @Body(ValidationPipe) dto: UpdatePartidaDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.presupuesto.actualizarPartida(id, dto, user.empresa_id, user.id);
  }

  @Delete('partidas/:id')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_PRESUPUESTO_ELIMINAR')
  bajaPartida(@Param('id', ParseUUIDPipe) id: string, @GetUser() user: LoginUserInfo) {
    return this.presupuesto.bajaPartida(id, user.empresa_id, user.id);
  }

  // --------------------------------------------------------------- certificados

  @Get('certificados')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_CERTIFICADO_VER')
  @ApiQuery({ name: 'proyecto_id', required: false })
  @ApiQuery({ name: 'partida_id', required: false })
  @ApiQuery({ name: 'proveedor_id', required: false })
  @ApiQuery({ name: 'estado', required: false, enum: inm_certificado_estado })
  listarCertificados(
    @GetUser() user: LoginUserInfo,
    @Query('proyecto_id') proyecto_id?: string,
    @Query('partida_id') partida_id?: string,
    @Query('proveedor_id') proveedor_id?: string,
    @Query('estado') estado?: inm_certificado_estado,
  ) {
    return this.certificados.listar(user.empresa_id, { proyecto_id, partida_id, proveedor_id, estado });
  }

  @Get('certificados/:id')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_CERTIFICADO_VER')
  certificado(@Param('id', ParseUUIDPipe) id: string, @GetUser() user: LoginUserInfo) {
    return this.certificados.detalle(id, user.empresa_id);
  }

  @Post('certificados')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_CERTIFICADO_CREAR')
  crearCertificado(@Body(ValidationPipe) dto: CreateCertificadoDto, @GetUser() user: LoginUserInfo) {
    return this.certificados.crear(dto, user.empresa_id, user.id);
  }

  @Patch('certificados/:id')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_CERTIFICADO_CREAR')
  actualizarCertificado(
    @Param('id', ParseUUIDPipe) id: string,
    @Body(ValidationPipe) dto: UpdateCertificadoDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.certificados.actualizar(id, dto, user.empresa_id, user.id);
  }

  @Post('certificados/:id/aprobar')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_CERTIFICADO_APROBAR')
  @ApiOperation({ summary: 'Aprueba el certificado: imputa el costo y actualiza el avance de la partida' })
  aprobar(@Param('id', ParseUUIDPipe) id: string, @GetUser() user: LoginUserInfo) {
    return this.certificados.aprobar(id, user.empresa_id, user.id);
  }

  @Post('certificados/:id/anular')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_CERTIFICADO_ANULAR')
  anular(
    @Param('id', ParseUUIDPipe) id: string,
    @Body(ValidationPipe) dto: AnularCertificadoDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.certificados.anular(id, dto, user.empresa_id, user.id);
  }

  @Get('certificados/:id/facturas')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_CERTIFICADO_CREAR')
  @ApiOperation({ summary: 'Compras del contratista que se pueden vincular al certificado' })
  @ApiQuery({ name: 'q', required: false })
  async facturasVinculables(
    @Param('id', ParseUUIDPipe) id: string,
    @GetUser() user: LoginUserInfo,
    @Query('q') q?: string,
  ) {
    const c = await this.certificados.detalle(id, user.empresa_id);
    return this.costos.imputables(user.empresa_id, c.proyecto_id, 'COMPRA', { q, proveedor_id: c.proveedor_id });
  }

  @Put('certificados/:id/factura')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_CERTIFICADO_CREAR')
  @ApiOperation({ summary: 'Vincula (o quita) la factura del contratista cargada en Compras' })
  vincularFactura(
    @Param('id', ParseUUIDPipe) id: string,
    @Body(ValidationPipe) dto: VincularFacturaDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.certificados.vincularFactura(id, dto, user.empresa_id, user.id);
  }

  @Post('certificados/:id/devolver-fondo')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_CERTIFICADO_APROBAR')
  @ApiOperation({ summary: 'Registra la devolución del fondo de reparo al contratista' })
  devolverFondo(
    @Param('id', ParseUUIDPipe) id: string,
    @Body(ValidationPipe) dto: DevolverFondoDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.certificados.devolverFondo(id, dto, user.empresa_id, user.id);
  }

  // ------------------------------------------------------------------- costos

  @Get('imputaciones')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_COSTO_VER')
  @ApiQuery({ name: 'proyecto_id', required: false })
  @ApiQuery({ name: 'partida_id', required: false })
  @ApiQuery({ name: 'origen', required: false, enum: inm_imputacion_origen })
  imputaciones(
    @GetUser() user: LoginUserInfo,
    @Query('proyecto_id') proyecto_id?: string,
    @Query('partida_id') partida_id?: string,
    @Query('origen') origen?: inm_imputacion_origen,
  ) {
    return this.costos.listar(user.empresa_id, { proyecto_id, partida_id, origen });
  }

  @Get('proyectos/:id/imputables')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_COSTO_IMPUTAR')
  @ApiOperation({ summary: 'Compras o gastos con base todavía sin imputar' })
  @ApiQuery({ name: 'tipo', enum: ['COMPRA', 'GASTO'] })
  @ApiQuery({ name: 'q', required: false })
  @ApiQuery({ name: 'desde', required: false })
  @ApiQuery({ name: 'hasta', required: false })
  @ApiQuery({ name: 'proveedor_id', required: false })
  imputables(
    @Param('id', ParseUUIDPipe) id: string,
    @GetUser() user: LoginUserInfo,
    @Query('tipo') tipo: string,
    @Query('q') q?: string,
    @Query('desde') desde?: string,
    @Query('hasta') hasta?: string,
    @Query('proveedor_id') proveedor_id?: string,
  ) {
    return this.costos.imputables(user.empresa_id, id, tipo === 'GASTO' ? 'GASTO' : 'COMPRA', {
      q,
      desde,
      hasta,
      proveedor_id,
    });
  }

  @Post('imputaciones')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_COSTO_IMPUTAR')
  imputar(@Body(ValidationPipe) dto: CreateImputacionDto, @GetUser() user: LoginUserInfo) {
    return this.costos.imputar(dto, user.empresa_id, user.id);
  }

  @Delete('imputaciones/:id')
  @UseGuards(PermissionGuard)
  @RequirePermission('INMOBILIARIO', 'INM_OBR_COSTO_ELIMINAR')
  eliminarImputacion(@Param('id', ParseUUIDPipe) id: string, @GetUser() user: LoginUserInfo) {
    return this.costos.eliminar(id, user.empresa_id, user.id);
  }
}
