jest.mock(
  '@librechat/data-schemas',
  () => ({
    logger: { info: jest.fn(), warn: jest.fn(), debug: jest.fn(), error: jest.fn() },
    getTenantId: jest.fn(() => undefined),
    DEFAULT_SESSION_EXPIRY: 900000,
    DEFAULT_REFRESH_TOKEN_EXPIRY: 604800000,
  }),
  { virtual: true },
);
jest.mock(
  'librechat-data-provider',
  () => ({
    ErrorTypes: {},
    SystemRoles: { USER: 'USER', ADMIN: 'ADMIN' },
    errorsToString: jest.fn(),
  }),
  { virtual: true },
);
jest.mock(
  '@librechat/api',
  () => ({
    isEnabled: jest.fn((val) => val === 'true' || val === true),
    checkEmailConfig: jest.fn(),
    isEmailDomainAllowed: jest.fn(),
    math: jest.fn((val, fallback) => (val ? Number(val) : fallback)),
    shouldUseSecureCookie: jest.fn(() => false),
    resolveAppConfigForUser: jest.fn(async (_getAppConfig, _user) => ({})),
    setCloudFrontCookies: jest.fn(() => true),
    getCloudFrontConfig: jest.fn(() => ({
      domain: 'https://cdn.example.com',
      imageSigning: 'cookies',
      cookieDomain: '.example.com',
      privateKey: 'test-private-key',
      keyPairId: 'K123ABC',
    })),
    parseCloudFrontCookieScope: jest.fn(() => null),
    CLOUDFRONT_SCOPE_COOKIE: 'LibreChat-CloudFront-Scope',
  }),
  { virtual: true },
);
jest.mock('~/models', () => ({
  findUser: jest.fn(),
  findToken: jest.fn(),
  createUser: jest.fn(),
  updateUser: jest.fn(),
  countUsers: jest.fn(),
  getUserById: jest.fn(),
  findSession: jest.fn(),
  createToken: jest.fn(),
  deleteTokens: jest.fn(),
  deleteSession: jest.fn(),
  createSession: jest.fn(),
  generateToken: jest.fn(),
  deleteUserById: jest.fn(),
  generateRefreshToken: jest.fn(),
}));
jest.mock('~/strategies/validators', () => ({
  registerSchema: {
    safeParse: jest.fn((user) => ({
      success: true,
      data: {
        name: user.name,
        username: user.username,
        email: user.email,
        password: user.password,
        confirm_password: user.confirm_password,
      },
    })),
  },
}));
jest.mock('~/server/services/Config', () => ({ getAppConfig: jest.fn() }));
jest.mock('~/server/utils', () => ({ sendEmail: jest.fn() }));

const {
  checkEmailConfig,
  shouldUseSecureCookie,
  isEmailDomainAllowed,
  resolveAppConfigForUser,
  setCloudFrontCookies,
  getCloudFrontConfig,
  parseCloudFrontCookieScope,
} = require('@librechat/api');
const jwt = require('jsonwebtoken');
const { logger, getTenantId } = require('@librechat/data-schemas');
const {
  findUser,
  findToken,
  createUser,
  updateUser,
  countUsers,
  getUserById,
  generateToken,
  generateRefreshToken,
  createSession,
  createToken,
  deleteTokens,
} = require('~/models');
const { getAppConfig } = require('~/server/services/Config');
const { sendEmail } = require('~/server/utils');
const bcrypt = require('bcryptjs');
const {
  setOpenIDAuthTokens,
  requestPasswordReset,
  registerUser,
  resetPassword,
  resendVerificationEmail,
  setAuthTokens,
  setCloudFrontAuthCookies,
  verifyEmail,
} = require('./AuthService');

/** Helper to build a mock Express response */
function mockResponse() {
  const cookies = {};
  const res = {
    cookie: jest.fn((name, value, options) => {
      cookies[name] = { value, options };
    }),
    _cookies: cookies,
  };
  return res;
}

/** Helper to build a mock Express request with session */
function mockRequest(sessionData = {}, cookies = {}) {
  return {
    session: { openidTokens: null, ...sessionData },
    cookies,
  };
}

