// Mock express-validator
const mockValidationResult = jest.fn();
const mockBody = jest.fn();

jest.mock('express-validator', () => ({
  body: jest.fn(() => ({
    isEmail: jest.fn().mockReturnThis(),
    normalizeEmail: jest.fn().mockReturnThis(),
    withMessage: jest.fn().mockReturnThis(),
    isLength: jest.fn().mockReturnThis(),
    matches: jest.fn().mockReturnThis(),
    custom: jest.fn().mockReturnThis(),
    trim: jest.fn().mockReturnThis(),
    optional: jest.fn().mockReturnThis(),
    isMobilePhone: jest.fn().mockReturnThis(),
    notEmpty: jest.fn().mockReturnThis()
  })),
  validationResult: jest.fn()
}));

// Mock DOMPurify
const mockSanitize = jest.fn();
jest.mock('dompurify', () => jest.fn(() => ({
  sanitize: mockSanitize
})));

// Mock JSDOM
jest.mock('jsdom', () => ({
  JSDOM: jest.fn(() => ({
    window: {}
  }))
}));

const {
  sanitizeInput,
  handleValidationErrors,
  validateRegistration,
  validateLogin,
  validateGoogleAuth,
  validateProfileUpdate,
  validatePasswordChange
} = require('../../../middleware/validation');

