import { hash, compare } from "bcryptjs";
import { CustomerUser } from "@open-mercato/core/modules/customer_accounts/data/entities";
import { hashForLookup, lookupHashCandidates } from "@open-mercato/shared/lib/encryption/aes";
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
const BCRYPT_COST = 10;
const MAX_FAILED_ATTEMPTS = 5;
const LOCKOUT_DURATION_MS = 15 * 60 * 1e3;
class CustomerUserService {
  constructor(em) {
    this.em = em;
  }
  async createUser(email, password, displayName, scope) {
    const passwordHash = await hash(password, BCRYPT_COST);
    const emailHash = hashForLookup(email);
    const user = this.em.create(CustomerUser, {
      email: email.toLowerCase().trim(),
      emailHash,
      passwordHash,
      displayName,
      tenantId: scope.tenantId,
      organizationId: scope.organizationId,
      isActive: true,
      failedLoginAttempts: 0,
      createdAt: /* @__PURE__ */ new Date()
    });
    return user;
  }
  async findByEmail(email, tenantId) {
    return findOneWithDecryption(
      this.em,
      CustomerUser,
      {
        emailHash: { $in: lookupHashCandidates(email) },
        tenantId,
        deletedAt: null
      },
      void 0,
      { tenantId }
    );
  }
  async findById(id, tenantId, organizationId) {
    const where = { id, tenantId, deletedAt: null };
    if (organizationId !== void 0) where.organizationId = organizationId;
    return findOneWithDecryption(
      this.em,
      CustomerUser,
      where,
      void 0,
      { tenantId, organizationId }
    );
  }
  async verifyPassword(user, password) {
    if (!user.passwordHash) return false;
    return compare(password, user.passwordHash);
  }
  async updateLastLoginAt(user) {
    const now = /* @__PURE__ */ new Date();
    await this.em.nativeUpdate(CustomerUser, { id: user.id }, { lastLoginAt: now });
    user.lastLoginAt = now;
  }
  checkLockout(user) {
    if (!user.lockedUntil) return false;
    if (user.lockedUntil.getTime() > Date.now()) return true;
    return false;
  }
  async incrementFailedAttempts(user) {
    const newCount = (user.failedLoginAttempts || 0) + 1;
    const updates = { failedLoginAttempts: newCount };
    if (newCount >= MAX_FAILED_ATTEMPTS) {
      updates.lockedUntil = new Date(Date.now() + LOCKOUT_DURATION_MS);
    }
    await this.em.nativeUpdate(CustomerUser, { id: user.id }, updates);
    user.failedLoginAttempts = newCount;
    if (updates.lockedUntil) user.lockedUntil = updates.lockedUntil;
  }
  async resetFailedAttempts(user) {
    await this.em.nativeUpdate(CustomerUser, { id: user.id }, {
      failedLoginAttempts: 0,
      lockedUntil: null
    });
    user.failedLoginAttempts = 0;
    user.lockedUntil = null;
  }
  async updatePassword(user, newPassword, em) {
    const passwordHash = await hash(newPassword, BCRYPT_COST);
    await (em ?? this.em).nativeUpdate(CustomerUser, {
      id: user.id,
      tenantId: user.tenantId,
      organizationId: user.organizationId
    }, { passwordHash });
    user.passwordHash = passwordHash;
  }
  async updateProfile(user, data) {
    const updates = {};
    if (data.displayName !== void 0) updates.displayName = data.displayName;
    if (Object.keys(updates).length === 0) return;
    await this.em.nativeUpdate(CustomerUser, { id: user.id }, updates);
    if (data.displayName !== void 0) user.displayName = data.displayName;
  }
  async softDelete(userId, scope) {
    const where = { id: userId };
    if (scope) {
      where.tenantId = scope.tenantId;
      where.organizationId = scope.organizationId;
    }
    await this.em.nativeUpdate(CustomerUser, where, {
      deletedAt: /* @__PURE__ */ new Date(),
      isActive: false
    });
  }
}
export {
  CustomerUserService
};
//# sourceMappingURL=customerUserService.js.map