describe('setOpenIDAuthTokens', () => {
  const env = process.env;

  beforeEach(() => {
    jest.clearAllMocks();
    process.env = {
      ...env,
      JWT_REFRESH_SECRET: 'test-refresh-secret',
      OPENID_REUSE_TOKENS: 'true',
    };
  });

  afterAll(() => {
    process.env = env;
  });

  describe('token selection (id_token vs access_token)', () => {
    it('should return id_token when both id_token and access_token are present', () => {
      const tokenset = {
        id_token: 'the-id-token',
        access_token: 'the-access-token',
        refresh_token: 'the-refresh-token',
      };
      const req = mockRequest();
      const res = mockResponse();

      const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
      expect(result).toBe('the-id-token');
    });

    it('should return access_token when id_token is not available', () => {
      const tokenset = {
        access_token: 'the-access-token',
        refresh_token: 'the-refresh-token',
      };
      const req = mockRequest();
      const res = mockResponse();

      const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
      expect(result).toBe('the-access-token');
    });

    it('should return access_token when id_token is undefined', () => {
      const tokenset = {
        id_token: undefined,
        access_token: 'the-access-token',
        refresh_token: 'the-refresh-token',
      };
      const req = mockRequest();
      const res = mockResponse();

      const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
      expect(result).toBe('the-access-token');
    });

    it('should return access_token when id_token is null', () => {
      const tokenset = {
        id_token: null,
        access_token: 'the-access-token',
        refresh_token: 'the-refresh-token',
      };
      const req = mockRequest();
      const res = mockResponse();

      const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
      expect(result).toBe('the-access-token');
    });

    it('should return id_token even when id_token and access_token differ', () => {
      const tokenset = {
        id_token: 'id-token-jwt-signed-by-idp',
        access_token: 'opaque-graph-api-token',
        refresh_token: 'refresh-token',
      };
      const req = mockRequest();
      const res = mockResponse();

      const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
      expect(result).toBe('id-token-jwt-signed-by-idp');
      expect(result).not.toBe('opaque-graph-api-token');
    });
  });

  describe('session token storage', () => {
    it('should store the original access_token in session (not id_token)', () => {
      const tokenset = {
        id_token: 'the-id-token',
        access_token: 'the-access-token',
        refresh_token: 'the-refresh-token',
      };
      const req = mockRequest();
      const res = mockResponse();

      setOpenIDAuthTokens(tokenset, req, res, 'user-123');

      expect(req.session.openidTokens.accessToken).toBe('the-access-token');
      expect(req.session.openidTokens.idToken).toBe('the-id-token');
      expect(req.session.openidTokens.refreshToken).toBe('the-refresh-token');
      expect(req.session.openidTokens.lastRefreshedAt).toEqual(expect.any(Number));
    });

    it('should return the existing unexpired session id_token when refresh omits one', () => {
      const existingIdToken = jwt.sign(
        { sub: 'user-123', exp: Math.floor(Date.now() / 1000) + 3600 },
        'idp-signing-secret',
      );
      const tokenset = {
        access_token: 'new-access-token',
        refresh_token: 'new-refresh-token',
      };
      const req = mockRequest({
        openidTokens: {
          accessToken: 'old-access-token',
          idToken: existingIdToken,
          refreshToken: 'old-refresh-token',
        },
      });
      const res = mockResponse();

      const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');

      expect(result).toBe(existingIdToken);
      expect(req.session.openidTokens.accessToken).toBe('new-access-token');
      expect(req.session.openidTokens.idToken).toBe(existingIdToken);
      expect(req.session.openidTokens.refreshToken).toBe('new-refresh-token');
      expect(req.session.openidTokens.lastRefreshedAt).toEqual(expect.any(Number));
    });

    it('should fall back to access_token when the existing session id_token is expired', () => {
      const expiredIdToken = jwt.sign(
        { sub: 'user-123', exp: Math.floor(Date.now() / 1000) - 60 },
        'idp-signing-secret',
      );
      const tokenset = {
        access_token: 'new-access-token',
        refresh_token: 'new-refresh-token',
      };
      const req = mockRequest({
        openidTokens: {
          accessToken: 'old-access-token',
          idToken: expiredIdToken,
          refreshToken: 'old-refresh-token',
        },
      });
      const res = mockResponse();

      const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');

      expect(result).toBe('new-access-token');
      expect(req.session.openidTokens.idToken).toBe(expiredIdToken);
      expect(req.session.openidTokens.accessToken).toBe('new-access-token');
    });

    it('should fall back to access_token when the existing session id_token is near expiry', () => {
      const nearExpiryIdToken = jwt.sign(
        { sub: 'user-123', exp: Math.floor(Date.now() / 1000) + 10 },
        'idp-signing-secret',
      );
      const tokenset = {
        access_token: 'new-access-token',
        refresh_token: 'new-refresh-token',
      };
      const req = mockRequest({
        openidTokens: {
          accessToken: 'old-access-token',
          idToken: nearExpiryIdToken,
          refreshToken: 'old-refresh-token',
        },
      });
      const res = mockResponse();

      const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');

      expect(result).toBe('new-access-token');
      expect(req.session.openidTokens.idToken).toBe(nearExpiryIdToken);
      expect(req.session.openidTokens.accessToken).toBe('new-access-token');
    });
  });

  describe('cookie secure flag', () => {
    it('should call shouldUseSecureCookie for every cookie set', () => {
      const tokenset = {
        id_token: 'the-id-token',
        access_token: 'the-access-token',
        refresh_token: 'the-refresh-token',
      };
      const req = mockRequest();
      const res = mockResponse();

      setOpenIDAuthTokens(tokenset, req, res, 'user-123');

      // token_provider + openid_user_id (session path, so no refreshToken/openid_access_token cookies)
      const secureCalls = shouldUseSecureCookie.mock.calls.length;
      expect(secureCalls).toBeGreaterThanOrEqual(2);

      // Verify all cookies use the result of shouldUseSecureCookie
      for (const [, cookie] of Object.entries(res._cookies)) {
        expect(cookie.options.secure).toBe(false);
      }
    });

    it('should set secure: true when shouldUseSecureCookie returns true', () => {
      shouldUseSecureCookie.mockReturnValue(true);

      const tokenset = {
        id_token: 'the-id-token',
        access_token: 'the-access-token',
        refresh_token: 'the-refresh-token',
      };
      const req = mockRequest();
      const res = mockResponse();

      setOpenIDAuthTokens(tokenset, req, res, 'user-123');

      for (const [, cookie] of Object.entries(res._cookies)) {
        expect(cookie.options.secure).toBe(true);
      }
    });

    it('should use shouldUseSecureCookie for cookie fallback path (no session)', () => {
      shouldUseSecureCookie.mockReturnValue(false);

      const tokenset = {
        id_token: 'the-id-token',
        access_token: 'the-access-token',
        refresh_token: 'the-refresh-token',
      };
      const req = { session: null };
      const res = mockResponse();

      setOpenIDAuthTokens(tokenset, req, res, 'user-123');

      // In the cookie fallback path, we get: refreshToken, openid_access_token, token_provider, openid_user_id
      expect(res.cookie).toHaveBeenCalledWith(
        'refreshToken',
        expect.any(String),
        expect.objectContaining({ secure: false }),
      );
      expect(res.cookie).toHaveBeenCalledWith(
        'openid_access_token',
        expect.any(String),
        expect.objectContaining({ secure: false }),
      );
      expect(res.cookie).toHaveBeenCalledWith(
        'token_provider',
        'openid',
        expect.objectContaining({ secure: false }),
      );
    });
  });

  describe('edge cases', () => {
    it('should return undefined when tokenset is null', () => {
      const req = mockRequest();
      const res = mockResponse();
      const result = setOpenIDAuthTokens(null, req, res, 'user-123');
      expect(result).toBeUndefined();
    });

    it('should return undefined when access_token is missing', () => {
      const tokenset = { refresh_token: 'refresh' };
      const req = mockRequest();
      const res = mockResponse();
      const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
      expect(result).toBeUndefined();
    });

    it('should return undefined when no refresh token is available', () => {
      const tokenset = { access_token: 'access', id_token: 'id' };
      const req = mockRequest();
      const res = mockResponse();
      const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123');
      expect(result).toBeUndefined();
    });

    it('should use existingRefreshToken when tokenset has no refresh_token', () => {
      const tokenset = {
        id_token: 'the-id-token',
        access_token: 'the-access-token',
      };
      const req = mockRequest();
      const res = mockResponse();

      const result = setOpenIDAuthTokens(tokenset, req, res, 'user-123', 'existing-refresh');
      expect(result).toBe('the-id-token');
      expect(req.session.openidTokens.refreshToken).toBe('existing-refresh');
    });
  });
});

