import { BadRequestException, ConflictException, ForbiddenException, Injectable, NotFoundException, Logger } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { usuario } from '@prisma/client';
import * as bcrypt from 'bcrypt';
import { AuditService } from 'src/audit/audit.service';
import { EstadoAutorizacionCaja } from 'src/autorizaciones-caja/dto/crear-autorizacion.dto';
import { CodigosVerificacionService } from 'src/codigos-verificacion/codigos-verificacion.service';
import { PlanLimitsService } from 'src/plan-limits/plan-limits.service';
import { PrismaService } from 'src/prisma/prisma.service';
import { QueuesService } from 'src/queues/queues.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { ValidarPinDto } from './dto/validar-pin.dto';

@Injectable()
export class UsersService {
  private readonly logger = new Logger(UsersService.name);
  constructor(
    private prisma: PrismaService,
    private codigosVerificacionService: CodigosVerificacionService,
    private queuesService: QueuesService,
    private readonly auditService: AuditService,
    private readonly planLimits: PlanLimitsService,
    private readonly jwtService: JwtService,
  ) {}

  /**
   * Resuelve el alcance multi-empresa del actor para la gestión de usuarios.
   * - `elevated=true` (superAdmin, holding o reseller): puede gestionar usuarios de
   *   cualquier empresa (mismo criterio que los guards de permisos).
   * - `elevated=false` (usuario normal): solo su propia empresa. Cualquier `empresa_id`
   *   que llegue del cliente se ignora/valida contra `actorEmpresaId`.
   */
  private async resolveActorScope(
    actorId?: string,
    actorEmpresaId?: string,
  ): Promise<{ elevated: boolean; empresaId?: string }> {
    if (!actorId || !actorEmpresaId) {
      // Sin actor identificable → tratar como no elevado y sin empresa (deniega cross-tenant).
      return { elevated: false, empresaId: actorEmpresaId };
    }
    const [actor, empresa] = await Promise.all([
      this.prisma.usuario.findUnique({ where: { id: actorId }, select: { is_superadmin: true } }),
      this.prisma.empresas.findUnique({ where: { id: actorEmpresaId }, select: { type: true } }),
    ]);
    const elevated =
      (actor?.is_superadmin ?? false) || empresa?.type === 'holding' || empresa?.type === 'reseller';
    return { elevated, empresaId: actorEmpresaId };
  }

  /**
   * Verifica que el usuario objetivo pertenezca a la empresa del actor (salvo elevado).
   * Lanza NotFound si no existe, y Forbidden si es de otra empresa. Devuelve el usuario.
   */
  private async assertUsuarioEnAlcance(
    targetId: string,
    actorId?: string,
    actorEmpresaId?: string,
  ): Promise<{ id: string; empresa_id: string | null; username: string; nombres: string | null; estado: string | null }> {
    const target = await this.prisma.usuario.findUnique({
      where: { id: targetId },
      select: { id: true, empresa_id: true, username: true, nombres: true, estado: true },
    });
    if (!target) throw new NotFoundException('Usuario no encontrado');
    const { elevated } = await this.resolveActorScope(actorId, actorEmpresaId);
    if (!elevated && target.empresa_id !== actorEmpresaId) {
      // Mismo mensaje que "no encontrado" para no revelar existencia de usuarios de otras empresas.
      throw new NotFoundException('Usuario no encontrado');
    }
    return target;
  }

