import {
    Body,
    Controller,
    Delete,
    Get,
    HttpException,
    HttpStatus,
    Param,
    Patch,
    Post,
    Put,
    Query,
    UploadedFile,
    UseGuards,
    UseInterceptors,
    ValidationPipe
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { FileInterceptor } from '@nestjs/platform-express';
import {
    ApiBearerAuth,
    ApiBody,
    ApiConsumes,
    ApiCreatedResponse,
    ApiOperation,
    ApiParam,
    ApiQuery,
} from '@nestjs/swagger';
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 { BulkFilasService } from './bulk-filas.service';
import { BulkFilasDto } from './dto/bulk-filas.dto';
import { BulkUpdateProductosDto } from './dto/bulk-update-productos.dto';
import { CreateProductoImagenDto } from './dto/create-producto-imagen.dto';
import { CreateProductoDto } from './dto/create-producto.dto';
import {
    response_producto_all,
    response_producto_find,
    response_producto_imagen,
    response_producto_standard,
} from './dto/doc-swagger';
import { GenerateVariantesDto } from './dto/generate-variantes.dto';
import { UpdateProductoImagenDto } from './dto/update-producto-imagen.dto';
import { UpdateProductoDto } from './dto/update-producto.dto';
import { CampoPendiente, ES_PENDIENTE } from './pendientes';
import { ProductosService } from './productos.service';

@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'), ModuleGuard)
@RequireModule('INVENTARIO')
@Controller({ path: 'productos', version: '1' })
export class ProductosController {
  constructor(
    private readonly productosService: ProductosService,
    private readonly bulkFilasService: BulkFilasService,
  ) {}

  @Post()
  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_PRODUCTO_CREAR')
  @ApiBody({ type: CreateProductoDto })
  @ApiCreatedResponse(response_producto_standard)
  create(@Body() createProductoDto: CreateProductoDto, @GetUser() user: LoginUserInfo) {
    return this.productosService.create(createProductoDto, user.empresa_id, user.id);
  }

  @ApiParam({
    name: 'page',
    type: 'string',
    required: false,
    description: 'Pagina actual',
  })
  @ApiParam({
    name: 'limit',
    type: 'string',
    required: false,
    description: 'Limit',
  })
  @ApiParam({
    name: 'categoria_id',
    type: 'string',
    required: false,
    description: 'Filtrar por categoría',
  })
  @ApiParam({
    name: 'mostrar_en_ecommerce',
    type: 'string',
    required: false,
    description: 'Filtrar por productos publicados (true) o no publicados (false) en el ecommerce',
  })
  @ApiQuery({
    name: 'pendiente',
    required: false,
    description: 'Filtrar productos a los que les falta un dato: costo, precio, categoria o marca',
  })
  @ApiQuery({
    name: 'rubro_id',
    required: false,
    description:
      'Rubro en contexto. Devuelve los productos de categorías del rubro más los compartidos (sin categoría o con categoría sin rubro).',
  })
  @ApiCreatedResponse(response_producto_all)
  @Get()
  async findAll(
    @GetUser() user: LoginUserInfo,
    @Query('page') page?: string,
    @Query('limit') limit?: string,
    @Query('categoria_id') categoria_id?: string,
    @Query('marca_id') marca_id?: string,
    @Query('solo_activos') solo_activos?: string,
    @Query('busqueda') busqueda?: string,
    @Query('deposito_id') deposito_id?: string,
    @Query('mostrar_en_ecommerce') mostrar_en_ecommerce?: string,
    @Query('pendiente') pendiente?: string,
    @Query('sucursal_id') sucursal_id?: string,
    @Query('filtro_stock') filtro_stock?: string,
    @Query('rubro_id') rubro_id?: string,
  ) {
    try {
      const pendienteValido = ES_PENDIENTE.includes(pendiente as CampoPendiente)
        ? (pendiente as CampoPendiente)
        : undefined;

      const {
        items,
        total,
        page: currentPage,
        limit: pageLimit,
        lastPage,
      } = await this.productosService.findAll(
        Number(page) || 1,
        Number(limit) || 10,
        user.empresa_id,
        categoria_id,
        solo_activos === 'true',
        busqueda,
        marca_id,
        deposito_id,
        mostrar_en_ecommerce,
        pendienteValido,
        sucursal_id,
        filtro_stock === 'con' || filtro_stock === 'sin' ? filtro_stock : undefined,
        user.id,
        rubro_id || undefined,
      );

      return {
        message: 'Productos obtenidos exitosamente',
        data: items,
        total,
        page: currentPage,
        limit: pageLimit,
        lastPage,
      };
    } catch (error) {
      console.log(error);
      const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
      throw new HttpException(
        {
          success: false,
          message: 'Error al obtener clientes',
          error: errorMessage,
        },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Get('search')
  @ApiParam({
    name: 'q',
    type: 'string',
    required: true,
    description: 'Término de búsqueda',
  })
  @ApiParam({
    name: 'limit',
    type: 'string',
    required: false,
    description: 'Límite de resultados',
  })
  @ApiParam({
    name: 'deposito_id',
    type: 'string',
    required: false,
    description: 'ID del depósito para consultar stock',
  })
  async search(
    @Query('q') query: string,
    @Query('limit') limit: string,
    @Query('deposito_id') deposito_id: string,
    @Query('categoria_id') categoria_id: string,
    @Query('solo_activos') solo_activos: string,
    @Query('marca_id') marca_id: string,
    @GetUser() user: LoginUserInfo,
    // Rubro del POS: recorta la búsqueda al catálogo de la sucursal donde vende.
    // Ver docs/plan-rubros-multi-sucursal.md.
    @Query('rubro_id') rubro_id?: string,
  ) {
    const items = await this.productosService.search(
      query,
      user.empresa_id,
      Number(limit) || 20,
      deposito_id,
      categoria_id,
      user.id,
      solo_activos === 'true',
      marca_id,
      rubro_id,
    );
    return { message: 'Búsqueda exitosa', data: items };
  }

  @Get('destacados')
  @ApiParam({
    name: 'limit',
    type: 'string',
    required: false,
    description: 'Límite de resultados',
  })
  async getDestacados(@Query('limit') limit: string, @GetUser() user: LoginUserInfo) {
    const items = await this.productosService.findDestacados(user.empresa_id, Number(limit) || 50);
    return {
      message: 'Productos destacados obtenidos exitosamente',
      data: items,
    };
  }

  @Get(':id')
  @ApiParam({
    name: 'id',
    type: 'string',
    required: true,
    description: 'ID del producto',
  })
  @ApiCreatedResponse(response_producto_find)
  findOne(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.productosService.findOne(id, user.empresa_id);
  }

  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_PRODUCTO_EDITAR')
  @ApiParam({
    name: 'id',
    type: 'string',
    required: true,
    description: 'ID del producto',
  })
  @ApiCreatedResponse(response_producto_standard)
  @Put('activar-desactivar/:id')
  activarDesactivar(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.productosService.activarDesactivar(id, user.empresa_id, user.id);
  }

  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_PRODUCTO_EDITAR')
  @ApiParam({
    name: 'id',
    type: 'string',
    required: true,
    description: 'ID del producto',
  })
  @ApiBody({ type: UpdateProductoDto })
  @ApiCreatedResponse(response_producto_standard)
  @Put(':id')
  update(@Param('id') id: string, @Body() updateProductoDto: UpdateProductoDto, @GetUser() user: LoginUserInfo) {
    return this.productosService.update(id, updateProductoDto, user.empresa_id, user.id);
  }

  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_PRODUCTO_ELIMINAR')
  @ApiParam({
    name: 'id',
    type: 'string',
    required: true,
    description: 'ID del producto',
  })
  @Delete(':id')
  @ApiCreatedResponse(response_producto_standard)
  remove(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.productosService.remove(id, user.empresa_id, user.id);
  }

  // ─── VARIANTES ──────────────────────────────────────────────

  @Post(':id/variantes')
  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_PRODUCTO_CREAR')
  @ApiParam({ name: 'id', description: 'ID del producto padre' })
  @ApiBody({ type: GenerateVariantesDto })
  generateVariantes(
    @Param('id') id: string,
    @Body(ValidationPipe) dto: GenerateVariantesDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.productosService.generateVariantes(id, dto, user.empresa_id, user.id);
  }

  @Get(':id/variantes')
  @ApiParam({ name: 'id', description: 'ID del producto padre' })
  getVariantes(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    return this.productosService.getVariantes(id, user.empresa_id);
  }

  // ─── IMÁGENES ───────────────────────────────────────────────

  @Post(':productoId/imagenes')
  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_PRODUCTO_EDITAR')
  @ApiConsumes('multipart/form-data')
  @ApiParam({
    name: 'productoId',
    type: 'string',
    required: true,
    description: 'ID del producto',
  })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        file: { type: 'string', format: 'binary' },
        orden: { type: 'number', nullable: true },
        es_principal: { type: 'boolean', nullable: true },
      },
      required: ['file'],
    },
  })
  @ApiCreatedResponse(response_producto_imagen)
  @UseInterceptors(FileInterceptor('file'))
  uploadImagen(
    @Param('productoId') productoId: string,
    @UploadedFile() file: Express.Multer.File,
    @Body() dto: CreateProductoImagenDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.productosService.addImagen(productoId, file, dto, user.empresa_id, user.id);
  }

  @Put(':productoId/imagenes/:imagenId')
  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_PRODUCTO_EDITAR')
  @ApiConsumes('multipart/form-data')
  @ApiParam({
    name: 'productoId',
    type: 'string',
    required: true,
    description: 'ID del producto',
  })
  @ApiParam({
    name: 'imagenId',
    type: 'string',
    required: true,
    description: 'ID de la imagen',
  })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        file: { type: 'string', format: 'binary', nullable: true },
        orden: { type: 'number', nullable: true },
        es_principal: { type: 'boolean', nullable: true },
      },
    },
  })
  @ApiCreatedResponse(response_producto_imagen)
  @UseInterceptors(FileInterceptor('file'))
  updateImagen(
    @Param('productoId') productoId: string,
    @Param('imagenId') imagenId: string,
    @UploadedFile() file: Express.Multer.File,
    @Body() dto: UpdateProductoImagenDto,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.productosService.updateImagen(productoId, imagenId, file, dto, user.empresa_id, user.id);
  }

  @Delete(':productoId/imagenes/:imagenId')
  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_PRODUCTO_ELIMINAR')
  @ApiParam({
    name: 'productoId',
    type: 'string',
    required: true,
    description: 'ID del producto',
  })
  @ApiParam({
    name: 'imagenId',
    type: 'string',
    required: true,
    description: 'ID de la imagen',
  })
  @ApiCreatedResponse(response_producto_imagen)
  deleteImagen(
    @Param('productoId') productoId: string,
    @Param('imagenId') imagenId: string,
    @GetUser() user: LoginUserInfo,
  ) {
    return this.productosService.deleteImagen(productoId, imagenId, user.empresa_id, user.id);
  }

  // ─── ACCIONES MASIVAS ───────────────────────────────────────
  // Nota: las rutas usan el prefijo `bulk/` (dos segmentos en el GET) para no colisionar
  // con `@Get(':id')`, que está declarado más arriba en este mismo controlador.

  @Get('bulk/ids')
  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_PRODUCTO_VER')
  @ApiQuery({ name: 'categoria_id', required: false, description: 'IDs de categoría separados por coma' })
  @ApiQuery({ name: 'marca_id', required: false, description: 'IDs de marca separados por coma' })
  @ApiQuery({ name: 'solo_activos', required: false, description: 'true para incluir solo productos activos' })
  @ApiQuery({ name: 'busqueda', required: false, description: 'Término de búsqueda (mínimo 2 caracteres)' })
  @ApiQuery({
    name: 'mostrar_en_ecommerce',
    required: false,
    description: 'true / false para filtrar por publicación en ecommerce',
  })
  @ApiQuery({
    name: 'pendiente',
    required: false,
    description: 'Filtrar productos a los que les falta un dato: costo, precio, categoria o marca',
  })
  async findIdsForBulk(
    @GetUser() user: LoginUserInfo,
    @Query('categoria_id') categoria_id?: string,
    @Query('marca_id') marca_id?: string,
    @Query('solo_activos') solo_activos?: string,
    @Query('busqueda') busqueda?: string,
    @Query('mostrar_en_ecommerce') mostrar_en_ecommerce?: string,
    @Query('pendiente') pendiente?: string,
  ) {
    const pendienteValido = ES_PENDIENTE.includes(pendiente as CampoPendiente)
      ? (pendiente as CampoPendiente)
      : undefined;

    const { ids, total, truncado, limite } = await this.productosService.findIdsByFilters(user.empresa_id, {
      categoria_id,
      marca_id,
      solo_activos: solo_activos === 'true',
      busqueda,
      mostrar_en_ecommerce,
      pendiente: pendienteValido,
    });

    return {
      message: 'IDs de productos obtenidos exitosamente',
      data: ids,
      total,
      truncado,
      limite,
    };
  }

  @Patch('bulk')
  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_PRODUCTO_EDITAR')
  @ApiBody({ type: BulkUpdateProductosDto })
  bulkUpdate(@Body() dto: BulkUpdateProductosDto, @GetUser() user: LoginUserInfo) {
    return this.productosService.bulkUpdate(dto, user.empresa_id, user.id);
  }

  @Post('bulk-filas/validar')
  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_EDICION_MASIVA')
  @ApiBody({ type: BulkFilasDto })
  @ApiOperation({
    summary: 'Valida (dry-run) una edición masiva con valores distintos por fila. No escribe nada.',
  })
  async validarBulkFilas(@Body() dto: BulkFilasDto, @GetUser() user: LoginUserInfo) {
    const permisos = await this.bulkFilasService.permisosDe(user.id);
    return this.bulkFilasService.validar(dto, user.empresa_id, permisos);
  }

  @Post('bulk-filas')
  @UseGuards(PermissionGuard)
  @RequirePermission('INVENTARIO', 'INV_PRD_EDICION_MASIVA')
  @ApiBody({ type: BulkFilasDto })
  @ApiOperation({
    summary: 'Aplica una edición masiva con valores distintos por fila. Valida todo antes de escribir.',
  })
  async aplicarBulkFilas(@Body() dto: BulkFilasDto, @GetUser() user: LoginUserInfo) {
    const permisos = await this.bulkFilasService.permisosDe(user.id);
    return this.bulkFilasService.aplicar(dto, user.empresa_id, user.id, permisos);
  }
}