describe('registerUser', () => {
  const registrationPayload = {
    name: 'Test User',
    username: 'testuser',
    email: 'test@example.com',
    password: 'Password123!',
    confirm_password: 'Password123!',
  };

  beforeEach(() => {
    jest.clearAllMocks();
    process.env.ALLOW_UNVERIFIED_EMAIL_LOGIN = 'false';
    checkEmailConfig.mockReturnValue(false);
    isEmailDomainAllowed.mockReturnValue(true);
    getAppConfig.mockResolvedValue({
      balance: { enabled: false },
      registration: { allowedDomains: [] },
    });
    findUser.mockResolvedValue(null);
    countUsers.mockResolvedValue(1);
    createUser.mockResolvedValue({ _id: 'new-user-id' });
    updateUser.mockResolvedValue({ _id: 'new-user-id' });
  });

  it('ignores provider values from the public registration payload', async () => {
    const result = await registerUser({ ...registrationPayload, provider: 'google' });

    expect(result.status).toBe(200);
    expect(createUser.mock.calls[0][0]).toEqual(
      expect.objectContaining({
        email: registrationPayload.email,
        provider: 'local',
      }),
    );
  });

  it('allows trusted callers to set provider through additional data', async () => {
    const result = await registerUser(registrationPayload, {
      emailVerified: true,
      provider: 'google',
    });

    expect(result.status).toBe(200);
    expect(createUser.mock.calls[0][0]).toEqual(
      expect.objectContaining({
        email: registrationPayload.email,
        emailVerified: true,
        provider: 'google',
      }),
    );
  });
});

