package com.agilex.healthcare.schedulingmanager.validator;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import org.junit.Ignore;
import org.junit.Test;

import com.agilex.healthcare.mobilehealthplatform.domain.Facility;
import com.agilex.healthcare.mobilehealthplatform.domain.ValidationError;
import com.agilex.healthcare.mobilehealthplatform.domain.ValidationResult;
import com.agilex.healthcare.mobilehealthplatform.enumeration.AppointmentRequestType;
import com.agilex.healthcare.mobilehealthplatform.enumeration.AppointmentRequestVisitType;
import com.agilex.healthcare.schedulingmanager.domain.SMAppointmentRequest;
import com.agilex.healthcare.schedulingmanager.domain.SchedulingFacility;
import com.agilex.healthcare.schedulingmanager.enumeration.AppointmentRequestPurposeOfVisit;
import com.agilex.healthcare.utility.DateHelper;

public class AppointmentRequestValidatorTest {
	com.agilex.healthcare.schedulingmanager.validator.AppointmentRequestValidator validator = new AppointmentRequestValidator(96, 120);
    private HashSet<String> bestTimetoCall;

    @Test
    public void verifyAppointmentRequestValid() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, true);
        assertEquals(true, result.isValid());
    }

    @Test
    public void verifyNull() {
    	SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setAppointmentType(null);
        appointmentRequest.setVisitType(null);
        appointmentRequest.setFacility(null);
        appointmentRequest.setEmail(null);
        appointmentRequest.setPhoneNumber(null);
        appointmentRequest.setOptionDate1(null);
        appointmentRequest.setOptionTime1(null);
        appointmentRequest.setOptionDate2(null);
        appointmentRequest.setOptionTime2(null);
        appointmentRequest.setOptionDate3(null);
        appointmentRequest.setOptionTime3(null);
        appointmentRequest.setBestTimetoCall(null);
        appointmentRequest.setPurposeOfVisit(null);
        appointmentRequest.setProviderId(null);
        appointmentRequest.setBookedApptDateTime(null);
        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
        assertEquals(10, result.getErrors().size());
    }

    @Test
    public void verifyNotEmpty() {
        String facilityAddress = "";
        String facilityCity = "";
        String facilityState = "";
        String facilityCode = "";
        String facilityName = "";
        String facilityParentSiteCode = "";
        String facilityType = "";

        Facility facility = new Facility();
        facility.setAddress(facilityAddress);
        facility.setCity(facilityCity);
        facility.setFacilityCode(facilityCode);
        facility.setName(facilityName);
        facility.setParentSiteCode(facilityParentSiteCode);
        facility.setState(facilityState);
        facility.setType(facilityType);

        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setAppointmentType("");
        appointmentRequest.setVisitType("");
        appointmentRequest.setFacility(new SchedulingFacility());
        appointmentRequest.setEmail("");
        appointmentRequest.setPhoneNumber("");
        appointmentRequest.setOptionDate1("");
        appointmentRequest.setOptionTime1("");
        appointmentRequest.setOptionDate2("");
        appointmentRequest.setOptionTime2("");
        appointmentRequest.setOptionDate3("");
        appointmentRequest.setOptionTime3("");
        appointmentRequest.setBestTimetoCall(bestTimetoCall);
        appointmentRequest.setPurposeOfVisit("");
        appointmentRequest.setProviderId("");
        appointmentRequest.setBookedApptDateTime(DateHelper.getToday());

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
        assertEquals(10, result.getErrors().size());
    }

    @Test
    public void verifyInvalidMinLengthTextMessaging() {
    	SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setTextMessagingPhoneNumber("5555-5555");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyInvalidMaxLengthTextMessaging() {
    	SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setTextMessagingPhoneNumber("5555-5555-5555-5555-5555-5555-5555-5555-5555-5555-5");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyInvalidMinDigitTextMessaging() {
    	SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setTextMessagingPhoneNumber("5---------");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyInvalidCharactersInTextMessaging() {
        List<String> PhoneNumbersWithInvalidCharacters = new LinkedList<String>();
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555!");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555@");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555#");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555$");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555%");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555^");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555&");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555*");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555=");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555_");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555{");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555}");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555[");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555]");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555\\");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555|");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555?");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555,");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555<");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555>");

        for (String PhoneNumberWithInvalidCharacters : PhoneNumbersWithInvalidCharacters) {
        	SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
            appointmentRequest.setTextMessagingPhoneNumber(PhoneNumberWithInvalidCharacters);

            ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
            assertEquals(false, result.isValid());
        }
    }

    @Test
    public void verifyEmptyTextMessagingOk() {
    	SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setTextMessagingPhoneNumber("");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(true, result.isValid());
    }

    @Test
    public void verifyNullTextMessagingOk() {
    	SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setTextMessagingPhoneNumber(null);

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(true, result.isValid());
    }

    @Test
    public void verifyInvalidMinLengthPhoneNumber() {
    	SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setPhoneNumber("5555-5555");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyInvalidMaxLengthPhoneNumber() {
    	SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setPhoneNumber("5555-5555-5555-5555-5555-5555-5555-5555-5555-5555-5");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyInvalidMinDigitPhoneNumber() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setPhoneNumber("5---------");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyInvalidCharactersInPhoneNumber() {
        List<String> PhoneNumbersWithInvalidCharacters = new LinkedList<String>();
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555!");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555@");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555#");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555$");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555%");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555^");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555&");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555*");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555=");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555_");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555{");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555}");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555[");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555]");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555\\");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555|");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555?");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555,");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555<");
        PhoneNumbersWithInvalidCharacters.add("+1 (555) 555-5555>");

        for (String PhoneNumberWithInvalidCharacters : PhoneNumbersWithInvalidCharacters) {
            SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
            appointmentRequest.setPhoneNumber(PhoneNumberWithInvalidCharacters);

            ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
            assertEquals(false, result.isValid());
        }
    }

    //Just a simple test to ensure email validators are invoked as part of appointment request validation
    //more detailed email tests are found in ValidatorHelperTest
    @Test
    public void verifyValidEmail() {
        String validEmail = "niceandsimple@example.com";

        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setEmail(validEmail);

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertTrue(result.isValid());
    }

    //Just a simple test to ensure email validators are invoked as part of appointment request validation
    //more detailed email tests are found in ValidatorHelperTest
    //10/06/2014: No email validation will be required
    @Ignore
    public void verifyInvalidEmail() {
        String invalidEmail = "Abc.example.com";

        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setEmail(invalidEmail);

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertFalse(result.isValid());
    }

    @Test
    public void verifyInvalidAppointmentType() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();
        appointmentRequest.setAppointmentType("INVALID_TYPE");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyNonUniqueOptions12() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, 7);
        String nonUniqueDateString = DateHelper.formatDate(cal.getTime());
        appointmentRequest.setOptionDate1(nonUniqueDateString);
        appointmentRequest.setOptionTime1("AM");
        appointmentRequest.setOptionDate2(nonUniqueDateString);
        appointmentRequest.setOptionTime2("AM");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyNonUniqueOptions23() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, 7);
        String nonUniqueDateString = DateHelper.formatDate(cal.getTime());
        appointmentRequest.setOptionDate2(nonUniqueDateString);
        appointmentRequest.setOptionTime2("AM");
        appointmentRequest.setOptionDate3(nonUniqueDateString);
        appointmentRequest.setOptionTime3("AM");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyNonUniqueOptions13() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, 7);
        String nonUniqueDateString = DateHelper.formatDate(cal.getTime());
        appointmentRequest.setOptionDate1(nonUniqueDateString);
        appointmentRequest.setOptionTime1("AM");
        appointmentRequest.setOptionDate3(nonUniqueDateString);
        appointmentRequest.setOptionTime3("AM");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyInvalidMinTimeOption() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, 2);
        appointmentRequest.setOptionDate1(DateHelper.formatDate(cal.getTime()));
        appointmentRequest.setOptionTime1("AM");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, true);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyInvalidMinTimeOptionIsValidWithNonFullValidation() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, 2);
        appointmentRequest.setOptionDate1(DateHelper.formatDate(cal.getTime()));
        appointmentRequest.setOptionTime1("AM");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(true, result.isValid());
    }

    @Test
    public void verifyDateOptionOfFiveDaysFromTodayIsValid() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, 5);
        appointmentRequest.setOptionDate1(DateHelper.formatDate(cal.getTime()));
        appointmentRequest.setOptionTime1("PM");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, true);
        assertEquals(true, result.isValid());
    }

    @Test
    public void verifyInvalidMaxTimeOption() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, 121);
        appointmentRequest.setOptionDate1(DateHelper.formatDate(cal.getTime()));
        appointmentRequest.setOptionTime1("AM");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, true);
        assertEquals(false, result.isValid());
    }

    @Test
    public void verifyInvalidMaxTimeOptionWithNonFullValidation() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, 121);
        appointmentRequest.setOptionDate1(DateHelper.formatDate(cal.getTime()));
        appointmentRequest.setOptionTime1("AM");

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(true, result.isValid());
    }

    @Test
    public void verifyInvalidOtherPurposeOfVisitOption() {
        SMAppointmentRequest appointmentRequest = createValidAppointmentRequest();

        String textOver100Characters = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901";
        appointmentRequest.setOtherPurposeOfVisit(textOver100Characters);

        ValidationResult<SMAppointmentRequest> result = validator.validate(appointmentRequest, false);
        assertEquals(false, result.isValid());
    }

    private SMAppointmentRequest createValidAppointmentRequest() {
        String appointmentType = AppointmentRequestType.MENTAL_HEALTH.getName();
        String visitType = AppointmentRequestVisitType.OFFICE_VISIT.getName();
        String email = "test@agilex.com";
        String phoneNumber = "+1 (555) 555-5555 x55.55";
        String purposeOfVisit = AppointmentRequestPurposeOfVisit.OTHER.getName();
        String otherPurposeOfVisit = "test other purpose of visit";
        String providerId = "PROV1";

        String facilityAddress = "test address";
        String facilityCity = "test city";
        String facilityState = "test state";
        String facilityCode = "test facility code";
        String facilityName = "Test Facility";
        String facilityParentSiteCode = "test parent site code";
        String facilityType = "test facility type";

        SchedulingFacility facility = new SchedulingFacility();
        facility.setAddress(facilityAddress);
        facility.setCity(facilityCity);
        facility.setFacilityCode(facilityCode);
        facility.setName(facilityName);
        facility.setParentSiteCode(facilityParentSiteCode);
        facility.setState(facilityState);
        facility.setType(facilityType);

        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, 7);
        String optionDate1 = DateHelper.formatDate(calendar.getTime());
        String optionTime1 = "AM";
        calendar.add(Calendar.DATE, 1);
        String optionDate2 = DateHelper.formatDate(calendar.getTime());
        String optionTime2 = "PM";
        calendar.add(Calendar.DATE, 2);
        String optionDate3 = DateHelper.formatDate(calendar.getTime());
        String optionTime3 = "AM";
        Date bookedApptDateTime = DateHelper.getToday();
        bestTimetoCall = new HashSet<String>();
        bestTimetoCall.add("9 AM - 11 AM");
        bestTimetoCall.add("11 PM - 1 PM");


        SMAppointmentRequest a = new SMAppointmentRequest();

        a.setAppointmentType(appointmentType);
        a.setVisitType(visitType);
        a.setFacility(facility);
        a.setEmail(email);
        a.setPhoneNumber(phoneNumber);
        a.setOptionDate1(optionDate1);
        a.setOptionTime1(optionTime1);
        a.setOptionDate2(optionDate2);
        a.setOptionTime2(optionTime2);
        a.setOptionDate3(optionDate3);
        a.setOptionTime3(optionTime3);
        a.setBestTimetoCall(bestTimetoCall);
        a.setPurposeOfVisit(purposeOfVisit);
        a.setOtherPurposeOfVisit(otherPurposeOfVisit);
        a.setProviderId(providerId);
        a.setBookedApptDateTime(bookedApptDateTime);

        return a;
    }

    /**
     * Call before assertion to print error messages for debugging
     *
     * @param result
     */
    @SuppressWarnings("unused")
    private void printErrors(ValidationResult<SMAppointmentRequest> result) {
        for (ValidationError error : result.getErrors()) {
            System.out.println(String.format("%s - %s", error.getFieldName(), error.getErrorMessage()));
        }
    }
}
