/**
 * Property-Based Test: Incorrect Current Password Is Always Rejected
 *
 * Feature: user-profile, Property 4: Incorrect current password is always rejected
 *
 * For any password string that does not match the user's current password,
 * the endpoint returns 401 and the stored hash remains unchanged.
 *
 * Validates: Requirements 2.3
 */

const fc = require('fast-check');
const bcrypt = require('bcryptjs');

// bcrypt cost factor — production uses 10, but we use 4 (the minimum) here
// to keep 100 iterations feasible within test timeouts. The rejection property
// holds regardless of cost factor.
const BCRYPT_COST = 4;

describe('Feature: user-profile, Property 4: Incorrect current password is always rejected', () => {
  it('bcrypt.compare rejects any wrong password and the stored hash remains unchanged', async () => {
    await fc.assert(
      fc.asyncProperty(
        // Current password: any non-empty string (bcrypt max input is 72 bytes)
        fc.string({ minLength: 1, maxLength: 72 }),
        // Wrong password: any non-empty string (will be filtered to differ from current)
        fc.string({ minLength: 1, maxLength: 72 }),
        async (currentPassword, wrongPassword) => {
          // Ensure the wrong password is always different from the current password
          fc.pre(wrongPassword !== currentPassword);

          // Step 1: Hash the current password (simulates existing stored hash)
          const currentHash = await bcrypt.hash(currentPassword, BCRYPT_COST);

          // Capture the hash before the failed attempt
          const hashBefore = currentHash;

          // Step 2: Attempt to verify the wrong password against the stored hash
          // (simulates the bcrypt.compare check in the change-password route)
          const isValid = await bcrypt.compare(wrongPassword, currentHash);

          // The wrong password must always be rejected
          expect(isValid).toBe(false);

          // Step 3: The stored hash remains unchanged after the failed attempt
          // (no mutation should occur on rejection)
          expect(currentHash).toBe(hashBefore);
        }
      ),
      { numRuns: 100 }
    );
  }, 120000); // 2-minute timeout for 100 bcrypt iterations
});