describe('verifyEmail public response handling', () => {
  const email = 'user@example.com';
  const encodedEmail = encodeURIComponent(email);
  const invalidEmailVerificationMessage = 'Invalid or expired email verification token';

  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('does not reveal that an account is already verified without a valid token', async () => {
    findUser.mockResolvedValue({ _id: 'user-id', email, emailVerified: true });
    findToken.mockResolvedValue(null);

    const result = await verifyEmail({ body: { email: encodedEmail, token: 'not-the-token' } });

    expect(result).toBeInstanceOf(Error);
    expect(result.message).toBe(invalidEmailVerificationMessage);
    expect(updateUser).not.toHaveBeenCalled();
    expect(deleteTokens).not.toHaveBeenCalled();
  });

  it('returns the same generic error for missing users and invalid tokens', async () => {
    findUser.mockResolvedValueOnce(null);

    const missingUserResult = await verifyEmail({
      body: { email: encodedEmail, token: 'not-the-token' },
    });

    findUser.mockResolvedValueOnce({ _id: 'user-id', email, emailVerified: false });
    findToken.mockResolvedValueOnce({
      userId: 'user-id',
      email,
      token: bcrypt.hashSync('real-token', 10),
    });

    const invalidTokenResult = await verifyEmail({
      body: { email: encodedEmail, token: 'not-the-token' },
    });

    expect(missingUserResult).toBeInstanceOf(Error);
    expect(invalidTokenResult).toBeInstanceOf(Error);
    expect(missingUserResult.message).toBe(invalidEmailVerificationMessage);
    expect(invalidTokenResult.message).toBe(invalidEmailVerificationMessage);
  });

  it('verifies an unverified account when the token is valid', async () => {
    const hashedToken = bcrypt.hashSync('real-token', 10);
    findUser.mockResolvedValue({ _id: 'user-id', email, emailVerified: false });
    findToken.mockResolvedValue({ userId: 'user-id', email, token: hashedToken });
    updateUser.mockResolvedValue({ _id: 'user-id', emailVerified: true });

    const result = await verifyEmail({ body: { email: encodedEmail, token: 'real-token' } });

    expect(result).toEqual({
      message: 'Email verification was successful',
      status: 'success',
    });
    expect(updateUser).toHaveBeenCalledWith('user-id', { emailVerified: true });
    expect(deleteTokens).toHaveBeenCalledWith({
      token: hashedToken,
      userId: 'user-id',
      email,
      identifier: null,
      type: null,
    });
  });

  it('returns the generic error when a valid verification update fails', async () => {
    const hashedToken = bcrypt.hashSync('real-token', 10);
    findUser.mockResolvedValue({ _id: 'user-id', email, emailVerified: false });
    findToken.mockResolvedValue({ userId: 'user-id', email, token: hashedToken });
    updateUser.mockResolvedValue(null);

    const result = await verifyEmail({ body: { email: encodedEmail, token: 'real-token' } });

    expect(result).toBeInstanceOf(Error);
    expect(result.message).toBe(invalidEmailVerificationMessage);
    expect(deleteTokens).not.toHaveBeenCalled();
  });

  it('allows idempotent success only when an already verified account presents a valid token', async () => {
    const hashedToken = bcrypt.hashSync('real-token', 10);
    findUser.mockResolvedValue({ _id: 'user-id', email, emailVerified: true });
    findToken.mockResolvedValue({ userId: 'user-id', email, token: hashedToken });

    const result = await verifyEmail({ body: { email: encodedEmail, token: 'real-token' } });

    expect(result).toEqual({
      message: 'Email verification was successful',
      status: 'success',
    });
    expect(updateUser).not.toHaveBeenCalled();
    expect(deleteTokens).toHaveBeenCalledWith({
      token: hashedToken,
      userId: 'user-id',
      email,
      identifier: null,
      type: null,
    });
  });
});

describe('requestPasswordReset', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    isEmailDomainAllowed.mockReturnValue(true);
    getAppConfig.mockResolvedValue({
      registration: { allowedDomains: ['example.com'] },
    });
    resolveAppConfigForUser.mockResolvedValue({
      registration: { allowedDomains: ['example.com'] },
    });
  });

  it('should fast-fail with base config before DB lookup for blocked domains', async () => {
    isEmailDomainAllowed.mockReturnValue(false);

    const req = { body: { email: 'blocked@evil.com' }, ip: '127.0.0.1' };
    const result = await requestPasswordReset(req);

    expect(getAppConfig).toHaveBeenCalledWith({ baseOnly: true });
    expect(findUser).not.toHaveBeenCalled();
    expect(result).toBeInstanceOf(Error);
  });

  it('should call resolveAppConfigForUser for tenant user', async () => {
    const user = {
      _id: 'user-tenant',
      email: 'user@example.com',
      tenantId: 'tenant-x',
      role: 'USER',
    };
    findUser.mockResolvedValue(user);

    const req = { body: { email: 'user@example.com' }, ip: '127.0.0.1' };
    await requestPasswordReset(req);

    expect(resolveAppConfigForUser).toHaveBeenCalledWith(getAppConfig, user);
  });

  it('should reuse baseConfig for non-tenant user without calling resolveAppConfigForUser', async () => {
    findUser.mockResolvedValue({ _id: 'user-no-tenant', email: 'user@example.com' });

    const req = { body: { email: 'user@example.com' }, ip: '127.0.0.1' };
    await requestPasswordReset(req);

    expect(resolveAppConfigForUser).not.toHaveBeenCalled();
  });

  it('should return generic response when tenant config blocks the domain (non-enumerable)', async () => {
    const user = {
      _id: 'user-tenant',
      email: 'user@example.com',
      tenantId: 'tenant-x',
      role: 'USER',
    };
    findUser.mockResolvedValue(user);
    isEmailDomainAllowed.mockReturnValueOnce(true).mockReturnValueOnce(false);

    const req = { body: { email: 'user@example.com' }, ip: '127.0.0.1' };
    const result = await requestPasswordReset(req);

    expect(result).not.toBeInstanceOf(Error);
    expect(result.message).toContain('If an account with that email exists');
  });

  it('should only delete existing password reset tokens when issuing a new reset link', async () => {
    const user = { _id: 'user-reset', email: 'user@example.com' };
    findUser.mockResolvedValue(user);

    const req = { body: { email: 'user@example.com' }, ip: '127.0.0.1' };
    await requestPasswordReset(req);

    expect(deleteTokens).toHaveBeenCalledWith({
      userId: user._id,
      type: 'password_reset',
    });
    expect(deleteTokens).toHaveBeenCalledWith({
      userId: user._id,
      email: null,
      identifier: null,
      type: null,
    });
    expect(createToken).toHaveBeenCalledWith(
      expect.objectContaining({
        userId: user._id,
        type: 'password_reset',
      }),
    );
  });
});

