import validate from '../../middleware/validators/validate'
import { ValidationRule } from '../../types/controller';
import { Request } from "express";

const mockNumberValidatorPass = {} as ValidationRule<number>;
const mockNotNullOrEmptyValidatorPass = {} as ValidationRule<number>;
const mockNumberValidatorFails = {} as ValidationRule<number>;
const mockNotNullOrEmptyValidatorFails = {} as ValidationRule<number>;

mockNotNullOrEmptyValidatorPass.validate = jest.fn(() => true);
mockNumberValidatorPass.validate = jest.fn(() => true);

mockNotNullOrEmptyValidatorFails.validate = jest.fn(() => false);
mockNumberValidatorFails.validate = jest.fn(() => false);

const createMockReq = (body: object) => {
    return {
      body: body,
      session: { id: '0', formErrors: [], error: {} },
      query: { hasError: false }
    } as unknown as Request
  };


describe('Form validation function tests', () => {

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

    test('When no validators provided, validate as true', () => {
        const validators = [] as ValidationRule<number>[];
        const req = createMockReq({number: 1});
        const result = validate(req, validators);
        expect(result).toBe(true);
    });

    test('When given a valid single number with a single validator, validate as true', () => {
        const validators = [] as ValidationRule<number>[];
        mockNumberValidatorPass.fieldNames = ['number'];
        validators.push(mockNumberValidatorPass);
        const req = createMockReq({ number: 1 });
        const result = validate(req, validators);
        expect(mockNumberValidatorPass.validate).toHaveBeenCalledTimes(1);
        expect(result).toBe(true);
    });

    test('When given an invalid single number with a single validator, validates as false', () => {
        const validators = [] as ValidationRule<number>[];
        mockNumberValidatorFails.fieldNames = ['number'];
        mockNumberValidatorFails.validate = jest.fn(() => false)
        validators.push(mockNumberValidatorFails);
        const req = createMockReq({ number: 1 });
        const result = validate(req, validators);
        expect(mockNumberValidatorFails.validate).toHaveBeenCalledTimes(1);
        expect(result).toBe(false);
    });

    test('When given multiple validators, with a single number that is valid against all, validates as true', () => {
        
        const validators = [] as ValidationRule<number>[];
            
        mockNumberValidatorPass.fieldNames = ['number'];
        mockNotNullOrEmptyValidatorPass.fieldNames = ['number'];
            
        validators.push(mockNumberValidatorPass);
        validators.push(mockNotNullOrEmptyValidatorPass);

        const req = createMockReq({ number: 1 });
        const result = validate(req, validators);

        expect(mockNumberValidatorPass.validate).toHaveBeenCalledTimes(1);
        expect(mockNotNullOrEmptyValidatorPass.validate).toHaveBeenCalledTimes(1);
        expect(result).toBe(true);
    });
    
    test('When given multiple validators with a single value that is invalid for all, validates as false', () => {
        
        const validators = [] as ValidationRule<number>[];
            
        mockNumberValidatorFails.fieldNames = ['number'];
        mockNotNullOrEmptyValidatorFails.fieldNames = ['number'];
            
        validators.push(mockNumberValidatorFails);
        validators.push(mockNotNullOrEmptyValidatorFails);

        const req = createMockReq({ number: 1 });
        const result = validate(req, validators);

        expect(mockNumberValidatorFails.validate).toHaveBeenCalledTimes(1);
        expect(mockNotNullOrEmptyValidatorFails.validate).toHaveBeenCalledTimes(1);
        expect(result).toBe(false);
    });
    test('When given multiple validators and multiple valid numbers, validates as true', () => {
        
        const validators = [] as ValidationRule<number>[];
            
        mockNumberValidatorPass.fieldNames = ['number', 'cost'];
        mockNotNullOrEmptyValidatorPass.fieldNames = ['number', 'cost'];
            
        validators.push(mockNumberValidatorPass);
        validators.push(mockNotNullOrEmptyValidatorPass);

        const req = createMockReq({  number: 1, cost: 2 });
        const result = validate(req, validators);

        expect(mockNumberValidatorPass.validate).toHaveBeenCalledTimes(2);
        expect(mockNotNullOrEmptyValidatorPass.validate).toHaveBeenCalledTimes(2);
        expect(result).toBe(true);
    });
     test('When given multiple validators and multiple numbers that are valid only against one validator, validates as false', () => {
        
        const validators = [] as ValidationRule<number>[];
            
        mockNumberValidatorPass.fieldNames = ['number', 'cost'];
        mockNotNullOrEmptyValidatorFails.fieldNames = ['number', 'cost'];
            
        validators.push(mockNumberValidatorPass);
        validators.push(mockNotNullOrEmptyValidatorFails);

        const req = createMockReq({  number: 1, cost: 2 });
        const result = validate(req, validators);

        expect(mockNumberValidatorPass.validate).toHaveBeenCalledTimes(2);
        expect(mockNotNullOrEmptyValidatorFails.validate).toHaveBeenCalledTimes(2);
        expect(result).toBe(false);
    });
})