  async create(
    createUserDto: CreateUserDto,
    actor_id?: string,
    actor_empresa_id?: string,
  ): Promise<{
    message: string;
    data: usuario;
  }> {
    const userFind = await this.prisma.usuario.findUnique({ where: { username: createUserDto.username } });
    if (userFind) throw new ConflictException('Usuario ya existe');

    // Multi-tenant: un actor no elevado solo puede crear usuarios en SU empresa.
    // Se ignora cualquier empresa_id del body que apunte a otra empresa.
    const { elevated } = await this.resolveActorScope(actor_id, actor_empresa_id);
    if (!elevated) {
      if (!actor_empresa_id) throw new ForbiddenException('No autorizado a crear usuarios');
      createUserDto.empresa_id = actor_empresa_id;
    }

    // Validar que los perfiles asignados pertenezcan a la empresa destino o sean de
    // sistema (empresa_id null). Evita que un admin asigne perfiles de otra empresa.
    if (!elevated && createUserDto.perfiles?.length) {
      const perfilesValidos = await this.prisma.perfiles.findMany({
        where: {
          id: { in: createUserDto.perfiles },
          OR: [{ empresa_id: createUserDto.empresa_id }, { is_sistema: true }],
        },
        select: { id: true },
      });
      if (perfilesValidos.length !== createUserDto.perfiles.length) {
        throw new ForbiddenException('Uno o más perfiles no pertenecen a la empresa');
      }
    }

    await this.planLimits.checkLimit(createUserDto.empresa_id, 'usuarios');

    const rawPassword = createUserDto.password;
    if (!rawPassword) throw new Error('Password es obligatorio');
    createUserDto.password = await bcrypt.hash(rawPassword, 10);

    try {
      const user: usuario = await this.prisma.usuario.create({
        data: {
          nombres: createUserDto.nombres ?? '',
          apellidos: createUserDto.apellidos ?? '',
          username: createUserDto.username,
          password: createUserDto.password,
          email: createUserDto.email?.trim() || null,
          verificado: true,
          estado: createUserDto.estado,
          empresa_id: createUserDto.empresa_id,
        },
      });
      // Asignar el Perfil Admin al usuario
      if (createUserDto.perfiles && createUserDto.perfiles.length > 0) {
        for (const perfil of createUserDto.perfiles) {
          const perfilFind = await this.prisma.perfiles.findUnique({ where: { id: perfil } });
          if (perfilFind) {
            await this.prisma.usuario_perfiles.create({
              data: {
                user_id: user.id,
                perfil_id: perfil,
              },
            });
          }
        }
      }

      this.logger.log(`Usuario creado: ${user.username} (${user.id})`, 'UsersService');
      await this.auditService.log({
        empresa_id: createUserDto.empresa_id,
        user_id: actor_id,
        action: 'CREATE',
        entity_type: 'usuario',
        entity_id: user.id,
        descripcion: `Usuario creado: ${user.username} | Empresa: ${createUserDto.empresa_id}`,
        new_value: { username: user.username, nombres: user.nombres, empresa_id: user.empresa_id },
      });
      return { message: 'Usuario creado exitosamente', data: user };
    } catch (error) {
      if (error instanceof ConflictException) throw error;
      const message = error instanceof Error ? error.message : 'Error desconocido';
      throw new Error(`Error al crear usuario: ${message}`);
    }
  }

  async create_user_inicial(createUserDto: CreateUserDto): Promise<{
    user: usuario;
  }> {
    try {
      const user: usuario = await this.prisma.usuario.create({
        data: {
          nombres: createUserDto.nombres,
          username: createUserDto.username,
          password: createUserDto.password,
          estado: createUserDto.estado,
          empresa_id: createUserDto.empresa_id,
        },
      });
      // Asignar el Perfil Admin al usuario
      const perfil = await this.prisma.perfiles.findFirst({
        where: {
          perfil: 'Admin',
        },
      });
      if (perfil) {
        await this.prisma.usuario_perfiles.create({
          data: {
            user_id: user.id,
            perfil_id: perfil.id,
          },
        });
      }

      // Tambien generar un usuario funcional para autenticación del mid (middleware)

      await this.prisma.usuario_funcional.create({
        data: {
          username: createUserDto.ruc,
          password: createUserDto.passwordUserFuncional,
          active: true,
          verificado: true,
          estado: 'activo',
          empresas: {
            connect: {
              id: createUserDto.empresa_id,
            },
          },
        },
      });

      // // Generar codigo de verificacion
      // const codigoVerificacion: codigos_verificacion = await this.codigosVerificacionService.create({
      //   usuario_id: user.id,
      //   tipo: 'email',
      //   email: createUserDto.email,
      //   celular: createUserDto.celular,
      // });

      // // Enviar email a través de la cola
      // await this.queuesService.sendVerificationEmail(createUserDto.email, codigoVerificacion.codigo, user.id);

      return { user };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error desconocido';
      throw new Error(`Error al crear usuario: ${message}`);
    }
  }