describe('resetPassword', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    checkEmailConfig.mockReturnValue(false);
  });

  it('should only accept password reset tokens for password reset', async () => {
    const verificationHash = bcrypt.hashSync('verification-token', 10);
    findToken.mockImplementation(async (query) => {
      if (query.type === 'password_reset') {
        return null;
      }
      if (query.type === null && query.email === null && query.identifier === null) {
        return null;
      }
      return { token: verificationHash, userId: 'user-reset', email: 'user@example.com' };
    });
    updateUser.mockResolvedValue({ email: 'user@example.com' });

    const result = await resetPassword('user-reset', 'verification-token', 'new-password');

    expect(result).toBeInstanceOf(Error);
    expect(findToken).toHaveBeenCalledWith(
      {
        userId: 'user-reset',
        type: 'password_reset',
      },
      { sort: { createdAt: -1 } },
    );
    expect(findToken).toHaveBeenCalledWith(
      {
        userId: 'user-reset',
        email: null,
        identifier: null,
        type: null,
      },
      { sort: { createdAt: -1 } },
    );
    expect(updateUser).not.toHaveBeenCalled();
    expect(deleteTokens).not.toHaveBeenCalled();
  });

  it('should delete only the used password reset token after a successful reset', async () => {
    const resetHash = bcrypt.hashSync('reset-token', 10);
    findToken.mockResolvedValue({
      token: resetHash,
      userId: 'user-reset',
      type: 'password_reset',
    });
    updateUser.mockResolvedValue({ email: 'user@example.com' });

    const result = await resetPassword('user-reset', 'reset-token', 'new-password');

    expect(result).toEqual({ message: 'Password reset was successful' });
    expect(findToken).toHaveBeenCalledWith(
      {
        userId: 'user-reset',
        type: 'password_reset',
      },
      { sort: { createdAt: -1 } },
    );
    expect(deleteTokens).toHaveBeenCalledWith({
      token: resetHash,
      type: 'password_reset',
    });
  });

  it('should accept legacy reset tokens without affecting verification-shaped tokens', async () => {
    const legacyResetHash = bcrypt.hashSync('legacy-reset-token', 10);
    findToken.mockImplementation(async (query) => {
      if (query.type === 'password_reset') {
        return null;
      }
      if (query.type === null && query.email === null && query.identifier === null) {
        return {
          token: legacyResetHash,
          userId: 'user-reset',
        };
      }
      return null;
    });
    updateUser.mockResolvedValue({ email: 'user@example.com' });

    const result = await resetPassword('user-reset', 'legacy-reset-token', 'new-password');

    expect(result).toEqual({ message: 'Password reset was successful' });
    expect(findToken).toHaveBeenCalledWith(
      {
        userId: 'user-reset',
        type: 'password_reset',
      },
      { sort: { createdAt: -1 } },
    );
    expect(findToken).toHaveBeenCalledWith(
      {
        userId: 'user-reset',
        email: null,
        identifier: null,
        type: null,
      },
      { sort: { createdAt: -1 } },
    );
    expect(deleteTokens).toHaveBeenCalledWith({
      token: legacyResetHash,
      email: null,
      identifier: null,
      type: null,
    });
  });
});

describe('verifyEmail', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should scope verification token lookup to the user and token category', async () => {
    const verificationHash = bcrypt.hashSync('verification-token', 10);
    const user = {
      _id: 'user-verify',
      email: 'user@example.com',
      emailVerified: false,
    };
    findUser.mockResolvedValue(user);
    findToken.mockImplementation(async (query) => {
      if (query.type === 'email_verification') {
        return {
          userId: user._id,
          email: user.email,
          token: verificationHash,
          type: 'email_verification',
        };
      }
      return null;
    });
    updateUser.mockResolvedValue({ ...user, emailVerified: true });

    const result = await verifyEmail({
      body: {
        email: encodeURIComponent(user.email),
        token: 'verification-token',
      },
    });

    expect(result).toEqual({
      message: 'Email verification was successful',
      status: 'success',
    });
    expect(findToken).toHaveBeenCalledWith(
      {
        userId: user._id,
        email: user.email,
        type: 'email_verification',
      },
      { sort: { createdAt: -1 } },
    );
    expect(deleteTokens).toHaveBeenCalledWith({
      token: verificationHash,
      type: 'email_verification',
    });
  });

  it('should fall back only to legacy verification tokens for the same user', async () => {
    const verificationHash = bcrypt.hashSync('legacy-verification-token', 10);
    const user = {
      _id: 'user-verify',
      email: 'user@example.com',
      emailVerified: false,
    };
    findUser.mockResolvedValue(user);
    findToken.mockImplementation(async (query) => {
      if (query.type === 'email_verification') {
        return null;
      }
      if (query.type === null && query.identifier === null && query.userId === user._id) {
        return {
          userId: user._id,
          email: user.email,
          token: verificationHash,
        };
      }
      return null;
    });
    updateUser.mockResolvedValue({ ...user, emailVerified: true });

    const result = await verifyEmail({
      body: {
        email: encodeURIComponent(user.email),
        token: 'legacy-verification-token',
      },
    });

    expect(result).toEqual({
      message: 'Email verification was successful',
      status: 'success',
    });
    expect(findToken).toHaveBeenCalledWith(
      {
        userId: user._id,
        email: user.email,
        identifier: null,
        type: null,
      },
      { sort: { createdAt: -1 } },
    );
    expect(deleteTokens).toHaveBeenCalledWith({
      token: verificationHash,
      userId: user._id,
      email: user.email,
      identifier: null,
      type: null,
    });
  });
});