describe('Validation Middleware', () => {
  let req, res, next;

  beforeEach(() => {
    req = {
      body: {},
      query: {},
      params: {}
    };
    res = {
      status: jest.fn().mockReturnThis(),
      json: jest.fn()
    };
    next = jest.fn();

    // Reset mocks
    jest.clearAllMocks();

    // Set up mock sanitize function default behavior
    mockSanitize.mockImplementation((value) => value); // Default passthrough

    mockValidationResult.mockReturnValue({ isEmpty: jest.fn(() => true), array: jest.fn(() => []) });

    const { validationResult } = require('express-validator');
    validationResult.mockImplementation(mockValidationResult);
  });

  describe('sanitizeInput', () => {
    describe('String sanitization', () => {
      it('should sanitize string values in req.body', () => {
        req.body = {
          name: '<script>alert("xss")</script>John',
          message: '<b>Hello</b> World'
        };
        mockSanitize
          .mockReturnValueOnce('John')
          .mockReturnValueOnce('Hello World');

        sanitizeInput(req, res, next);

        expect(mockSanitize).toHaveBeenCalledWith('<script>alert("xss")</script>John', { ALLOWED_TAGS: [] });
        expect(mockSanitize).toHaveBeenCalledWith('<b>Hello</b> World', { ALLOWED_TAGS: [] });
        expect(req.body).toEqual({
          name: 'John',
          message: 'Hello World'
        });
        expect(next).toHaveBeenCalled();
      });

      it('should sanitize string values in req.query', () => {
        req.query = {
          search: '<img src="x" onerror="alert(1)">test',
          filter: 'normal text'
        };
        mockSanitize
          .mockReturnValueOnce('test')
          .mockReturnValueOnce('normal text');

        sanitizeInput(req, res, next);

        expect(mockSanitize).toHaveBeenCalledWith('<img src="x" onerror="alert(1)">test', { ALLOWED_TAGS: [] });
        expect(req.query).toEqual({
          search: 'test',
          filter: 'normal text'
        });
      });

      it('should sanitize string values in req.params', () => {
        req.params = {
          id: '<script>malicious</script>123',
          slug: 'safe-slug'
        };
        mockSanitize
          .mockReturnValueOnce('123')
          .mockReturnValueOnce('safe-slug');

        sanitizeInput(req, res, next);

        expect(mockSanitize).toHaveBeenCalledWith('<script>malicious</script>123', { ALLOWED_TAGS: [] });
        expect(req.params).toEqual({
          id: '123',
          slug: 'safe-slug'
        });
      });
    });

    describe('Object sanitization', () => {
      it('should recursively sanitize nested objects', () => {
        req.body = {
          user: {
            name: '<script>alert("nested")</script>John',
            profile: {
              bio: '<b>Bold</b> text'
            }
          },
          tags: ['<em>tag1</em>', 'tag2']
        };
        mockSanitize
          .mockReturnValueOnce('John')
          .mockReturnValueOnce('Bold text')
          .mockReturnValueOnce('tag1')
          .mockReturnValueOnce('tag2');

        sanitizeInput(req, res, next);

        expect(req.body.user.name).toBe('John');
        expect(req.body.user.profile.bio).toBe('Bold text');
        expect(req.body.tags['0']).toBe('tag1');
        expect(req.body.tags['1']).toBe('tag2');
      });

      it('should handle arrays within objects', () => {
        req.body = {
          items: [
            { name: '<script>test</script>Item1' },
            { name: 'Item2' }
          ]
        };
        mockSanitize
          .mockReturnValueOnce('Item1')
          .mockReturnValueOnce('Item2');

        sanitizeInput(req, res, next);

        expect(req.body.items[0].name).toBe('Item1');
        expect(req.body.items[1].name).toBe('Item2');
      });
    });

    describe('Non-string values', () => {
      it('should preserve numbers', () => {
        req.body = {
          age: 25,
          price: 99.99,
          count: 0
        };

        sanitizeInput(req, res, next);

        expect(req.body).toEqual({
          age: 25,
          price: 99.99,
          count: 0
        });
        expect(mockSanitize).not.toHaveBeenCalled();
      });

      it('should preserve booleans', () => {
        req.body = {
          isActive: true,
          isDeleted: false
        };

        sanitizeInput(req, res, next);

        expect(req.body).toEqual({
          isActive: true,
          isDeleted: false
        });
        expect(mockSanitize).not.toHaveBeenCalled();
      });

      it('should preserve null values', () => {
        req.body = {
          nullValue: null
        };

        sanitizeInput(req, res, next);

        expect(req.body.nullValue).toBeNull();
        expect(mockSanitize).not.toHaveBeenCalled();
      });

      it('should preserve undefined values', () => {
        req.body = {
          undefinedValue: undefined
        };

        sanitizeInput(req, res, next);

        expect(req.body.undefinedValue).toBeUndefined();
        expect(mockSanitize).not.toHaveBeenCalled();
      });
    });

    describe('Edge cases', () => {
      it('should handle empty objects', () => {
        req.body = {};
        req.query = {};
        req.params = {};

        sanitizeInput(req, res, next);

        expect(req.body).toEqual({});
        expect(req.query).toEqual({});
        expect(req.params).toEqual({});
        expect(next).toHaveBeenCalled();
      });

      it('should handle missing req properties', () => {
        delete req.body;
        delete req.query;
        delete req.params;

        sanitizeInput(req, res, next);

        expect(next).toHaveBeenCalled();
        expect(mockSanitize).not.toHaveBeenCalled();
      });

      it('should handle mixed data types in objects', () => {
        req.body = {
          string: '<script>test</script>Hello',
          number: 42,
          boolean: true,
          array: ['<em>item</em>', 123, false],
          object: {
            nested: '<b>nested</b>value'
          }
        };
        mockSanitize
          .mockReturnValueOnce('Hello')
          .mockReturnValueOnce('item')
          .mockReturnValueOnce('nestedvalue');

        sanitizeInput(req, res, next);

        expect(req.body.string).toBe('Hello');
        expect(req.body.number).toBe(42);
        expect(req.body.boolean).toBe(true);
        expect(req.body.array['0']).toBe('item');
        expect(req.body.array['1']).toBe(123);
        expect(req.body.array['2']).toBe(false);
        expect(req.body.object.nested).toBe('nestedvalue');
      });
    });
  });

  describe('handleValidationErrors', () => {
    it('should call next when no validation errors', () => {
      const mockResult = {
        isEmpty: jest.fn(() => true),
        array: jest.fn(() => [])
      };
      mockValidationResult.mockReturnValue(mockResult);

      handleValidationErrors(req, res, next);

      expect(mockValidationResult).toHaveBeenCalledWith(req);
      expect(mockResult.isEmpty).toHaveBeenCalled();
      expect(next).toHaveBeenCalled();
      expect(res.status).not.toHaveBeenCalled();
    });

    it('should return 400 with error details when validation fails', () => {
      const mockErrors = [
        { path: 'email', msg: 'Invalid email format' },
        { path: 'password', msg: 'Password too short' }
      ];
      const mockResult = {
        isEmpty: jest.fn(() => false),
        array: jest.fn(() => mockErrors)
      };
      mockValidationResult.mockReturnValue(mockResult);

      handleValidationErrors(req, res, next);

      expect(res.status).toHaveBeenCalledWith(400);
      expect(res.json).toHaveBeenCalledWith({
        error: 'Validation failed',
        details: [
          { field: 'email', message: 'Invalid email format' },
          { field: 'password', message: 'Password too short' }
        ]
      });
      expect(next).not.toHaveBeenCalled();
    });

    it('should handle single validation error', () => {
      const mockErrors = [
        { path: 'username', msg: 'Username is required' }
      ];
      const mockResult = {
        isEmpty: jest.fn(() => false),
        array: jest.fn(() => mockErrors)
      };
      mockValidationResult.mockReturnValue(mockResult);

      handleValidationErrors(req, res, next);

      expect(res.json).toHaveBeenCalledWith({
        error: 'Validation failed',
        details: [
          { field: 'username', message: 'Username is required' }
        ]
      });
    });

    it('should handle errors with different field names', () => {
      const mockErrors = [
        { path: 'firstName', msg: 'First name is required' },
        { path: 'lastName', msg: 'Last name is required' },
        { path: 'phone', msg: 'Invalid phone number' }
      ];
      const mockResult = {
        isEmpty: jest.fn(() => false),
        array: jest.fn(() => mockErrors)
      };
      mockValidationResult.mockReturnValue(mockResult);

      handleValidationErrors(req, res, next);

      expect(res.json).toHaveBeenCalledWith({
        error: 'Validation failed',
        details: [
          { field: 'firstName', message: 'First name is required' },
          { field: 'lastName', message: 'Last name is required' },
          { field: 'phone', message: 'Invalid phone number' }
        ]
      });
    });
  });

  describe('Validation rule arrays', () => {
    const { body } = require('express-validator');

    describe('validateRegistration', () => {
      it('should be an array of validation middlewares', () => {
        expect(Array.isArray(validateRegistration)).toBe(true);
        expect(validateRegistration.length).toBeGreaterThan(1);
        expect(validateRegistration[validateRegistration.length - 1]).toBe(handleValidationErrors);
      });

      it('should include validation fields', () => {
        // Since we're mocking express-validator, we can't test the actual calls
        // but we can verify the validation array structure
        expect(validateRegistration.length).toBeGreaterThan(5); // Should have multiple validators
        expect(validateRegistration[validateRegistration.length - 1]).toBe(handleValidationErrors);
      });
    });

    describe('validateLogin', () => {
      it('should be an array of validation middlewares', () => {
        expect(Array.isArray(validateLogin)).toBe(true);
        expect(validateLogin.length).toBeGreaterThan(1);
        expect(validateLogin[validateLogin.length - 1]).toBe(handleValidationErrors);
      });

      it('should include validation fields', () => {
        expect(validateLogin.length).toBeGreaterThan(2); // Should have email, password, and handler
        expect(validateLogin[validateLogin.length - 1]).toBe(handleValidationErrors);
      });
    });

    describe('validateGoogleAuth', () => {
      it('should be an array of validation middlewares', () => {
        expect(Array.isArray(validateGoogleAuth)).toBe(true);
        expect(validateGoogleAuth.length).toBeGreaterThan(1);
        expect(validateGoogleAuth[validateGoogleAuth.length - 1]).toBe(handleValidationErrors);
      });

      it('should include validation fields', () => {
        expect(validateGoogleAuth.length).toBeGreaterThan(1);
        expect(validateGoogleAuth[validateGoogleAuth.length - 1]).toBe(handleValidationErrors);
      });
    });

    describe('validateProfileUpdate', () => {
      it('should be an array of validation middlewares', () => {
        expect(Array.isArray(validateProfileUpdate)).toBe(true);
        expect(validateProfileUpdate.length).toBeGreaterThan(1);
        expect(validateProfileUpdate[validateProfileUpdate.length - 1]).toBe(handleValidationErrors);
      });

      it('should include validation fields', () => {
        expect(validateProfileUpdate.length).toBeGreaterThan(5);
        expect(validateProfileUpdate[validateProfileUpdate.length - 1]).toBe(handleValidationErrors);
      });
    });

    describe('validatePasswordChange', () => {
      it('should be an array of validation middlewares', () => {
        expect(Array.isArray(validatePasswordChange)).toBe(true);
        expect(validatePasswordChange.length).toBeGreaterThan(1);
        expect(validatePasswordChange[validatePasswordChange.length - 1]).toBe(handleValidationErrors);
      });

      it('should include validation fields', () => {
        expect(validatePasswordChange.length).toBeGreaterThan(3);
        expect(validatePasswordChange[validatePasswordChange.length - 1]).toBe(handleValidationErrors);
      });
    });
  });

  describe('Integration with express-validator', () => {
    const { body } = require('express-validator');

    it('should create validation chains with proper methods', () => {
      // Verify that body() returns an object with chaining methods
      const validationChain = body('test');

      expect(validationChain.isEmail).toBeDefined();
      expect(validationChain.isLength).toBeDefined();
      expect(validationChain.matches).toBeDefined();
      expect(validationChain.custom).toBeDefined();
      expect(validationChain.trim).toBeDefined();
      expect(validationChain.optional).toBeDefined();
      expect(validationChain.withMessage).toBeDefined();
    });

    it('should chain validation methods correctly', () => {
      const validationChain = body('email');

      // Test method chaining
      const result = validationChain
        .isEmail()
        .normalizeEmail()
        .withMessage('Test message')
        .isLength({ max: 255 });

      expect(result).toBe(validationChain); // Should return the same object for chaining
    });
  });

  describe('DOMPurify integration', () => {
    const DOMPurify = require('dompurify');
    const { JSDOM } = require('jsdom');

    it('should use mocked DOMPurify and JSDOM', () => {
      const { JSDOM } = require('jsdom');
      const DOMPurify = require('dompurify');

      // Test that our mocks are in place
      expect(typeof JSDOM).toBe('function');
      expect(typeof DOMPurify).toBe('function');

      // Test that DOMPurify returns our mock sanitize function
      const purifyInstance = DOMPurify();
      expect(purifyInstance.sanitize).toBe(mockSanitize);
    });

    it('should call DOMPurify.sanitize with correct options', () => {
      req.body = { test: '<script>alert("xss")</script>Hello' };
      mockSanitize.mockReturnValue('Hello');

      sanitizeInput(req, res, next);

      expect(mockSanitize).toHaveBeenCalledWith('<script>alert("xss")</script>Hello', { ALLOWED_TAGS: [] });
    });

    it('should strip all HTML tags by default', () => {
      req.body = {
        input1: '<div>content</div>',
        input2: '<span>text</span>',
        input3: '<script>malicious</script>'
      };
      mockSanitize
        .mockReturnValueOnce('content')
        .mockReturnValueOnce('text')
        .mockReturnValueOnce('');

      sanitizeInput(req, res, next);

      expect(mockSanitize).toHaveBeenCalledWith('<div>content</div>', { ALLOWED_TAGS: [] });
      expect(mockSanitize).toHaveBeenCalledWith('<span>text</span>', { ALLOWED_TAGS: [] });
      expect(mockSanitize).toHaveBeenCalledWith('<script>malicious</script>', { ALLOWED_TAGS: [] });
    });
  });

  describe('Module exports', () => {
    it('should export all required validation functions and middlewares', () => {
      const validationModule = require('../../../middleware/validation');

      expect(validationModule).toHaveProperty('sanitizeInput');
      expect(validationModule).toHaveProperty('handleValidationErrors');
      expect(validationModule).toHaveProperty('validateRegistration');
      expect(validationModule).toHaveProperty('validateLogin');
      expect(validationModule).toHaveProperty('validateGoogleAuth');
      expect(validationModule).toHaveProperty('validateProfileUpdate');
      expect(validationModule).toHaveProperty('validatePasswordChange');
    });

    it('should export functions and arrays with correct types', () => {
      const validationModule = require('../../../middleware/validation');

      expect(typeof validationModule.sanitizeInput).toBe('function');
      expect(typeof validationModule.handleValidationErrors).toBe('function');
      expect(Array.isArray(validationModule.validateRegistration)).toBe(true);
      expect(Array.isArray(validationModule.validateLogin)).toBe(true);
      expect(Array.isArray(validationModule.validateGoogleAuth)).toBe(true);
      expect(Array.isArray(validationModule.validateProfileUpdate)).toBe(true);
      expect(Array.isArray(validationModule.validatePasswordChange)).toBe(true);
    });
  });

  describe('Password strength validation', () => {
    // Since we're testing the module structure, we can verify that password validation
    // includes the expected patterns and common password checks
    it('should include password strength requirements in registration', () => {
      const registrationValidation = validateRegistration;

      // The password validation should be one of the middleware functions
      expect(registrationValidation.length).toBeGreaterThan(1);
      expect(Array.isArray(registrationValidation)).toBe(true);
    });

    it('should include password change validation with multiple fields', () => {
      const passwordChangeValidation = validatePasswordChange;

      // Should have current password, new password, and confirm password validation
      expect(passwordChangeValidation.length).toBeGreaterThan(1);
      expect(Array.isArray(passwordChangeValidation)).toBe(true);
    });

    describe('Password pattern validation', () => {
      it('should accept strong passwords with all required elements', () => {
        // Test passwords that should pass the regex: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/
        const strongPasswords = [
          'Password123!',
          'MyStr0ngP@ss',
          'C0mpl3xP@ssw0rd',
          'Secure123#Pass',
          'TestUser2024!'
        ];

        strongPasswords.forEach(password => {
          const passwordRegex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/;
          expect(passwordRegex.test(password)).toBe(true);
        });
      });

      it('should reject passwords missing uppercase letters', () => {
        const weakPasswords = [
          'password123!',
          'weak@pass1',
          'lowercase123#'
        ];

        weakPasswords.forEach(password => {
          const passwordRegex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/;
          expect(passwordRegex.test(password)).toBe(false);
        });
      });

      it('should reject passwords missing lowercase letters', () => {
        const weakPasswords = [
          'PASSWORD123!',
          'UPPERCASE@123',
          'ALLCAPS456#'
        ];

        weakPasswords.forEach(password => {
          const passwordRegex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/;
          expect(passwordRegex.test(password)).toBe(false);
        });
      });

      it('should reject passwords missing numbers', () => {
        const weakPasswords = [
          'Password!',
          'NoNumbers@Pass',
          'WeakPassword#'
        ];

        weakPasswords.forEach(password => {
          const passwordRegex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/;
          expect(passwordRegex.test(password)).toBe(false);
        });
      });

      it('should reject passwords under 8 characters', () => {
        const shortPasswords = [
          'Pass1!',
          'Ab3#',
          'Short1!'
        ];

        shortPasswords.forEach(password => {
          const passwordRegex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/;
          expect(passwordRegex.test(password)).toBe(false);
        });
      });
    });

    describe('Common password validation', () => {
      it('should reject common passwords regardless of case', () => {
        const commonPasswords = [
          'password',
          'PASSWORD',
          'Password',
          '123456',
          'qwerty',
          'QWERTY',
          'admin',
          'ADMIN',
          'password123',
          'PASSWORD123'
        ];

        // Test the actual common passwords array from validation.js
        const validationCommonPasswords = [
          'password',
          '123456',
          '123456789',
          'qwerty',
          'abc123',
          'password123',
          'admin',
          'letmein',
          'welcome',
          'monkey',
          '1234567890'
        ];

        commonPasswords.forEach(password => {
          const isCommon = validationCommonPasswords.includes(password.toLowerCase());
          if (validationCommonPasswords.includes(password.toLowerCase())) {
            expect(isCommon).toBe(true);
          }
        });
      });

      it('should accept strong passwords not in common list', () => {
        const uniquePasswords = [
          'MyUniqueP@ss123',
          'Str0ngP@ssw0rd2024',
          'C0mpl3xS3cur3P@ss',
          'UnguessableP@ss456'
        ];

        const validationCommonPasswords = [
          'password',
          '123456',
          '123456789',
          'qwerty',
          'abc123',
          'password123',
          'admin',
          'letmein',
          'welcome',
          'monkey',
          '1234567890'
        ];

        uniquePasswords.forEach(password => {
          const isCommon = validationCommonPasswords.includes(password.toLowerCase());
          expect(isCommon).toBe(false);
        });
      });
    });

    describe('Password length boundaries', () => {
      it('should reject passwords exactly 7 characters', () => {
        const passwordRegex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/;
        expect(passwordRegex.test('Pass12!')).toBe(false); // 7 chars
      });

      it('should accept passwords exactly 8 characters', () => {
        const passwordRegex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/;
        expect(passwordRegex.test('Pass123!')).toBe(true); // 8 chars
      });

      it('should accept very long passwords up to 128 characters', () => {
        const longPassword = 'A1!' + 'a'.repeat(125); // 128 chars total
        const passwordRegex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/;
        expect(passwordRegex.test(longPassword)).toBe(true);
        expect(longPassword.length).toBe(128);
      });
    });

    describe('Password change specific validation', () => {
      it('should ensure new password differs from current password', () => {
        // This tests the custom validation logic in validatePasswordChange
        const samePassword = 'SamePassword123!';

        // Mock request object for password change
        const mockReq = {
          body: {
            currentPassword: samePassword,
            newPassword: samePassword,
            confirmPassword: samePassword
          }
        };

        // Test that passwords are identical (this would trigger custom validation error)
        expect(mockReq.body.currentPassword).toBe(mockReq.body.newPassword);
      });

      it('should ensure confirm password matches new password', () => {
        const mockReq = {
          body: {
            currentPassword: 'OldPassword123!',
            newPassword: 'NewPassword123!',
            confirmPassword: 'DifferentPassword123!'
          }
        };

        // Test that passwords don't match (this would trigger custom validation error)
        expect(mockReq.body.newPassword).not.toBe(mockReq.body.confirmPassword);
      });

      it('should accept valid password change with all different passwords', () => {
        const mockReq = {
          body: {
            currentPassword: 'OldPassword123!',
            newPassword: 'NewPassword456!',
            confirmPassword: 'NewPassword456!'
          }
        };

        // All validations should pass
        expect(mockReq.body.currentPassword).not.toBe(mockReq.body.newPassword);
        expect(mockReq.body.newPassword).toBe(mockReq.body.confirmPassword);

        const passwordRegex = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/;
        expect(passwordRegex.test(mockReq.body.newPassword)).toBe(true);
      });
    });
  });

  describe('Field-specific validation tests', () => {
    describe('Email validation', () => {
      it('should accept valid email formats', () => {
        const validEmails = [
          'user@example.com',
          'test.email@domain.co.uk',
          'user+tag@example.org',
          'firstname.lastname@company.com',
          'user123@domain123.com'
        ];

        validEmails.forEach(email => {
          // Basic email regex test (simplified version of what express-validator uses)
          const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
          expect(emailRegex.test(email)).toBe(true);
        });
      });

      it('should reject invalid email formats', () => {
        const invalidEmails = [
          'invalid-email',
          '@domain.com',
          'user@',
          'user@domain',
          'user.domain.com'
        ];

        invalidEmails.forEach(email => {
          const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
          expect(emailRegex.test(email)).toBe(false);
        });
      });

      it('should handle email normalization', () => {
        const emailsToNormalize = [
          { input: 'User@Example.COM', normalized: 'user@example.com' },
          { input: 'TEST.USER@DOMAIN.ORG', normalized: 'test.user@domain.org' },
          { input: '  user@domain.com  ', normalized: 'user@domain.com' }
        ];

        emailsToNormalize.forEach(({ input, normalized }) => {
          // Test email normalization (lowercase and trim)
          const result = input.toLowerCase().trim();
          expect(result).toBe(normalized);
        });
      });

      it('should enforce email length limits', () => {
        const longEmail = 'a'.repeat(244) + '@example.com'; // > 255 chars
        expect(longEmail.length).toBeGreaterThan(255);

        const validEmail = 'user@example.com';
        expect(validEmail.length).toBeLessThanOrEqual(255);
      });
    });

    describe('Name validation (firstName/lastName)', () => {
      it('should accept valid name formats', () => {
        const validNames = [
          'John',
          'Mary-Jane',
          "O'Connor",
          'Jean-Pierre',
          'Anna Maria',
          'McDonalds'
        ];

        validNames.forEach(name => {
          const nameRegex = /^[a-zA-Z\s\-']+$/;
          expect(nameRegex.test(name)).toBe(true);
          expect(name.length).toBeGreaterThan(0);
          expect(name.length).toBeLessThanOrEqual(50);
        });
      });

      it('should reject invalid name formats', () => {
        const invalidNames = [
          'John123',
          'Mary@Jane',
          'User$Name',
          'Name#WithSymbols',
          'John.Doe',
          'Name_With_Underscores',
          'Name+Plus',
          'Name(Parens)'
        ];

        invalidNames.forEach(name => {
          const nameRegex = /^[a-zA-Z\s\-']+$/;
          expect(nameRegex.test(name)).toBe(false);
        });
      });

      it('should enforce name length limits', () => {
        const tooLongName = 'a'.repeat(51);
        expect(tooLongName.length).toBeGreaterThan(50);

        const emptyName = '';
        expect(emptyName.length).toBe(0);

        const validName = 'John';
        expect(validName.length).toBeGreaterThan(0);
        expect(validName.length).toBeLessThanOrEqual(50);
      });

      it('should handle name trimming', () => {
        const namesToTrim = [
          { input: '  John  ', trimmed: 'John' },
          { input: '\tMary\t', trimmed: 'Mary' },
          { input: '  Jean-Pierre  ', trimmed: 'Jean-Pierre' }
        ];

        namesToTrim.forEach(({ input, trimmed }) => {
          expect(input.trim()).toBe(trimmed);
        });
      });
    });

    describe('Username validation', () => {
      it('should accept valid username formats', () => {
        const validUsernames = [
          'user123',
          'test_user',
          'user-name',
          'username',
          'user_123',
          'test-user-123',
          'USER123',
          'TestUser'
        ];

        validUsernames.forEach(username => {
          const usernameRegex = /^[a-zA-Z0-9_-]+$/;
          expect(usernameRegex.test(username)).toBe(true);
          expect(username.length).toBeGreaterThanOrEqual(3);
          expect(username.length).toBeLessThanOrEqual(30);
        });
      });

      it('should reject invalid username formats', () => {
        const invalidUsernames = [
          'user@name',
          'user.name',
          'user+name',
          'user name',
          'user#name',
          'user$name',
          'user%name',
          'user!name'
        ];

        invalidUsernames.forEach(username => {
          const usernameRegex = /^[a-zA-Z0-9_-]+$/;
          expect(usernameRegex.test(username)).toBe(false);
        });
      });

      it('should enforce username length limits', () => {
        const tooShort = 'ab';
        expect(tooShort.length).toBeLessThan(3);

        const tooLong = 'a'.repeat(31);
        expect(tooLong.length).toBeGreaterThan(30);

        const validUsername = 'user123';
        expect(validUsername.length).toBeGreaterThanOrEqual(3);
        expect(validUsername.length).toBeLessThanOrEqual(30);
      });
    });

    describe('Phone number validation', () => {
      it('should accept valid phone number formats', () => {
        const validPhones = [
          '+1234567890',
          '+12345678901',
          '1234567890',
          '+447912345678', // UK
          '+33123456789', // France
          '+81312345678' // Japan
        ];

        // Since we're using isMobilePhone(), we test the general format
        validPhones.forEach(phone => {
          expect(phone).toMatch(/^\+?\d{10,15}$/);
        });
      });

      it('should reject invalid phone number formats', () => {
        const invalidPhones = [
          '123',
          'abc123456',
          '123-456-7890',
          '(123) 456-7890',
          '+1 234 567 890',
          'phone123',
          '12345678901234567890' // Too long
        ];

        invalidPhones.forEach(phone => {
          expect(phone).not.toMatch(/^\+?\d{10,15}$/);
        });
      });
    });

    describe('Address validation', () => {
      it('should enforce address field length limits', () => {
        const validAddress1 = '123 Main Street';
        expect(validAddress1.length).toBeLessThanOrEqual(255);

        const validAddress2 = 'Apt 4B';
        expect(validAddress2.length).toBeLessThanOrEqual(255);

        const validCity = 'New York';
        expect(validCity.length).toBeLessThanOrEqual(100);

        const validState = 'California';
        expect(validState.length).toBeLessThanOrEqual(100);

        const validCountry = 'United States';
        expect(validCountry.length).toBeLessThanOrEqual(100);
      });

      it('should accept valid city formats', () => {
        const validCities = [
          'New York',
          'Los Angeles',
          'Saint-Pierre',
          "O'Fallon"
        ];

        validCities.forEach(city => {
          const cityRegex = /^[a-zA-Z\s\-']+$/;
          expect(cityRegex.test(city)).toBe(true);
        });
      });

      it('should reject invalid city formats', () => {
        const invalidCities = [
          'City123',
          'City@Name',
          'City_Name',
          'City.Name',
          'City+Name'
        ];

        invalidCities.forEach(city => {
          const cityRegex = /^[a-zA-Z\s\-']+$/;
          expect(cityRegex.test(city)).toBe(false);
        });
      });
    });

    describe('ZIP code validation', () => {
      it('should accept valid ZIP code formats', () => {
        const validZipCodes = [
          '12345',
          '12345-6789',
          '90210',
          '00501-0001'
        ];

        validZipCodes.forEach(zip => {
          const zipRegex = /^[0-9]{5}(-[0-9]{4})?$/;
          expect(zipRegex.test(zip)).toBe(true);
        });
      });

      it('should reject invalid ZIP code formats', () => {
        const invalidZipCodes = [
          '1234',
          '123456',
          '12345-678',
          '12345-67890',
          'abcde',
          '12345-abcd',
          '12345 6789',
          '12345_6789'
        ];

        invalidZipCodes.forEach(zip => {
          const zipRegex = /^[0-9]{5}(-[0-9]{4})?$/;
          expect(zipRegex.test(zip)).toBe(false);
        });
      });
    });

    describe('Google Auth token validation', () => {
      it('should enforce token length limits', () => {
        const validToken = 'eyJ' + 'a'.repeat(2000); // Under 2048 chars
        expect(validToken.length).toBeLessThanOrEqual(2048);

        const tooLongToken = 'a'.repeat(2049);
        expect(tooLongToken.length).toBeGreaterThan(2048);
      });

      it('should require non-empty token', () => {
        const emptyToken = '';
        expect(emptyToken.length).toBe(0);

        const validToken = 'eyJhbGciOiJSUzI1NiIsImtpZCI6...';
        expect(validToken.length).toBeGreaterThan(0);
      });
    });
  });

  describe('Custom validation logic tests', () => {
    describe('Optional field validation', () => {
      it('should allow optional fields to be undefined', () => {
        const optionalFields = {
          username: undefined,
          phone: undefined,
          address1: undefined,
          address2: undefined,
          city: undefined,
          state: undefined,
          zipCode: undefined,
          country: undefined
        };

        // These fields should be valid when undefined/optional
        Object.entries(optionalFields).forEach(([field, value]) => {
          expect(value).toBeUndefined();
        });
      });

      it('should allow optional fields to be empty strings after trimming', () => {
        const optionalFieldsEmpty = {
          username: '   ',
          address1: '\t\t',
          address2: '  \n  '
        };

        Object.entries(optionalFieldsEmpty).forEach(([field, value]) => {
          const trimmed = value.trim();
          expect(trimmed).toBe('');
        });
      });

      it('should validate optional fields when they have values', () => {
        const validOptionalFields = {
          username: 'validuser123',
          phone: '+1234567890',
          address1: '123 Main St',
          city: 'New York',
          zipCode: '12345'
        };

        // Test that optional fields with valid values pass validation
        expect(validOptionalFields.username).toMatch(/^[a-zA-Z0-9_-]+$/);
        expect(validOptionalFields.phone).toMatch(/^\+?\d{10,15}$/);
        expect(validOptionalFields.city).toMatch(/^[a-zA-Z\s\-']+$/);
        expect(validOptionalFields.zipCode).toMatch(/^[0-9]{5}(-[0-9]{4})?$/);
      });
    });

    describe('Required field validation', () => {
      it('should enforce required fields for registration', () => {
        const requiredRegistrationFields = {
          email: 'user@example.com',
          password: 'Password123!',
          firstName: 'John',
          lastName: 'Doe'
        };

        // All required fields should have values
        Object.entries(requiredRegistrationFields).forEach(([field, value]) => {
          expect(value).toBeDefined();
          expect(value.toString().trim()).not.toBe('');
        });
      });

      it('should enforce required fields for login', () => {
        const requiredLoginFields = {
          email: 'user@example.com',
          password: 'anypassword'
        };

        Object.entries(requiredLoginFields).forEach(([field, value]) => {
          expect(value).toBeDefined();
          expect(value.toString().trim()).not.toBe('');
        });
      });

      it('should enforce required fields for password change', () => {
        const requiredPasswordChangeFields = {
          currentPassword: 'OldPassword123!',
          newPassword: 'NewPassword456!',
          confirmPassword: 'NewPassword456!'
        };

        Object.entries(requiredPasswordChangeFields).forEach(([field, value]) => {
          expect(value).toBeDefined();
          expect(value.toString().trim()).not.toBe('');
        });
      });

      it('should enforce required field for Google auth', () => {
        const requiredGoogleAuthFields = {
          idToken: 'eyJhbGciOiJSUzI1NiIsImtpZCI6...'
        };

        Object.entries(requiredGoogleAuthFields).forEach(([field, value]) => {
          expect(value).toBeDefined();
          expect(value.toString().trim()).not.toBe('');
        });
      });
    });

    describe('Conditional validation logic', () => {
      it('should validate password confirmation matches new password', () => {
        const passwordChangeScenarios = [
          {
            newPassword: 'NewPassword123!',
            confirmPassword: 'NewPassword123!',
            shouldMatch: true
          },
          {
            newPassword: 'NewPassword123!',
            confirmPassword: 'DifferentPassword456!',
            shouldMatch: false
          },
          {
            newPassword: 'Password',
            confirmPassword: 'password', // Case sensitive
            shouldMatch: false
          }
        ];

        passwordChangeScenarios.forEach(({ newPassword, confirmPassword, shouldMatch }) => {
          const matches = newPassword === confirmPassword;
          expect(matches).toBe(shouldMatch);
        });
      });

      it('should validate new password differs from current password', () => {
        const passwordChangeScenarios = [
          {
            currentPassword: 'OldPassword123!',
            newPassword: 'NewPassword456!',
            shouldBeDifferent: true
          },
          {
            currentPassword: 'SamePassword123!',
            newPassword: 'SamePassword123!',
            shouldBeDifferent: false
          },
          {
            currentPassword: 'password123',
            newPassword: 'PASSWORD123', // Case sensitive
            shouldBeDifferent: true
          }
        ];

        passwordChangeScenarios.forEach(({ currentPassword, newPassword, shouldBeDifferent }) => {
          const isDifferent = currentPassword !== newPassword;
          expect(isDifferent).toBe(shouldBeDifferent);
        });
      });
    });

    describe('Field interdependency validation', () => {
      it('should validate complete address sets when partially provided', () => {
        const addressCombinations = [
          {
            address1: '123 Main St',
            city: 'New York',
            state: 'NY',
            zipCode: '12345',
            isComplete: true
          },
          {
            address1: '123 Main St',
            city: undefined,
            state: 'NY',
            zipCode: '12345',
            isComplete: false
          },
          {
            address1: undefined,
            city: undefined,
            state: undefined,
            zipCode: undefined,
            isComplete: true // All empty is valid
          }
        ];

        addressCombinations.forEach(({ address1, city, state, zipCode, isComplete }) => {
          const hasAnyAddress = [address1, city, state, zipCode].some(field =>
            field !== undefined && (typeof field === 'string' ? field.trim() !== '' : true)
          );
          const hasAllRequired = !!(address1 && city && state && zipCode);

          if (hasAnyAddress) {
            expect(hasAllRequired).toBe(isComplete);
          } else {
            expect(true).toBe(true); // All empty is valid
          }
        });
      });
    });

    describe('Data type validation', () => {
      it('should handle string input validation correctly', () => {
        const stringInputs = [
          { value: 'validstring', isValid: true },
          { value: '', isValid: false }, // Empty after trim
          { value: '   ', isValid: false }, // Whitespace only
          { value: 'a'.repeat(256), isValid: false }, // Too long for most fields
          { value: 'Valid String 123', isValid: true }
        ];

        stringInputs.forEach(({ value, isValid }) => {
          const trimmed = value.trim();
          const hasContent = trimmed.length > 0;
          const isReasonableLength = trimmed.length <= 255;

          expect(hasContent && isReasonableLength).toBe(isValid);
        });
      });

      it('should handle numeric string validation', () => {
        const numericInputs = [
          { value: '12345', isNumeric: true },
          { value: '123abc', isNumeric: false },
          { value: '12345-6789', isNumeric: false }, // Contains hyphen
          { value: '', isNumeric: false },
          { value: '000123', isNumeric: true }
        ];

        numericInputs.forEach(({ value, isNumeric }) => {
          const isDigitsOnly = /^\d+$/.test(value);
          expect(isDigitsOnly).toBe(isNumeric);
        });
      });
    });

    describe('Edge case handling', () => {
      it('should handle unicode characters appropriately', () => {
        const unicodeTestCases = [
          { value: 'Москва', field: 'city', shouldPass: false }, // Cyrillic not allowed in name regex
          { value: '北京', field: 'city', shouldPass: false } // Chinese characters
        ];

        unicodeTestCases.forEach(({ value, field, shouldPass }) => {
          let regex;
          switch (field) {
            case 'name':
            case 'city':
              regex = /^[a-zA-Z\s\-']+$/;
              break;
            case 'email':
              regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
              break;
            default:
              regex = /./;
          }

          expect(regex.test(value)).toBe(shouldPass);
        });
      });

      it('should handle very long input strings', () => {
        const longInputTests = [
          { field: 'email', maxLength: 255, value: 'user@' + 'a'.repeat(250) + '.com' },
          { field: 'firstName', maxLength: 50, value: 'a'.repeat(51) },
          { field: 'username', maxLength: 30, value: 'a'.repeat(31) },
          { field: 'password', maxLength: 128, value: 'A1!' + 'a'.repeat(126) }
        ];

        longInputTests.forEach(({ field, maxLength, value }) => {
          const exceedsLimit = value.length > maxLength;
          expect(exceedsLimit).toBe(true);
        });
      });

      it('should handle malformed input gracefully', () => {
        const malformedInputs = [
          null,
          undefined,
          {},
          [],
          123,
          true,
          false
        ];

        malformedInputs.forEach(input => {
          const isString = typeof input === 'string';
          const isValidForStringValidation = isString || input == null;

          // Only strings and null/undefined should pass initial type checks
          expect(isValidForStringValidation).toBe(
            typeof input === 'string' || input == null
          );
        });
      });
    });
  });

  describe('Edge cases and security tests', () => {
    describe('Advanced sanitization tests', () => {
      it('should sanitize complex XSS attack vectors', () => {
        const xssPayloads = [
          '<script>alert("XSS")</script>',
          '<img src="x" onerror="alert(1)">',
          '<svg onload="alert(\'XSS\')">',
          '<iframe src="javascript:alert(\'XSS\')"></iframe>',
          '<object data="javascript:alert(\'XSS\')"></object>',
          '<embed src="javascript:alert(\'XSS\')">',
          '<link rel="stylesheet" href="javascript:alert(\'XSS\')">',
          '<style>@import "javascript:alert(\'XSS\')";</style>',
          '<div onclick="alert(\'XSS\')">Click me</div>',
          '<input type="image" src="x" onerror="alert(\'XSS\')">'
        ];

        req.body = { maliciousInput: '<script>alert("test")</script>Hello' };
        mockSanitize.mockReturnValue('Hello');

        sanitizeInput(req, res, next);

        expect(mockSanitize).toHaveBeenCalledWith('<script>alert("test")</script>Hello', { ALLOWED_TAGS: [] });
        expect(req.body.maliciousInput).toBe('Hello');
      });

      it('should sanitize SQL injection attempts', () => {
        const sqlInjectionPayloads = [
          "'; DROP TABLE users; --",
          "' OR '1'='1",
          "1'; UNION SELECT * FROM users--",
          "'; INSERT INTO admin VALUES ('hacker', 'password'); --",
          "1' AND (SELECT COUNT(*) FROM users) > 0 --"
        ];

        sqlInjectionPayloads.forEach((payload, index) => {
          req.body = { userInput: payload };
          mockSanitize.mockReturnValue('sanitized');

          sanitizeInput(req, res, next);

          expect(mockSanitize).toHaveBeenCalledWith(payload, { ALLOWED_TAGS: [] });
        });
      });

      it('should handle deeply nested objects with malicious content', () => {
        req.body = {
          level1: {
            level2: {
              level3: {
                level4: {
                  malicious: '<script>alert("deep")</script>',
                  array: [
                    '<img src="x" onerror="alert(1)">',
                    {
                      nested: '<svg onload="alert(2)">'
                    }
                  ]
                }
              }
            }
          }
        };

        mockSanitize
          .mockReturnValueOnce('sanitized1')
          .mockReturnValueOnce('sanitized2')
          .mockReturnValueOnce('sanitized3');

        sanitizeInput(req, res, next);

        expect(mockSanitize).toHaveBeenCalledTimes(3);
        expect(req.body.level1.level2.level3.level4.malicious).toBe('sanitized1');
      });

      it('should handle mixed content types in arrays', () => {
        req.body = {
          mixedArray: [
            '<script>alert("xss")</script>',
            123,
            true,
            {
              nested: '<img src="x" onerror="alert(1)">'
            },
            null,
            undefined,
            false
          ]
        };

        mockSanitize
          .mockReturnValueOnce('cleaned')
          .mockReturnValueOnce('cleaned_nested');

        sanitizeInput(req, res, next);

        expect(req.body.mixedArray[0]).toBe('cleaned');
        expect(req.body.mixedArray[1]).toBe(123);
        expect(req.body.mixedArray[2]).toBe(true);
        expect(req.body.mixedArray[3].nested).toBe('cleaned_nested');
        expect(req.body.mixedArray[4]).toBeNull();
        expect(req.body.mixedArray[5]).toBeUndefined();
        expect(req.body.mixedArray[6]).toBe(false);
      });
    });

    describe('Large payload handling', () => {
      it('should handle extremely large input strings', () => {
        const largeString = '<script>alert("xss")</script>' + 'A'.repeat(10000);
        req.body = { largeInput: largeString };
        mockSanitize.mockReturnValue('A'.repeat(10000));

        sanitizeInput(req, res, next);

        expect(mockSanitize).toHaveBeenCalledWith(largeString, { ALLOWED_TAGS: [] });
        expect(req.body.largeInput.length).toBe(10000);
      });

      it('should handle objects with many properties', () => {
        const manyProps = {};
        for (let i = 0; i < 1000; i++) {
          manyProps[`prop${i}`] = `<script>alert(${i})</script>value${i}`;
        }
        req.body = manyProps;

        // Mock to return cleaned values
        mockSanitize.mockImplementation((value) => value.replace(/<script>.*?<\/script>/, ''));

        sanitizeInput(req, res, next);

        expect(mockSanitize).toHaveBeenCalledTimes(1000);
        expect(req.body.prop0).toContain('value0');
        expect(req.body.prop999).toContain('value999');
      });

      it('should handle deeply nested object structures', () => {
        let deepObject = { value: '<script>alert("deep")</script>content' };
        for (let i = 0; i < 100; i++) {
          deepObject = { nested: deepObject };
        }
        req.body = deepObject;

        mockSanitize.mockReturnValue('content');

        sanitizeInput(req, res, next);

        expect(mockSanitize).toHaveBeenCalledWith('<script>alert("deep")</script>content', { ALLOWED_TAGS: [] });
      });
    });

    describe('Unicode and encoding attacks', () => {
      it('should handle various unicode XSS attempts', () => {
        const unicodeXSS = [
          '\u003cscript\u003ealert("XSS")\u003c/script\u003e',
          '%3Cscript%3Ealert("XSS")%3C/script%3E',
          '&lt;script&gt;alert("XSS")&lt;/script&gt;',
          '\x3cscript\x3ealert("XSS")\x3c/script\x3e'
        ];

        unicodeXSS.forEach((payload, index) => {
          req.body = { unicodeInput: payload };
          mockSanitize.mockReturnValue('sanitized');

          sanitizeInput(req, res, next);

          expect(mockSanitize).toHaveBeenCalledWith(payload, { ALLOWED_TAGS: [] });
        });
      });

      it('should handle null bytes and control characters', () => {
        const controlCharacters = [
          'test\x00script',
          'test\x01alert',
          'test\n\r\tscript',
          'test\u0000null',
          'test\uFEFFbom'
        ];

        controlCharacters.forEach((payload) => {
          req.body = { controlInput: payload };
          mockSanitize.mockReturnValue('test');

          sanitizeInput(req, res, next);

          expect(mockSanitize).toHaveBeenCalledWith(payload, { ALLOWED_TAGS: [] });
        });
      });
    });

    describe('Memory and performance edge cases', () => {
      it('should handle circular object references gracefully', () => {
        const circularObj = { name: '<script>alert("test")</script>safe' };
        circularObj.self = circularObj;

        // This should not cause infinite recursion
        expect(() => {
          // Test that we can detect circular references
          try {
            JSON.stringify(circularObj);
          } catch (error) {
            expect(error.message).toContain('circular');
          }
        }).not.toThrow();
      });

      it('should handle empty and null edge cases', () => {
        const edgeCases = [
          { body: {}, expected: {} },
          { body: { empty: '' }, expected: { empty: '' } },
          { body: { nullValue: null }, expected: { nullValue: null } },
          { body: { undefinedValue: undefined }, expected: { undefinedValue: undefined } },
          { body: { emptyObject: {} }, expected: { emptyObject: {} } }
        ];

        edgeCases.forEach(({ body, expected }) => {
          req.body = body;
          mockSanitize.mockImplementation((value) => value);

          sanitizeInput(req, res, next);

          expect(req.body).toEqual(expected);
        });
      });
    });

    describe('Validation bypass attempts', () => {
      it('should handle case variations in malicious input', () => {
        const caseVariations = [
          '<Script>alert("XSS")</Script>',
          '<SCRIPT>alert("XSS")</SCRIPT>',
          '<ScRiPt>alert("XSS")</ScRiPt>',
          '<sCrIpT>alert("XSS")</ScRiPt>'
        ];

        caseVariations.forEach((payload) => {
          req.body = { caseInput: payload };
          mockSanitize.mockReturnValue('');

          sanitizeInput(req, res, next);

          expect(mockSanitize).toHaveBeenCalledWith(payload, { ALLOWED_TAGS: [] });
        });
      });

      it('should handle whitespace variations in malicious input', () => {
        const whitespaceVariations = [
          '< script >alert("XSS")< /script >',
          '<\tscript\t>alert("XSS")<\t/script\t>',
          '<\nscript\n>alert("XSS")<\n/script\n>',
          '<\rscript\r>alert("XSS")<\r/script\r>'
        ];

        whitespaceVariations.forEach((payload) => {
          req.body = { whitespaceInput: payload };
          mockSanitize.mockReturnValue('alert("XSS")');

          sanitizeInput(req, res, next);

          expect(mockSanitize).toHaveBeenCalledWith(payload, { ALLOWED_TAGS: [] });
        });
      });

      it('should handle attribute-based XSS attempts', () => {
        const attributeXSS = [
          '<div onclick="alert(\'XSS\')">Click</div>',
          '<img src="x" onerror="alert(\'XSS\')">',
          '<body onload="alert(\'XSS\')">',
          '<input onfocus="alert(\'XSS\')" autofocus>',
          '<select onfocus="alert(\'XSS\')" autofocus><option>test</option></select>'
        ];

        attributeXSS.forEach((payload) => {
          req.body = { attributeInput: payload };
          mockSanitize.mockReturnValue('Click');

          sanitizeInput(req, res, next);

          expect(mockSanitize).toHaveBeenCalledWith(payload, { ALLOWED_TAGS: [] });
        });
      });
    });

    describe('Protocol-based attacks', () => {
      it('should handle javascript protocol attempts', () => {
        const jsProtocols = [
          'javascript:alert("XSS")',
          'JAVASCRIPT:alert("XSS")',
          'JaVaScRiPt:alert("XSS")',
          'javascript&colon;alert("XSS")',
          'javascript&#x3A;alert("XSS")'
        ];

        jsProtocols.forEach((payload) => {
          req.body = { jsInput: payload };
          mockSanitize.mockReturnValue('');

          sanitizeInput(req, res, next);

          expect(mockSanitize).toHaveBeenCalledWith(payload, { ALLOWED_TAGS: [] });
        });
      });

      it('should handle data URI attempts', () => {
        const dataURIs = [
          'data:text/html,<script>alert("XSS")</script>',
          'data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik8L3NjcmlwdD4=',
          'data:application/javascript,alert("XSS")'
        ];

        dataURIs.forEach((payload) => {
          req.body = { dataInput: payload };
          mockSanitize.mockReturnValue('');

          sanitizeInput(req, res, next);

          expect(mockSanitize).toHaveBeenCalledWith(payload, { ALLOWED_TAGS: [] });
        });
      });
    });

    describe('Input mutation and normalization', () => {
      it('should handle HTML entity encoding attacks', () => {
        const htmlEntities = [
          '&lt;script&gt;alert("XSS")&lt;/script&gt;',
          '&#60;script&#62;alert("XSS")&#60;/script&#62;',
          '&#x3C;script&#x3E;alert("XSS")&#x3C;/script&#x3E;',
          '&amp;lt;script&amp;gt;alert("XSS")&amp;lt;/script&amp;gt;'
        ];

        htmlEntities.forEach((payload) => {
          req.body = { entityInput: payload };
          mockSanitize.mockReturnValue('alert("XSS")');

          sanitizeInput(req, res, next);

          expect(mockSanitize).toHaveBeenCalledWith(payload, { ALLOWED_TAGS: [] });
        });
      });

      it('should handle URL encoding attacks', () => {
        const urlEncoded = [
          '%3Cscript%3Ealert%28%22XSS%22%29%3C%2Fscript%3E',
          '%253Cscript%253Ealert%2528%2522XSS%2522%2529%253C%252Fscript%253E',
          '%2527%253E%253Cscript%253Ealert%2528String.fromCharCode%252888%252C83%252C83%2529%2529%253C%252Fscript%253E'
        ];

        urlEncoded.forEach((payload) => {
          req.body = { urlInput: payload };
          mockSanitize.mockReturnValue('');

          sanitizeInput(req, res, next);

          expect(mockSanitize).toHaveBeenCalledWith(payload, { ALLOWED_TAGS: [] });
        });
      });
    });
  });

  describe('Error message validation tests', () => {
    describe('Validation error message structure', () => {
      it('should return properly structured error messages', () => {
        const mockErrors = [
          { path: 'email', msg: 'Please provide a valid email address' },
          { path: 'password', msg: 'Password must be between 8 and 128 characters' },
          { path: 'firstName', msg: 'First name must be between 1 and 50 characters' }
        ];

        const mockResult = {
          isEmpty: jest.fn(() => false),
          array: jest.fn(() => mockErrors)
        };
        mockValidationResult.mockReturnValue(mockResult);

        handleValidationErrors(req, res, next);

        expect(res.status).toHaveBeenCalledWith(400);
        expect(res.json).toHaveBeenCalledWith({
          error: 'Validation failed',
          details: [
            { field: 'email', message: 'Please provide a valid email address' },
            { field: 'password', message: 'Password must be between 8 and 128 characters' },
            { field: 'firstName', message: 'First name must be between 1 and 50 characters' }
          ]
        });
      });

      it('should handle single field with multiple validation errors', () => {
        const mockErrors = [
          { path: 'password', msg: 'Password must be between 8 and 128 characters' },
          { path: 'password', msg: 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character' },
          { path: 'password', msg: 'Password is too common. Please choose a stronger password' }
        ];

        const mockResult = {
          isEmpty: jest.fn(() => false),
          array: jest.fn(() => mockErrors)
        };
        mockValidationResult.mockReturnValue(mockResult);

        handleValidationErrors(req, res, next);

        expect(res.json).toHaveBeenCalledWith({
          error: 'Validation failed',
          details: [
            { field: 'password', message: 'Password must be between 8 and 128 characters' },
            { field: 'password', message: 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character' },
            { field: 'password', message: 'Password is too common. Please choose a stronger password' }
          ]
        });
      });
    });

    describe('Specific validation error messages', () => {
      it('should provide specific error messages for email validation', () => {
        const emailErrors = [
          { field: 'email', message: 'Please provide a valid email address', input: 'invalid-email' },
          { field: 'email', message: 'Email must be less than 255 characters', input: 'a'.repeat(250) + '@example.com' }
        ];

        emailErrors.forEach(({ field, message, input }) => {
          expect(field).toBe('email');
          expect(message.toLowerCase()).toContain('email');
          expect(typeof message).toBe('string');
          expect(message.length).toBeGreaterThan(0);
        });
      });

      it('should provide specific error messages for password validation', () => {
        const passwordErrors = [
          { field: 'password', message: 'Password must be between 8 and 128 characters', input: '1234567' },
          { field: 'password', message: 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character', input: 'password' },
          { field: 'password', message: 'Password is too common. Please choose a stronger password', input: 'password123' }
        ];

        passwordErrors.forEach(({ field, message, input }) => {
          expect(field).toBe('password');
          expect(message).toContain('Password');
          expect(typeof message).toBe('string');
          expect(message.length).toBeGreaterThan(0);
        });
      });

      it('should provide specific error messages for name validation', () => {
        const nameErrors = [
          { field: 'firstName', message: 'First name must be between 1 and 50 characters', input: '' },
          { field: 'firstName', message: 'First name can only contain letters, spaces, hyphens, and apostrophes', input: 'John123' },
          { field: 'lastName', message: 'Last name must be between 1 and 50 characters', input: 'a'.repeat(51) },
          { field: 'lastName', message: 'Last name can only contain letters, spaces, hyphens, and apostrophes', input: 'Doe@Domain' }
        ];

        nameErrors.forEach(({ field, message, input }) => {
          expect(['firstName', 'lastName']).toContain(field);
          expect(message).toMatch(/name/i);
          expect(typeof message).toBe('string');
          expect(message.length).toBeGreaterThan(0);
        });
      });

      it('should provide specific error messages for username validation', () => {
        const usernameErrors = [
          { field: 'username', message: 'Username must be between 3 and 30 characters', input: 'ab' },
          { field: 'username', message: 'Username can only contain letters, numbers, underscores, and hyphens', input: 'user@name' }
        ];

        usernameErrors.forEach(({ field, message, input }) => {
          expect(field).toBe('username');
          expect(message).toContain('Username');
          expect(typeof message).toBe('string');
          expect(message.length).toBeGreaterThan(0);
        });
      });

      it('should provide specific error messages for phone validation', () => {
        const phoneErrors = [
          { field: 'phone', message: 'Please provide a valid phone number', input: 'invalid-phone' }
        ];

        phoneErrors.forEach(({ field, message, input }) => {
          expect(field).toBe('phone');
          expect(message).toContain('phone');
          expect(typeof message).toBe('string');
          expect(message.length).toBeGreaterThan(0);
        });
      });

      it('should provide specific error messages for address validation', () => {
        const addressErrors = [
          { field: 'address1', message: 'Address line 1 must be less than 255 characters', input: 'a'.repeat(256) },
          { field: 'city', message: 'City can only contain letters, spaces, hyphens, and apostrophes', input: 'City123' },
          { field: 'zipCode', message: 'Please provide a valid ZIP code', input: '1234' }
        ];

        addressErrors.forEach(({ field, message, input }) => {
          expect(['address1', 'address2', 'city', 'state', 'zipCode', 'country']).toContain(field);
          expect(typeof message).toBe('string');
          expect(message.length).toBeGreaterThan(0);
        });
      });
    });

    describe('Custom validation error messages', () => {
      it('should provide specific error messages for password change validation', () => {
        const passwordChangeErrors = [
          { field: 'currentPassword', message: 'Current password is required', input: '' },
          { field: 'newPassword', message: 'New password must be different from current password', input: 'samePassword' },
          { field: 'confirmPassword', message: 'Password confirmation does not match', input: 'differentPassword' }
        ];

        passwordChangeErrors.forEach(({ field, message, input }) => {
          expect(['currentPassword', 'newPassword', 'confirmPassword']).toContain(field);
          expect(message).toMatch(/password/i);
          expect(typeof message).toBe('string');
          expect(message.length).toBeGreaterThan(0);
        });
      });

      it('should provide specific error messages for Google auth validation', () => {
        const googleAuthErrors = [
          { field: 'idToken', message: 'Google ID token is required', input: '' },
          { field: 'idToken', message: 'Invalid token format', input: 'a'.repeat(2049) }
        ];

        googleAuthErrors.forEach(({ field, message, input }) => {
          expect(field).toBe('idToken');
          expect(message).toMatch(/token/i);
          expect(typeof message).toBe('string');
          expect(message.length).toBeGreaterThan(0);
        });
      });
    });

    describe('Error message formatting and consistency', () => {
      it('should format error messages consistently', () => {
        const consistencyTests = [
          { message: 'Please provide a valid email address', hasProperCapitalization: true },
          { message: 'Password must be between 8 and 128 characters', hasProperCapitalization: true },
          { message: 'First name must be between 1 and 50 characters', hasProperCapitalization: true },
          { message: 'Username can only contain letters, numbers, underscores, and hyphens', hasProperCapitalization: true }
        ];

        consistencyTests.forEach(({ message, hasProperCapitalization }) => {
          // Check that message starts with capital letter
          expect(message.charAt(0)).toBe(message.charAt(0).toUpperCase());
          // Check that message doesn't end with period (consistent format)
          expect(message.endsWith('.')).toBe(false);
          // Check minimum length
          expect(message.length).toBeGreaterThan(10);
          // Check for proper capitalization
          expect(hasProperCapitalization).toBe(true);
        });
      });

      it('should handle error message field mapping correctly', () => {
        const fieldMappingTests = [
          { originalPath: 'email', expectedField: 'email' },
          { originalPath: 'password', expectedField: 'password' },
          { originalPath: 'firstName', expectedField: 'firstName' },
          { originalPath: 'lastName', expectedField: 'lastName' },
          { originalPath: 'username', expectedField: 'username' },
          { originalPath: 'phone', expectedField: 'phone' }
        ];

        fieldMappingTests.forEach(({ originalPath, expectedField }) => {
          const mockErrors = [{ path: originalPath, msg: 'Test error message' }];
          const mockResult = {
            isEmpty: jest.fn(() => false),
            array: jest.fn(() => mockErrors)
          };
          mockValidationResult.mockReturnValue(mockResult);

          handleValidationErrors(req, res, next);

          expect(res.json).toHaveBeenCalledWith({
            error: 'Validation failed',
            details: [{ field: expectedField, message: 'Test error message' }]
          });
        });
      });

      it('should maintain consistent error response structure', () => {
        const mockErrors = [
          { path: 'testField', msg: 'Test error message' }
        ];
        const mockResult = {
          isEmpty: jest.fn(() => false),
          array: jest.fn(() => mockErrors)
        };
        mockValidationResult.mockReturnValue(mockResult);

        handleValidationErrors(req, res, next);

        const responseCall = res.json.mock.calls[0][0];

        // Verify response structure
        expect(responseCall).toHaveProperty('error');
        expect(responseCall).toHaveProperty('details');
        expect(responseCall.error).toBe('Validation failed');
        expect(Array.isArray(responseCall.details)).toBe(true);
        expect(responseCall.details.length).toBeGreaterThan(0);

        // Verify detail structure
        responseCall.details.forEach(detail => {
          expect(detail).toHaveProperty('field');
          expect(detail).toHaveProperty('message');
          expect(typeof detail.field).toBe('string');
          expect(typeof detail.message).toBe('string');
          expect(detail.field.length).toBeGreaterThan(0);
          expect(detail.message.length).toBeGreaterThan(0);
        });
      });
    });

    describe('Edge cases in error handling', () => {
      it('should handle empty error arrays gracefully', () => {
        const mockResult = {
          isEmpty: jest.fn(() => false),
          array: jest.fn(() => [])
        };
        mockValidationResult.mockReturnValue(mockResult);

        handleValidationErrors(req, res, next);

        expect(res.json).toHaveBeenCalledWith({
          error: 'Validation failed',
          details: []
        });
      });

      it('should handle malformed error objects', () => {
        const malformedErrors = [
          { path: undefined, msg: 'Test message' },
          { path: 'testField', msg: undefined },
          { path: null, msg: 'Test message' },
          { path: 'testField', msg: null }
        ];

        const mockResult = {
          isEmpty: jest.fn(() => false),
          array: jest.fn(() => malformedErrors)
        };
        mockValidationResult.mockReturnValue(mockResult);

        handleValidationErrors(req, res, next);

        const responseCall = res.json.mock.calls[0][0];
        expect(responseCall.details).toHaveLength(4);

        // Verify handling of undefined/null values
        responseCall.details.forEach(detail => {
          expect(detail).toHaveProperty('field');
          expect(detail).toHaveProperty('message');
        });
      });

      it('should handle very long error messages', () => {
        const longMessage = 'a'.repeat(1000);
        const mockErrors = [{ path: 'testField', msg: longMessage }];
        const mockResult = {
          isEmpty: jest.fn(() => false),
          array: jest.fn(() => mockErrors)
        };
        mockValidationResult.mockReturnValue(mockResult);

        handleValidationErrors(req, res, next);

        expect(res.json).toHaveBeenCalledWith({
          error: 'Validation failed',
          details: [{ field: 'testField', message: longMessage }]
        });
      });

      it('should handle special characters in error messages', () => {
        const specialCharMessages = [
          'Message with "quotes" and \'apostrophes\'',
          'Message with <html> tags',
          'Message with unicode characters: ñáéíóú',
          'Message with newlines\nand\ttabs',
          'Message with symbols: @#$%^&*()'
        ];

        specialCharMessages.forEach((message) => {
          const mockErrors = [{ path: 'testField', msg: message }];
          const mockResult = {
            isEmpty: jest.fn(() => false),
            array: jest.fn(() => mockErrors)
          };
          mockValidationResult.mockReturnValue(mockResult);

          handleValidationErrors(req, res, next);

          expect(res.json).toHaveBeenCalledWith({
            error: 'Validation failed',
            details: [{ field: 'testField', message: message }]
          });
        });
      });
    });
  });

});