  async findAll(page = 1, limit = 10, empresa_id?: string, actor_id?: string, actor_empresa_id?: string) {
    const take = Math.max(1, Math.min(100, Number(limit) || 10));
    const currentPage = Math.max(1, Number(page) || 1);
    const skip = (currentPage - 1) * take;

    // Multi-tenant: el actor no elevado SOLO ve usuarios de su empresa (se ignora el
    // empresa_id del cliente). El elevado puede filtrar por la empresa que indique.
    const { elevated } = await this.resolveActorScope(actor_id, actor_empresa_id);
    const empresaScope = elevated ? empresa_id : actor_empresa_id;
    const where = {
      ...(empresaScope && { empresa_id: empresaScope }),
    };

    const [items, total] = await this.prisma.$transaction([
      this.prisma.usuario.findMany({
        where,
        select: {
          id: true,
          username: true,
          nombres: true,
          apellidos: true,
          email: true,
          estado: true,
          active: true,
          verificado: true,
          empresa_id: true,
          created_at: true,
          pin_supervisor: true,
          usuario_perfiles: {
            include: {
              perfiles: {
                select: {
                  id: true,
                  perfil: true,
                },
              },
            },
          },
        },
        skip,
        take,
        orderBy: { created_at: 'desc' },
      }),
      this.prisma.usuario.count({ where }),
    ]);

    // Map: no exponer el hash del PIN, solo indicar si tiene
    const mappedItems = items.map(({ pin_supervisor, ...rest }) => ({
      ...rest,
      es_supervisor: !!pin_supervisor,
    }));

    return {
      items: mappedItems,
      total,
      page: currentPage,
      limit: take,
      lastPage: Math.max(1, Math.ceil(total / take) || 1),
    };
  }

  async findOne(id: string, actor_id?: string, actor_empresa_id?: string) {
    try {
      // Multi-tenant: no permitir leer usuarios de otra empresa (salvo elevado).
      await this.assertUsuarioEnAlcance(id, actor_id, actor_empresa_id);
      const user = await this.prisma.usuario.findUnique({
        where: { id },
        select: {
          id: true,
          username: true,
          nombres: true,
          apellidos: true,
          estado: true,
          verificado: true,
          usuario_perfiles: {
            include: {
              perfiles: {
                include: {
                  perfiles_privilegios: {
                    include: {
                      privilegios: true,
                      modulos: true,
                    },
                  },
                },
              },
            },
          },
        },
      });
      if (!user) throw new NotFoundException('Usuario no encontrado');

      return { message: 'Usuario encontrado', data: user };
    } catch (error) {
      // Re-lanzar las excepciones HTTP (NotFound/Forbidden) sin convertirlas en 500.
      if (error instanceof NotFoundException || error instanceof ForbiddenException) throw error;
      this.logger.error(`Error al buscar usuario ${id}`, error instanceof Error ? error.stack : undefined);
      const message = error instanceof Error ? error.message : 'Error desconocido';
      throw new Error(`Error al buscar usuario: ${message}`);
    }
  }