describe('resendVerificationEmail', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should not delete tokens when no user exists for the email', async () => {
    findUser.mockResolvedValue(null);

    const result = await resendVerificationEmail({
      body: { email: 'missing@example.com' },
    });

    expect(result).toEqual({
      status: 200,
      message: 'Please check your email to verify your email address.',
    });
    expect(deleteTokens).not.toHaveBeenCalled();
    expect(sendEmail).not.toHaveBeenCalled();
    expect(createToken).not.toHaveBeenCalled();
  });

  it('should delete only verification tokens scoped to the resolved user', async () => {
    const user = {
      _id: 'user-verify',
      email: 'user@example.com',
      name: 'User Verify',
    };
    findUser.mockResolvedValue(user);

    const result = await resendVerificationEmail({
      body: { email: user.email },
    });

    expect(result).toEqual({
      status: 200,
      message: 'Please check your email to verify your email address.',
    });
    expect(deleteTokens).toHaveBeenCalledWith({
      userId: user._id,
      email: user.email,
      type: 'email_verification',
    });
    expect(deleteTokens).toHaveBeenCalledWith({
      userId: user._id,
      email: user.email,
      identifier: null,
      type: null,
    });
    expect(deleteTokens).not.toHaveBeenCalledWith({ email: user.email });
    expect(createToken).toHaveBeenCalledWith(
      expect.objectContaining({
        userId: user._id,
        email: user.email,
        type: 'email_verification',
      }),
    );
  });
});

