package com.agilex.healthcare.schedulingmanager.validator;

import com.agilex.healthcare.mobilehealthplatform.domain.ValidationError;
import com.agilex.healthcare.mobilehealthplatform.domain.ValidationResult;
import com.agilex.healthcare.mobilehealthplatform.validator.ValidatorHelper;
import com.agilex.healthcare.schedulingmanager.domain.SMAppointmentRequest;
import com.agilex.healthcare.schedulingmanager.enumeration.AppointmentRequestPurposeOfVisit;
import com.agilex.healthcare.utility.DateHelper;
import com.agilex.healthcare.utility.NullChecker;

import java.util.Calendar;
import java.util.Date;
import java.util.Set;

public class AppointmentRequestValidator {
	

	private static final String ENTRY_TYPE = "Appointment Request";
	
	private static final String OPTION_DATE_1 = "optionDate1";
	private static final String OPTION_DATE_2 = "optionDate2";
	private static final String OPTION_DATE_3 = "optionDate3";
	private static final String EMAIL = "email";
	private static final String TEXT_MESSAGING_PHONE_NUMBER = "textMessagingPhoneNumber";
	private static final String PHONE_NUMBER = "phoneNumber";
	private static final String APPOINTMENT_TYPE = "appointmentType";
	private static final String VISIT_TYPE = "visitType";
	private static final String FACILITY_NAME = "facilityName";
	private static final String PURPOSE_OF_VISIT = "purposeOfVisit";
	private static final String OTHER_PURPOSE_OF_VISIT = "otherPurposeOfVisit";
	private static final String BEST_TIME_TO_CALL = "bestTimetoCall";
	private static final String PROVIDER = "provider";
	private static final String BOOKED_APPT_DATETIME = "bookedApptDateTime";
	
	private String RequestedDateErrorMessage = "%s is required. Please choose a date no earlier than 5 days and no later than 120 days";
	
	private int earliestTimeVeteranCanRequestAppointmentInHours;
	private int farthestTimeVeteranCanRequestAppointmentInDays;
	
	public AppointmentRequestValidator(int earliestTimeVeteranCanRequestAppointmentInHours, int farthestTimeVeteranCanRequestAppointmentInDays) {
		this.earliestTimeVeteranCanRequestAppointmentInHours = earliestTimeVeteranCanRequestAppointmentInHours;
		this.farthestTimeVeteranCanRequestAppointmentInDays = farthestTimeVeteranCanRequestAppointmentInDays;
	}
	
	public ValidationResult<SMAppointmentRequest> validate(SMAppointmentRequest SMAppointmentRequest, boolean fullValidation) {
		ValidationResult<SMAppointmentRequest> validationResult = new ValidationResult<SMAppointmentRequest>();
		validationResult.setRequestObject(SMAppointmentRequest);

		if (SMAppointmentRequest == null) {
			validationResult.addIfNotNull(new ValidationError(String.format("%s is required", ENTRY_TYPE), ENTRY_TYPE));
		} else {
			validateFacility(validationResult, SMAppointmentRequest);
			validatePhoneNumber(validationResult, SMAppointmentRequest.getPhoneNumber());
			validateAppointmentType(validationResult, SMAppointmentRequest.getAppointmentType());
			if(SMAppointmentRequest.isRequestedPhoneCall() == false){
				validateRequestedDateOptions(validationResult, SMAppointmentRequest, fullValidation);
				validateTextMessaging(validationResult, SMAppointmentRequest.getTextMessagingPhoneNumber());
				//validateEmail(validationResult, appointmentRequest.getEmail());
				validateProvider(validationResult, SMAppointmentRequest.getProviderId());
				validateVisitType(validationResult, SMAppointmentRequest.getVisitType());
				validatePurposeOfVisit(validationResult, SMAppointmentRequest.getPurposeOfVisit(), SMAppointmentRequest.getOtherPurposeOfVisit());
				validateProvider(validationResult, SMAppointmentRequest.getProviderId());
			}
			else{
				validateBestTimetoCall(validationResult, SMAppointmentRequest.getBestTimetoCall());
			}
		}
		
		return validationResult;
	}
	