  async update(id: string, updateUserDto: UpdateUserDto, actor_id?: string, actor_empresa_id?: string) {
    try {
      // Multi-tenant: solo editar usuarios de la empresa del actor (salvo elevado).
      const usuarioFind = await this.assertUsuarioEnAlcance(id, actor_id, actor_empresa_id);
      const { elevated } = await this.resolveActorScope(actor_id, actor_empresa_id);
      // Si envia su password, lo encripta
      if (updateUserDto.password) {
        updateUserDto.password = await bcrypt.hash(updateUserDto.password, 10);
      }

      // Extraer campos que no son directos de Prisma
      const { empresa_id, perfiles, pin_supervisor, ...restDto } = updateUserDto;

      // Un actor no elevado NO puede mover un usuario a otra empresa: se ignora empresa_id.
      const empresaDestino = elevated ? empresa_id : undefined;

      // Validar que los perfiles asignados pertenezcan a la empresa del usuario o sean de
      // sistema (evita asignar perfiles de otra empresa vía este endpoint).
      if (!elevated && perfiles?.length) {
        const perfilesValidos = await this.prisma.perfiles.findMany({
          where: {
            id: { in: perfiles },
            OR: [{ empresa_id: usuarioFind.empresa_id ?? actor_empresa_id }, { is_sistema: true }],
          },
          select: { id: true },
        });
        if (perfilesValidos.length !== perfiles.length) {
          throw new ForbiddenException('Uno o más perfiles no pertenecen a la empresa');
        }
      }

      // Construir data para Prisma
      const updateData: Record<string, unknown> = { ...restDto };

      // Email vacío → null para evitar colisión en unique constraint
      if ('email' in updateData) {
        updateData.email = (updateData.email as string)?.trim() || null;
      }

      // Manejar PIN de supervisor
      if (pin_supervisor !== undefined) {
        if (pin_supervisor === '' || pin_supervisor === null) {
          updateData.pin_supervisor = null;
        } else {
          updateData.pin_supervisor = await bcrypt.hash(pin_supervisor, 10);
        }
      }

      // Si se envía empresa_id (solo elevado), usar la relación
      if (empresaDestino) {
        updateData.empresa_rel = { connect: { id: empresaDestino } };
      }

      const user = await this.prisma.usuario.update({
        where: { id },
        data: updateData,
      });

      // Actualizar perfiles si se enviaron (incluye arreglo vacío como reemplazo explícito)
      if (perfiles !== undefined) {
        // Eliminar perfiles actuales
        await this.prisma.usuario_perfiles.deleteMany({ where: { user_id: id } });
        // Crear nuevos perfiles cuando existan
        if (perfiles.length > 0) {
          await this.prisma.usuario_perfiles.createMany({
            data: perfiles.map((perfil_id) => ({ user_id: id, perfil_id })),
          });
        }
      }

      this.logger.log(`Usuario actualizado: ${usuarioFind.username} (${id})`, 'UsersService');
      await this.auditService.log({
        empresa_id: empresa_id ?? usuarioFind.empresa_id ?? undefined,
        user_id: actor_id,
        action: 'UPDATE',
        entity_type: 'usuario',
        entity_id: id,
        descripcion: `Usuario actualizado: ${usuarioFind.username}`,
        old_value: { username: usuarioFind.username, nombres: usuarioFind.nombres, estado: usuarioFind.estado },
        new_value: { nombres: updateUserDto.nombres, estado: updateUserDto.estado },
      });
      return { message: 'Usuario actualizado exitosamente', data: user };
    } catch (error) {
      if (error instanceof NotFoundException || error instanceof ForbiddenException) throw error;
      this.logger.error(`Error al actualizar usuario ${id}`, error instanceof Error ? error.stack : undefined);
      const message = error instanceof Error ? error.message : 'Error desconocido';
      throw new Error(`Error al actualizar usuario: ${message}`);
    }
  }