describe('CloudFront cookie integration', () => {
  const cloudFrontCookieConfig = {
    domain: 'https://cdn.example.com',
    imageSigning: 'cookies',
    cookieDomain: '.example.com',
    privateKey: 'test-private-key',
    keyPairId: 'K123ABC',
  };

  beforeEach(() => {
    jest.clearAllMocks();
    getCloudFrontConfig.mockReturnValue(cloudFrontCookieConfig);
    setCloudFrontCookies.mockReturnValue(true);
    parseCloudFrontCookieScope.mockReturnValue(null);
  });

  describe('setCloudFrontAuthCookies', () => {
    it('passes user id and tenant scope from the user', () => {
      const req = mockRequest();
      const res = mockResponse();
      const user = {
        _id: { toString: () => 'user-123' },
        tenantId: { toString: () => 'tenantA' },
      };

      const result = setCloudFrontAuthCookies(req, res, user);

      expect(result).toBe(true);
      expect(setCloudFrontCookies).toHaveBeenCalledWith(
        res,
        {
          userId: 'user-123',
          tenantId: 'tenantA',
        },
        null,
      );
      expect(logger.debug).toHaveBeenCalledWith(
        '[setCloudFrontAuthCookies] CloudFront auth cookies refreshed',
        expect.objectContaining({
          attempted: true,
          set: true,
          has_user_id: true,
          has_tenant_scope: true,
        }),
      );
    });

    it('lets explicit scope options override user and request scope', () => {
      const req = mockRequest();
      req.user = { _id: 'request-user', tenantId: 'request-tenant' };
      const res = mockResponse();
      const user = { _id: 'user-123', tenantId: 'tenantA' };

      setCloudFrontAuthCookies(req, res, user, {
        userId: 'option-user',
        tenantId: 'option-tenant',
        storageRegion: 'us-east-2',
      });

      expect(setCloudFrontCookies).toHaveBeenCalledWith(
        res,
        {
          userId: 'option-user',
          tenantId: 'option-tenant',
          storageRegion: 'us-east-2',
        },
        null,
      );
    });

    it('falls back to request tenant scope when the user has none', () => {
      const req = mockRequest();
      req.user = { tenantId: 'request-tenant' };
      const res = mockResponse();

      setCloudFrontAuthCookies(req, res, { _id: 'user-123' });

      expect(setCloudFrontCookies).toHaveBeenCalledWith(
        res,
        {
          userId: 'user-123',
          tenantId: 'request-tenant',
        },
        null,
      );
    });

    it('uses org scope as tenant scope when tenantId is unavailable', () => {
      const req = mockRequest();
      const res = mockResponse();

      setCloudFrontAuthCookies(req, res, { _id: 'user-123', orgId: 'orgA' });

      expect(setCloudFrontCookies).toHaveBeenCalledWith(
        res,
        {
          userId: 'user-123',
          tenantId: 'orgA',
        },
        null,
      );
    });

    it('uses previous CloudFront scope for stale cookie cleanup', () => {
      parseCloudFrontCookieScope.mockReturnValue({ userId: 'old-user', tenantId: 'old-tenant' });
      const req = mockRequest({}, { 'LibreChat-CloudFront-Scope': 'encoded-scope' });
      const res = mockResponse();

      setCloudFrontAuthCookies(req, res, { _id: 'user-123', tenantId: 'tenantA' });

      expect(parseCloudFrontCookieScope).toHaveBeenCalledWith('encoded-scope');
      expect(setCloudFrontCookies).toHaveBeenCalledWith(
        res,
        {
          userId: 'user-123',
          tenantId: 'tenantA',
        },
        { userId: 'old-user', tenantId: 'old-tenant' },
      );
    });

    it('no-ops when CloudFront cookie signing is disabled', () => {
      getCloudFrontConfig.mockReturnValue({ ...cloudFrontCookieConfig, imageSigning: 'none' });
      const req = mockRequest();
      const res = mockResponse();

      const result = setCloudFrontAuthCookies(req, res, { _id: 'user-123' });

      expect(result).toBe(false);
      expect(setCloudFrontCookies).not.toHaveBeenCalled();
      expect(logger.debug).not.toHaveBeenCalledWith(
        '[setCloudFrontAuthCookies] CloudFront auth cookies skipped',
        expect.any(Object),
      );
    });

    it('fails closed when user id is missing', () => {
      const req = mockRequest();
      const res = mockResponse();

      const result = setCloudFrontAuthCookies(req, res, { tenantId: 'tenantA' });

      expect(result).toBe(false);
      expect(setCloudFrontCookies).not.toHaveBeenCalled();
      expect(logger.debug).toHaveBeenCalledWith(
        '[setCloudFrontAuthCookies] CloudFront auth cookies skipped',
        expect.objectContaining({
          attempted: false,
          set: false,
          reason: 'missing_user_id',
        }),
      );
    });

    it('skips when CloudFront cookie domain is missing', () => {
      getCloudFrontConfig.mockReturnValue({ ...cloudFrontCookieConfig, cookieDomain: null });
      const req = mockRequest();
      const res = mockResponse();

      const result = setCloudFrontAuthCookies(req, res, { _id: 'user-123' });

      expect(result).toBe(false);
      expect(setCloudFrontCookies).not.toHaveBeenCalled();
      expect(logger.debug).toHaveBeenCalledWith(
        '[setCloudFrontAuthCookies] CloudFront auth cookies skipped',
        expect.objectContaining({
          attempted: false,
          set: false,
          reason: 'missing_cookie_domain',
        }),
      );
    });

    it('does not log cookie secrets or signed-cookie values', () => {
      const req = mockRequest();
      const res = mockResponse();

      setCloudFrontAuthCookies(req, res, { _id: 'user-123' });

      const debugOutput = JSON.stringify(logger.debug.mock.calls);
      expect(debugOutput).not.toContain('test-private-key');
      expect(debugOutput).not.toContain('K123ABC');
      expect(debugOutput).not.toContain('CloudFront-Policy');
      expect(debugOutput).not.toContain('CloudFront-Signature');
      expect(debugOutput).not.toContain('CloudFront-Key-Pair-Id');
    });
  });

  describe('setOpenIDAuthTokens', () => {
    const validTokenset = {
      id_token: 'the-id-token',
      access_token: 'the-access-token',
      refresh_token: 'the-refresh-token',
    };

    it('calls setCloudFrontCookies with response object and user scope from options', () => {
      const req = mockRequest();
      const res = mockResponse();

      setOpenIDAuthTokens(validTokenset, req, res, {
        userId: 'user-123',
        tenantId: 'tenantA',
      });

      expect(setCloudFrontCookies).toHaveBeenCalledWith(
        res,
        {
          userId: 'user-123',
          tenantId: 'tenantA',
        },
        null,
      );
    });

    it('keeps backward compatibility with positional user and tenant params', () => {
      const req = mockRequest();
      const res = mockResponse();

      setOpenIDAuthTokens(validTokenset, req, res, 'user-123', undefined, 'tenantA');

      expect(setCloudFrontCookies).toHaveBeenCalledWith(
        res,
        {
          userId: 'user-123',
          tenantId: 'tenantA',
        },
        null,
      );
    });

    it('treats a null options argument as an empty legacy user id', () => {
      const req = mockRequest();
      const res = mockResponse();

      const result = setOpenIDAuthTokens(validTokenset, req, res, null);

      expect(result).toBe('the-id-token');
      expect(setCloudFrontCookies).not.toHaveBeenCalled();
      expect(logger.debug).toHaveBeenCalledWith(
        '[setCloudFrontAuthCookies] CloudFront auth cookies skipped',
        expect.objectContaining({
          attempted: false,
          set: false,
          reason: 'missing_user_id',
        }),
      );
    });

    it('treats omitted options as an empty legacy user id', () => {
      const req = mockRequest();
      const res = mockResponse();

      const result = setOpenIDAuthTokens(validTokenset, req, res);

      expect(result).toBe('the-id-token');
      expect(setCloudFrontCookies).not.toHaveBeenCalled();
      expect(logger.debug).toHaveBeenCalledWith(
        '[setCloudFrontAuthCookies] CloudFront auth cookies skipped',
        expect.objectContaining({
          attempted: false,
          set: false,
          reason: 'missing_user_id',
        }),
      );
    });

    it('treats an object without token option keys as empty options', () => {
      const req = mockRequest();
      const res = mockResponse();

      const result = setOpenIDAuthTokens(validTokenset, req, res, {});

      expect(result).toBe('the-id-token');
      expect(setCloudFrontCookies).not.toHaveBeenCalled();
      expect(logger.debug).toHaveBeenCalledWith(
        '[setCloudFrontAuthCookies] CloudFront auth cookies skipped',
        expect.objectContaining({
          attempted: false,
          set: false,
          reason: 'missing_user_id',
        }),
      );
    });

    it('succeeds even when setCloudFrontCookies returns false', () => {
      setCloudFrontCookies.mockReturnValue(false);

      const req = mockRequest();
      const res = mockResponse();

      const result = setOpenIDAuthTokens(validTokenset, req, res, 'user-123');

      expect(result).toBe('the-id-token');
    });
  });

  describe('setAuthTokens', () => {
    beforeEach(() => {
      getUserById.mockResolvedValue({ _id: 'user-123', tenantId: 'tenantA' });
      generateToken.mockResolvedValue('mock-access-token');
      generateRefreshToken.mockReturnValue('mock-refresh-token');
      createSession.mockResolvedValue({
        session: { expiration: new Date(Date.now() + 604800000) },
        refreshToken: 'mock-refresh-token',
      });
    });

    it('calls setCloudFrontCookies with response object and user scope', async () => {
      const res = mockResponse();

      await setAuthTokens('user-123', res);

      expect(setCloudFrontCookies).toHaveBeenCalledWith(
        res,
        {
          userId: 'user-123',
          tenantId: 'tenantA',
        },
        null,
      );
    });

    it('uses the fetched user id as the canonical CloudFront user scope', async () => {
      getUserById.mockResolvedValueOnce({
        _id: { toString: () => 'canonical-user' },
        tenantId: 'tenantA',
      });
      const res = mockResponse();

      await setAuthTokens('input-user-id', res);

      expect(setCloudFrontCookies).toHaveBeenCalledWith(
        res,
        {
          userId: 'canonical-user',
          tenantId: 'tenantA',
        },
        null,
      );
    });

    it('passes the previous CloudFront cookie scope when present', async () => {
      parseCloudFrontCookieScope.mockReturnValue({ userId: 'old-user', tenantId: 'old-tenant' });
      const res = mockResponse();
      const req = mockRequest({}, { 'LibreChat-CloudFront-Scope': 'encoded-scope' });

      await setAuthTokens('user-123', res, null, req);

      expect(parseCloudFrontCookieScope).toHaveBeenCalledWith('encoded-scope');
      expect(setCloudFrontCookies).toHaveBeenCalledWith(
        res,
        {
          userId: 'user-123',
          tenantId: 'tenantA',
        },
        { userId: 'old-user', tenantId: 'old-tenant' },
      );
    });

    it('succeeds even when setCloudFrontCookies returns false', async () => {
      setCloudFrontCookies.mockReturnValue(false);

      const res = mockResponse();

      const result = await setAuthTokens('user-123', res);

      expect(result).toBe('mock-access-token');
    });
  });
});