	private void validateFacility(ValidationResult<SMAppointmentRequest> validationResult, SMAppointmentRequest SMAppointmentRequest) {
		
		if (SMAppointmentRequest.getFacility() == null) {
			validationResult.addIfNotNull(new ValidationError(String.format("%s is required", "Facility Name"), FACILITY_NAME));
		} else if (SMAppointmentRequest.getFacility().getName() == null || SMAppointmentRequest.getFacility().getName().isEmpty()) {
			validationResult.addIfNotNull(new ValidationError(String.format("%s is required", "Facility Name"), FACILITY_NAME));
		}
	}
	
	private void validateBestTimetoCall(ValidationResult<SMAppointmentRequest> validationResult, Set<String> bestTimetoCall) {
		if (NullChecker.isNullish(bestTimetoCall) || bestTimetoCall.size() == 0 ) {
			validationResult.addIfNotNull(new ValidationError("Best time to call must not be empty", BEST_TIME_TO_CALL));
		}
	}

	private void validateRequestedDateOptions(ValidationResult<SMAppointmentRequest> validationResult, SMAppointmentRequest SMAppointmentRequest, boolean fullValidation){

		boolean areDatesValid = true;
		
		if (!SMAppointmentRequest.isSecondRequest()) {
			if (!(NullChecker.isNotNullish(SMAppointmentRequest.getOptionDate1()) && SMAppointmentRequest.getOptionDate1().equalsIgnoreCase("No Date Selected")) && !isValidDate(DateHelper.parseDate(SMAppointmentRequest.getOptionDate1()), fullValidation)){
				validationResult.addIfNotNull(new ValidationError(String.format(RequestedDateErrorMessage, "Date Preference 1"), OPTION_DATE_1));
				areDatesValid = false;
			}
		}

		if (!(NullChecker.isNotNullish(SMAppointmentRequest.getOptionDate2()) && SMAppointmentRequest.getOptionDate2().equalsIgnoreCase("No Date Selected")) && !isValidDate(DateHelper.parseDate(SMAppointmentRequest.getOptionDate2()), fullValidation)){
			validationResult.addIfNotNull(new ValidationError(String.format(RequestedDateErrorMessage, "Date Preference 2"), OPTION_DATE_2));
			areDatesValid = false;
		}

		if (!(NullChecker.isNotNullish(SMAppointmentRequest.getOptionDate3()) &&  SMAppointmentRequest.getOptionDate3().equalsIgnoreCase("No Date Selected")) && !isValidDate(DateHelper.parseDate(SMAppointmentRequest.getOptionDate3()), fullValidation)){
			validationResult.addIfNotNull(new ValidationError(String.format(RequestedDateErrorMessage, "Date Preference 3"), OPTION_DATE_3));
			areDatesValid = false;
		}

		if(areDatesValid){
			validationResult.addAll(AppointmentRequestValidatorHelper.validateUniqueOptions(SMAppointmentRequest, OPTION_DATE_1, OPTION_DATE_2, OPTION_DATE_3));
		}
	}
	
	private boolean isValidDate(Date requestedDate, boolean fullValidation){
		boolean result = false;
		
		if (NullChecker.isNotNullish(requestedDate)){
			if (fullValidation) {
				result = isDateInTheValidRange(requestedDate);
			} else {
				result = true;
			}
		}
		
		return result;
	}
	
	private boolean isDateInTheValidRange(Date requestedDate) {

		Date validStartDate = getDateComponent(DateHelper.plusDays(new Date(), earliestTimeVeteranCanRequestAppointmentInHours / 24));
		Date validEndDate = getDateComponent(DateHelper.plusDays(new Date(), farthestTimeVeteranCanRequestAppointmentInDays));
		requestedDate = getDateComponent(requestedDate);
		
		if (requestedDate.before(validStartDate) || requestedDate.after(validEndDate)){
			return false;
		}
		
		return true;
	}

	private Date getDateComponent(Date date){
		
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		date = cal.getTime();
		
		return date;
	}
	