  async activarDesactivar(id: string, actor_id?: string, actor_empresa_id?: string) {
    try {
      // Multi-tenant: solo activar/desactivar usuarios de la empresa del actor.
      await this.assertUsuarioEnAlcance(id, actor_id, actor_empresa_id);
      const userActual = await this.prisma.usuario.findUnique({
        where: { id },
        select: { active: true, username: true, empresa_id: true },
      });
      if (!userActual) throw new NotFoundException('Usuario no encontrado');
      const user = await this.prisma.usuario.update({
        where: { id },
        data: { active: !userActual.active },
      });
      const action = user.active ? 'ACTIVATE' : 'DEACTIVATE';
      this.logger.log(`Usuario (${id}) ${user.active ? 'activado' : 'desactivado'}`, 'UsersService');
      await this.auditService.log({
        empresa_id: userActual.empresa_id ?? actor_empresa_id ?? undefined,
        user_id: actor_id,
        action,
        entity_type: 'usuario',
        entity_id: id,
        descripcion: `Usuario ${user.active ? 'activado' : 'desactivado'}: ${userActual.username}`,
        old_value: { active: userActual.active },
        new_value: { active: user.active },
      });
      return { message: 'Usuario actualizado exitosamente', data: user };
    } catch (error) {
      if (error instanceof NotFoundException || error instanceof ForbiddenException) throw error;
      this.logger.error(`Error al activar/desactivar usuario ${id}`, error instanceof Error ? error.stack : undefined);
      const message = error instanceof Error ? error.message : 'Error desconocido';
      throw new Error(`Error al actualizar usuario: ${message}`);
    }
  }

  async remove(id: string, actor_id?: string, actor_empresa_id?: string) {
    // Multi-tenant: solo eliminar usuarios de la empresa del actor.
    const userFind = await this.assertUsuarioEnAlcance(id, actor_id, actor_empresa_id);
    const user = await this.prisma.usuario.delete({
      where: { id },
    });
    this.logger.log(`Usuario eliminado: ${user.username} (${id})`, 'UsersService');
    await this.auditService.log({
      empresa_id: userFind?.empresa_id ?? undefined,
      user_id: actor_id,
      action: 'DELETE',
      entity_type: 'usuario',
      entity_id: id,
      descripcion: `Usuario eliminado: ${user.username}`,
      old_value: { username: user.username, empresa_id: user.empresa_id },
    });
    return { user };
  }

  // ==================== GESTIÓN DE ACCESO A EMPRESAS ====================

  async getEmpresasAsignadas(usuarioId: string) {
    const usuario = await this.prisma.usuario.findUnique({
      where: { id: usuarioId },
      select: { id: true, empresa_id: true },
    });
    if (!usuario) throw new NotFoundException('Usuario no encontrado');

    const asignaciones = await this.prisma.usuario_empresas.findMany({
      where: { usuario_id: usuarioId },
      include: {
        empresa: { select: { id: true, razon_social: true, ruc: true, type: true, active: true } },
      },
      orderBy: { created_at: 'desc' },
    });

    return {
      empresa_principal: usuario.empresa_id,
      asignaciones: asignaciones.map((a) => ({
        id: a.id,
        empresa_id: a.empresa_id,
        empresa: a.empresa,
        rol: a.rol,
        active: a.active,
        created_at: a.created_at,
      })),
    };
  }

  async asignarEmpresa(
    usuarioId: string,
    empresaId: string,
    rol: string = 'operador',
    createdBy?: string,
    actor_id?: string,
  ) {
    // Verificar que el usuario existe
    const usuario = await this.prisma.usuario.findUnique({ where: { id: usuarioId } });
    if (!usuario) throw new NotFoundException('Usuario no encontrado');

    // Verificar que la empresa existe
    const empresa = await this.prisma.empresas.findUnique({ where: { id: empresaId } });
    if (!empresa) throw new NotFoundException('Empresa no encontrada');

    // Verificar si ya existe la asignación
    const existente = await this.prisma.usuario_empresas.findFirst({
      where: { usuario_id: usuarioId, empresa_id: empresaId },
    });

    if (existente) {
      // Actualizar rol si ya existe
      const updated = await this.prisma.usuario_empresas.update({
        where: { id: existente.id },
        data: { rol, active: true, updated_at: new Date() },
        include: { empresa: { select: { id: true, razon_social: true, ruc: true } } },
      });
      return { message: 'Acceso actualizado', data: updated };
    }

    // Crear nueva asignación
    const asignacion = await this.prisma.usuario_empresas.create({
      data: {
        usuario_id: usuarioId,
        empresa_id: empresaId,
        rol,
        created_by: createdBy,
        active: true,
      },
      include: { empresa: { select: { id: true, razon_social: true, ruc: true } } },
    });

    this.logger.log(`Empresa asignada a usuario ${usuarioId}: ${empresaId}`, 'UsersService');
    await this.auditService.log({
      empresa_id: empresaId,
      user_id: actor_id ?? createdBy,
      action: 'CREATE',
      entity_type: 'usuario_empresa',
      entity_id: asignacion.id,
      descripcion: `Acceso asignado: Usuario ${usuarioId} → Empresa ${empresaId} | Rol: ${rol}`,
      new_value: { usuario_id: usuarioId, empresa_id: empresaId, rol },
    });
    return { message: 'Acceso asignado correctamente', data: asignacion };
  }