describe('registerUser - allowedDomains admin-panel override', () => {
  const validUser = {
    email: 'new-user@example.com',
    password: 'a-secure-password',
    name: 'New User',
    username: 'new-user',
  };

  beforeEach(() => {
    jest.clearAllMocks();
    getTenantId.mockReturnValue(undefined);
    isEmailDomainAllowed.mockReturnValue(true);
    getAppConfig.mockResolvedValue({
      registration: { allowedDomains: ['example.com'] },
      balance: undefined,
    });
    findUser.mockResolvedValue(null);
    countUsers.mockResolvedValue(0);
  });

  it('should resolve the full app config so admin-panel overrides on the __base__ principal apply', async () => {
    // Regression guard for getAppConfig({ baseOnly: true }): that option short-circuits
    // before the DB override merge, which silently ignores any admin-panel edits to
    // registration.allowedDomains (the admin panel writes overrides to the __base__
    // principal in the configs collection). registerUser must request the merged config
    // so the global __base__ override is honored, same as it is for SSO callbacks via
    // checkDomainAllowed.
    await registerUser(validUser);

    expect(getAppConfig).toHaveBeenCalledTimes(1);
    expect(getAppConfig).toHaveBeenCalledWith({});
    expect(getAppConfig).not.toHaveBeenCalledWith(expect.objectContaining({ baseOnly: true }));
  });

  it('should pass tenantId from ALS so the merged-config cache key matches tenant-scoped DB queries', async () => {
    // /api/auth runs through preAuthTenantMiddleware, which puts a tenantId into
    // AsyncLocalStorage. Mongoose queries inside getApplicableConfigs are scoped by ALS,
    // but the per-principal merged-config cache key uses the explicit tenantId param.
    // If we don't forward the ALS tenantId, tenant A's request caches at `__default__`
    // and a later tenant B request can hit that entry — leaking config across tenants.
    getTenantId.mockReturnValue('tenant-x');

    await registerUser(validUser);

    expect(getAppConfig).toHaveBeenCalledWith({ tenantId: 'tenant-x' });
  });

  it('should block registration when the resolved allowedDomains rejects the email', async () => {
    isEmailDomainAllowed.mockReturnValue(false);

    const result = await registerUser({ ...validUser, email: 'blocked@evil.com' });

    expect(result.status).toBe(403);
    expect(result.message).toMatch(/cannot be used/i);
    // Domain check must happen before any DB user lookup.
    expect(findUser).not.toHaveBeenCalled();
  });
});