	private void validatePhoneNumber(ValidationResult<SMAppointmentRequest> validationResult, String phoneNumber) {
		
		if (NullChecker.isNullish(phoneNumber) || !ValidatorHelper.isPhoneNumberLengthValid(phoneNumber) ||
				!ValidatorHelper.isPhoneNumberHasMinDigits(phoneNumber) ||
				!ValidatorHelper.isPhoneNumberValidCharacters(phoneNumber)){

			validationResult.addIfNotNull(new ValidationError("Phone number is required and must have a minimum of 10 digits and only contain digits and these characters: + ( ) - . x space", PHONE_NUMBER));
		}
	}
	
	private void validateTextMessaging(ValidationResult<SMAppointmentRequest> validationResult, String textMessagingPhoneNumber) {
		
		if (NullChecker.isNotNullish(textMessagingPhoneNumber) && (!ValidatorHelper.isPhoneNumberLengthValid(textMessagingPhoneNumber) ||
				!ValidatorHelper.isPhoneNumberHasMinDigits(textMessagingPhoneNumber) ||
				!ValidatorHelper.isPhoneNumberValidCharacters(textMessagingPhoneNumber))){

			validationResult.addIfNotNull(new ValidationError("Mobile phone number for receiving text messages must have a minimum of 10 digits and only contain digits and these characters: + ( ) - . x space", TEXT_MESSAGING_PHONE_NUMBER));
		}
	}

	private void validateEmail(ValidationResult<SMAppointmentRequest> validationResult, String email) {
		if(!NullChecker.isNullish(email)){
			if(! ValidatorHelper.isEmailLengthValid(email) || ! ValidatorHelper.isEmailAddressFormatValid(email)){
			validationResult.addIfNotNull(new ValidationError("Email is required and must be between 3 and 50 characters in a valid email format", EMAIL));
			}
		}
	}
	
	private void validateAppointmentType(ValidationResult<SMAppointmentRequest> validationResult, String appointmentType) {
		
		if (NullChecker.isNullish(appointmentType) || !AppointmentRequestValidatorHelper.isValidAppointmentType(appointmentType)) {
			validationResult.addIfNotNull(new ValidationError("Type of Care is required", APPOINTMENT_TYPE));
		}
	}
	
	private void validateVisitType(ValidationResult<SMAppointmentRequest> validationResult, String visitType) {
		
		if (NullChecker.isNullish(visitType)) {
			validationResult.addIfNotNull(new ValidationError("Type of Visit is required", VISIT_TYPE));
		}
	
	}
	
	private void validatePurposeOfVisit(ValidationResult<SMAppointmentRequest> validationResult, String purposeOfVisit, String otherPurposeOfVisit) {
		
		if (NullChecker.isNullish(purposeOfVisit)) {
			validationResult.addIfNotNull(new ValidationError("Purpose Of Visit is required", PURPOSE_OF_VISIT));
		} else {
			if (purposeOfVisit.equals(AppointmentRequestPurposeOfVisit.OTHER.getName())) {
				if (NullChecker.isNullish(purposeOfVisit)) {
					validationResult.addIfNotNull(new ValidationError("Other Purpose Of Visit is required", OTHER_PURPOSE_OF_VISIT));
				} else if (otherPurposeOfVisit.length() > 100) {
					validationResult.addIfNotNull(new ValidationError("Other Purpose Of Visit must be less than 100 characters", OTHER_PURPOSE_OF_VISIT));
				}
			}
		}
	}
	
	private void validateProvider(ValidationResult<SMAppointmentRequest> validationResult, String providerId) {
		
		if (NullChecker.isNullish(providerId)) {
			validationResult.addIfNotNull(new ValidationError("Valid provider ID is required", PROVIDER));
		}
	}
	
	private void validateBookedApptDateTime(ValidationResult<SMAppointmentRequest> validationResult, Date bookedApptDateTime){
		if (NullChecker.isNullish(bookedApptDateTime)) {
			validationResult.addIfNotNull(new ValidationError("Booked appointment date is required", BOOKED_APPT_DATETIME));
		}
	}
}