  async revocarEmpresa(usuarioId: string, empresaId: string, actor_id?: string) {
    const asignacion = await this.prisma.usuario_empresas.findFirst({
      where: { usuario_id: usuarioId, empresa_id: empresaId },
    });

    if (!asignacion) throw new NotFoundException('Asignación no encontrada');

    await this.prisma.usuario_empresas.delete({ where: { id: asignacion.id } });
    this.logger.log(`Acceso revocado: usuario ${usuarioId} empresa ${empresaId}`, 'UsersService');
    await this.auditService.log({
      empresa_id: empresaId,
      user_id: actor_id,
      action: 'DELETE',
      entity_type: 'usuario_empresa',
      entity_id: asignacion.id,
      descripcion: `Acceso revocado: Usuario ${usuarioId} → Empresa ${empresaId}`,
      old_value: { usuario_id: usuarioId, empresa_id: empresaId, rol: asignacion.rol },
    });
    return { message: 'Acceso revocado correctamente' };
  }

  async toggleAccesoEmpresa(usuarioId: string, empresaId: string, actor_id?: string) {
    const asignacion = await this.prisma.usuario_empresas.findFirst({
      where: { usuario_id: usuarioId, empresa_id: empresaId },
    });

    if (!asignacion) throw new NotFoundException('Asignación no encontrada');

    const updated = await this.prisma.usuario_empresas.update({
      where: { id: asignacion.id },
      data: { active: !asignacion.active, updated_at: new Date() },
      include: { empresa: { select: { id: true, razon_social: true, ruc: true } } },
    });

    this.logger.log(
      `Acceso empresa ${updated.active ? 'activado' : 'desactivado'}: usuario ${usuarioId} empresa ${empresaId}`,
      'UsersService',
    );
    await this.auditService.log({
      empresa_id: empresaId,
      user_id: actor_id,
      action: updated.active ? 'ACTIVATE' : 'DEACTIVATE',
      entity_type: 'usuario_empresa',
      entity_id: asignacion.id,
      descripcion: `Acceso ${updated.active ? 'activado' : 'desactivado'}: Usuario ${usuarioId} → Empresa ${empresaId}`,
      old_value: { active: asignacion.active },
      new_value: { active: updated.active },
    });
    return { message: `Acceso ${updated.active ? 'activado' : 'desactivado'}`, data: updated };
  }

  // ==================== PIN SUPERVISOR ====================

  async configurarPin(userId: string, pin: string, actor_id?: string, empresa_id?: string) {
    const user = await this.prisma.usuario.findUnique({ where: { id: userId } });
    if (!user) throw new NotFoundException('Usuario no encontrado');

    const pinHash = await bcrypt.hash(pin, 10);

    await this.prisma.usuario.update({
      where: { id: userId },
      data: { pin_supervisor: pinHash },
    });

    this.logger.log(`PIN supervisor configurado para usuario ${user.username} (${userId})`, 'UsersService');
    await this.auditService.log({
      empresa_id: empresa_id ?? user.empresa_id ?? undefined,
      user_id: actor_id,
      action: 'UPDATE',
      entity_type: 'usuario_pin_supervisor',
      entity_id: userId,
      descripcion: `PIN supervisor configurado para: ${user.username}`,
    });

    return { message: 'PIN supervisor configurado exitosamente' };
  }

