import { compare, hash } from "bcryptjs";
import { User, UserRole, Session, PasswordReset } from "@open-mercato/core/modules/auth/data/entities";
import { emailHashLookupValues } from "@open-mercato/core/modules/auth/lib/emailHash";
import { generateAuthToken, hashAuthToken } from "@open-mercato/core/modules/auth/lib/tokenHash";
import { findWithDecryption, findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
const TIMING_EQUALIZER_PASSWORD_HASH = "$2b$10$OcZrhmZpIzJOjkfwUrk7d.Nl0eHNzOvalBcBlt5Ran.4lj8R3HZg6";
class AuthService {
  constructor(em) {
    this.em = em;
  }
  async findUserByEmail(email) {
    const emailHashes = emailHashLookupValues(email);
    return findOneWithDecryption(this.em, User, {
      deletedAt: null,
      $or: [
        { email },
        { emailHash: { $in: emailHashes } }
      ]
    });
  }
  async findUsersByEmail(email) {
    const emailHashes = emailHashLookupValues(email);
    return findWithDecryption(this.em, User, {
      deletedAt: null,
      $or: [
        { email },
        { emailHash: { $in: emailHashes } }
      ]
    });
  }
  async findUserByEmailAndTenant(email, tenantId) {
    const emailHashes = emailHashLookupValues(email);
    return findOneWithDecryption(
      this.em,
      User,
      {
        tenantId,
        deletedAt: null,
        $or: [
          { email },
          { emailHash: { $in: emailHashes } }
        ]
      },
      void 0,
      { tenantId }
    );
  }
  async verifyPassword(user, password) {
    const storedHash = user?.passwordHash ?? null;
    const matched = await compare(password, storedHash ?? TIMING_EQUALIZER_PASSWORD_HASH);
    return storedHash !== null && matched;
  }
  async updateLastLoginAt(user) {
    const now = /* @__PURE__ */ new Date();
    await this.em.nativeUpdate(User, { id: user.id }, { lastLoginAt: now });
    user.lastLoginAt = now;
  }
  async getUserRoles(user, tenantId) {
    const resolvedTenantId = tenantId ?? user.tenantId ?? null;
    if (!resolvedTenantId) return [];
    const links = await findWithDecryption(
      this.em,
      UserRole,
      { user, deletedAt: null, role: { tenantId: resolvedTenantId, deletedAt: null } },
      { populate: ["role"] },
      { tenantId: resolvedTenantId, organizationId: user.organizationId ?? null }
    );
    return links.map((l) => l.role).filter((role) => !!role).map((role) => role.name).filter((name) => typeof name === "string" && name.trim().length > 0);
  }
  async createSession(user, expiresAt) {
    const rawToken = generateAuthToken();
    const tokenHash = hashAuthToken(rawToken);
    const sess = this.em.create(Session, { user, token: tokenHash, expiresAt, createdAt: /* @__PURE__ */ new Date() });
    await this.em.persist(sess).flush();
    return { session: sess, token: rawToken };
  }
  async deleteSessionByToken(token) {
    const hashedToken = hashAuthToken(token);
    await this.em.nativeDelete(Session, { token: hashedToken });
  }
  async deleteSessionById(sessionId) {
    await this.em.nativeDelete(Session, { id: sessionId });
  }
  async findActiveSessionById(sessionId) {
    const session = await this.em.findOne(Session, { id: sessionId, deletedAt: null });
    if (!session) return null;
    if (session.expiresAt.getTime() < Date.now()) return null;
    return session;
  }
  async deleteAllUserSessions(userId) {
    await this.em.nativeDelete(Session, { user: userId });
  }
  async refreshFromSessionToken(token) {
    const now = /* @__PURE__ */ new Date();
    const hashedToken = hashAuthToken(token);
    const sess = await this.em.findOne(Session, { token: hashedToken });
    if (!sess || sess.expiresAt <= now) return null;
    const user = await findOneWithDecryption(this.em, User, { id: sess.user.id, deletedAt: null });
    if (!user) return null;
    const roles = await this.getUserRoles(user, user.tenantId ?? null);
    return { user, roles, session: sess };
  }
  async requestPasswordReset(email) {
    const user = await this.findUserByEmail(email);
    if (!user) return null;
    const rawToken = generateAuthToken();
    const tokenHash = hashAuthToken(rawToken);
    const expiresAt = new Date(Date.now() + 60 * 60 * 1e3);
    const row = this.em.create(PasswordReset, { user, token: tokenHash, expiresAt, createdAt: /* @__PURE__ */ new Date() });
    await this.em.persist(row).flush();
    return { user, token: rawToken };
  }
  async confirmPasswordReset(token, newPassword) {
    const now = /* @__PURE__ */ new Date();
    const hashedToken = hashAuthToken(token);
    const row = await this.em.findOne(PasswordReset, { token: hashedToken });
    if (!row || row.usedAt && row.usedAt <= now || row.expiresAt <= now) return null;
    const affected = await this.em.nativeUpdate(
      PasswordReset,
      { id: row.id, usedAt: null },
      { usedAt: now }
    );
    if (affected === 0) return null;
    const user = await findOneWithDecryption(this.em, User, { id: row.user.id, deletedAt: null });
    if (!user) return null;
    user.passwordHash = await hash(newPassword, 10);
    await this.em.flush();
    await this.deleteAllUserSessions(String(user.id));
    return user;
  }
}
export {
  AuthService
};
//# sourceMappingURL=authService.js.map