  async validarPinSupervisor(dto: ValidarPinDto, empresa_id: string, solicitante_id?: string) {
    // Buscar supervisor por username o ID
    const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(dto.usuario_id_or_username);
    const supervisor = await this.prisma.usuario.findFirst({
      where: isUuid
        ? { id: dto.usuario_id_or_username, active: true }
        : { username: dto.usuario_id_or_username, active: true },
      include: {
        usuario_perfiles: {
          include: {
            perfiles: {
              include: {
                perfiles_privilegios: {
                  include: { privilegios: true },
                },
              },
            },
          },
        },
      },
    });

    if (!supervisor) throw new NotFoundException('Supervisor no encontrado');
    if (!supervisor.pin_supervisor) throw new BadRequestException('El supervisor no tiene PIN configurado');

    // Verificar PIN
    const pinValido = await bcrypt.compare(dto.pin, supervisor.pin_supervisor);
    if (!pinValido) throw new ForbiddenException('PIN incorrecto');

    // Verificar privilegio POS_SUPERVISOR (superadmin bypasea)
    if (!supervisor.is_superadmin) {
      const tienePosSupevisor = supervisor.usuario_perfiles.some((up) =>
        up.perfiles?.perfiles_privilegios?.some((pp) => pp.privilegios?.codigo === 'POS_SUPERVISOR'),
      );
      if (!tienePosSupevisor) throw new ForbiddenException('El usuario no tiene privilegio de supervisor POS');
    }

    // Generar token temporal (30 segundos)
    const tokenPayload = {
      sub: supervisor.id,
      tipo: 'autorizacion_caja',
      operacion: dto.tipo_operacion,
      empresa_id,
      contexto: dto.contexto ?? {},
    };

    const token = await this.jwtService.signAsync(tokenPayload, {
      expiresIn: '30s',
      secret: process.env.JWT_SECRET,
    });

    // Registrar en autorizaciones_caja (aprobada directamente por PIN presencial)
    const requesterId = solicitante_id || supervisor.id;
    let autorizacionId: string | undefined;
    try {
      const autorizacionRow = await this.prisma.autorizaciones_caja.create({
        data: {
          empresa_id,
          tipo: dto.tipo_operacion,
          solicitante_id: requesterId,
          autorizador_id: supervisor.id,
          estado: EstadoAutorizacionCaja.APROBADA,
          contexto: (dto.contexto as any) ?? undefined,
          resolved_at: new Date(),
          expires_at: new Date(Date.now() + 30 * 1000),
        },
      });
      autorizacionId = autorizacionRow.id;
    } catch (e) {
      this.logger.warn(`No se pudo registrar autorizacion_caja: ${e?.message}`, 'UsersService');
    }

    this.logger.log(
      `PIN supervisor validado: ${supervisor.username} → operación: ${dto.tipo_operacion}`,
      'UsersService',
    );

    await this.auditService.log({
      empresa_id,
      user_id: supervisor.id,
      action: 'AUTHORIZE',
      entity_type: 'pin_supervisor_validacion',
      entity_id: supervisor.id,
      descripcion: `Supervisor ${supervisor.username} autorizó operación: ${dto.tipo_operacion}`,
      new_value: { tipo_operacion: dto.tipo_operacion, contexto: dto.contexto },
    });

    return {
      message: 'PIN validado correctamente',
      token,
      autorizacion_id: autorizacionId,
      supervisor: {
        id: supervisor.id,
        username: supervisor.username,
        nombres: supervisor.nombres,
        apellidos: supervisor.apellidos,
      },
      expires_in: 30,
    };
  }

  async verificarTienePin(userId: string): Promise<{ tiene_pin: boolean }> {
    const user = await this.prisma.usuario.findUnique({
      where: { id: userId },
      select: { pin_supervisor: true },
    });
    if (!user) throw new NotFoundException('Usuario no encontrado');
    return { tiene_pin: !!user.pin_supervisor };
  }
}
