package gov.va.med.esr.common.batchprocess;

import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.ee.EnrollmentDetermination;
import gov.va.med.esr.common.model.lookup.EnrollmentPriorityGroup;
import gov.va.med.esr.common.model.lookup.EnrollmentPrioritySubGroup;
import gov.va.med.esr.common.model.lookup.EnrollmentStatus;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.rule.parameter.CommunicationsInputParameter;
import gov.va.med.esr.common.rule.service.CommunicationRuleService;
import gov.va.med.esr.common.rule.service.EligibilityEnrollmentRuleService;
import gov.va.med.esr.common.rule.service.EventRuleService;
import gov.va.med.esr.common.rule.service.RuleValidationService;
import gov.va.med.esr.common.util.FeedbackTask;
import gov.va.med.esr.common.util.TimerFactory;
import gov.va.med.esr.service.PSDelegateService;
import gov.va.med.esr.service.PersonLockedException;
import gov.va.med.esr.service.PersonService;
import gov.va.med.fw.rule.RuleException;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.trigger.TriggerRouter;
import gov.va.med.fw.validation.ValidationMessages;
import gov.va.med.fw.validation.ValidationServiceException;

import java.io.IOException;
import java.util.Timer;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspWriter;

import org.apache.commons.logging.Log;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

/**
 * Re-applies determine enrollment rules to a veteran, logs errors for those
 * veteran's moved to a priority group other than 8b/d or if the enrollment
 * status after the move is not verified. Otherwise, re-applies send letter
 * rules, sends special 623R letter, and saves the updated veteran.
 * 
 * @author
 * 
 */
public class VeteranUpdaterProcessImpl implements BeanFactoryAware,
        VeteranUpdaterProcess {

    private int delay = 30000;

    private BeanFactory beanFactory;

    private Log logStatus;

    private HttpServletResponse httpServletResponse;

    private JspWriter jspWriter;

    private TimerFactory timerFactory;

    private PSDelegateService psDelegateService;

    private EligibilityEnrollmentRuleService eligibilityEnrollmentRuleService;

    private CommunicationRuleService communicationRuleService;

    private TriggerRouter triggerRouter;

    private EventRuleService eventRuleService;

    private PersonService personService;

    private RuleValidationService ruleValidationService;

    public void setRuleValidationService(
            RuleValidationService ruleValidationService) {
        this.ruleValidationService = ruleValidationService;
    }

    public void setPersonService(PersonService personService) {
        this.personService = personService;
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    /*
     * (non-Javadoc)
     * 
     * @see gov.va.med.esr.common.batchprocess.VeteranUpdaterProcess#setHttpServletResponse(javax.servlet.http.HttpServletResponse)
     */
    public void setHttpServletResponse(HttpServletResponse httpServletResponse) {
        this.httpServletResponse = httpServletResponse;
    }

    public void setEligibilityEnrollmentRuleService(
            EligibilityEnrollmentRuleService eligibilityEnrollmentRuleService) {
        this.eligibilityEnrollmentRuleService = eligibilityEnrollmentRuleService;
    }

    public void setCommunicationRuleService(
            CommunicationRuleService communicationRuleService) {
        this.communicationRuleService = communicationRuleService;
    }

    public void setTriggerRouter(TriggerRouter triggerRouter) {
        this.triggerRouter = triggerRouter;
    }

    public void setEventRuleService(EventRuleService eventRuleService) {
        this.eventRuleService = eventRuleService;
    }

    public void setPsDelegateService(PSDelegateService psDelegateService) {
        this.psDelegateService = psDelegateService;
    }

    public void setTimerFactory(TimerFactory timerFactory) {
        this.timerFactory = timerFactory;
    }

    public void setLogStatus(Log logStatus) {
        this.logStatus = logStatus;
    }

    /*
     * (non-Javadoc)
     * 
     * @see gov.va.med.esr.common.batchprocess.VeteranUpdaterProcess#setResponse(javax.servlet.http.HttpServletResponse)
     */
    public void setResponse(HttpServletResponse httpServletResponse) {
        this.httpServletResponse = httpServletResponse;
    }

    /*
     * (non-Javadoc)
     * 
     * @see gov.va.med.esr.common.batchprocess.VeteranUpdaterProcess#setJspWriter(javax.servlet.jsp.JspWriter)
     */
    public void setJspWriter(JspWriter jspWriter) {
        this.jspWriter = jspWriter;
    }

    /*
     * (non-Javadoc)
     * 
     * @see gov.va.med.esr.common.batchprocess.VeteranUpdaterProcess#updateVeteran(java.lang.String)
     */
    public void updateVeteran(String personId, String vpid,
            int indexOfPersonInResultSet) throws UnexpectedEnrollmentException,
            PersonLockedException, ServiceException, IOException,
            RuleException, UnexpectedEnrollmentStatusException {
        Timer timer = null;
        try {
            PersonEntityKey personEntityKey = CommonEntityKeyFactory
                    .createPersonIdEntityKey(personId);

            // find the person
            Person personToProcess;
            personToProcess = personService.getPerson(personEntityKey);

            String processingVeteranMessage = "Processing veteran: "
                    + (indexOfPersonInResultSet + 1) + " with person id: "
                    + personId + " and vpid: " + vpid;
            jspWriter.println("<br/>" + processingVeteranMessage);
            httpServletResponse.flushBuffer();
            logStatus.info(processingVeteranMessage);

            // calculate the veteran’s new enrollment
            FeedbackTask feedbackTask = new FeedbackTask(jspWriter,
                    httpServletResponse, ".");
            timer = timerFactory.scheduleTimerTask(feedbackTask, 0, delay);
            Person calculatedPerson = eligibilityEnrollmentRuleService
                    .determineEnrollmentNoTriggerLetters(personToProcess);
            timer.cancel();
            EnrollmentDetermination calculatedEnrollmentDetermination = calculatedPerson
                    .getEnrollmentDetermination();

            // if the veteran has changed from 8e or 8g
            if (!(calculatedEnrollmentDetermination.getPriorityGroup()
                    .getCodeObject() == EnrollmentPriorityGroup.GROUP_8 && calculatedEnrollmentDetermination
                    .getPrioritySubGroup().getCodeObject() == EnrollmentPrioritySubGroup.SUBGROUP_E)
                    && !(calculatedEnrollmentDetermination.getPriorityGroup()
                            .getCodeObject() == EnrollmentPriorityGroup.GROUP_8 && calculatedEnrollmentDetermination
                            .getPrioritySubGroup().getCodeObject() == EnrollmentPrioritySubGroup.SUBGROUP_G)) {

                // if the veteran’s sub priority has changed
                // to 8b or 8d
                if (calculatedEnrollmentDetermination.getPriorityGroup()
                        .getCodeObject() == EnrollmentPriorityGroup.GROUP_8
                        && (calculatedEnrollmentDetermination
                                .getPrioritySubGroup().getCodeObject() == EnrollmentPrioritySubGroup.SUBGROUP_B || calculatedEnrollmentDetermination
                                .getPrioritySubGroup().getCodeObject() == EnrollmentPrioritySubGroup.SUBGROUP_D)) {

                    // if enrollment status is not verified, throw exception
                    if (calculatedEnrollmentDetermination.getEnrollmentStatus()
                            .getCodeObject() != EnrollmentStatus.CODE_VERIFIED) {
                        throw new UnexpectedEnrollmentStatusException();
                    }

                    // set the veteran’s initial enrollment priority
                    // to the calculated enrollment priority
                    calculatedEnrollmentDetermination
                            .setInitialPriorityGroup(calculatedEnrollmentDetermination
                                    .getPriorityGroup());
                    calculatedEnrollmentDetermination
                            .setInitialPrioritySubGroup(calculatedEnrollmentDetermination
                                    .getPrioritySubGroup());

                    // trigger letters
                    String triggerLettersMessage = "Triggering letters";
                    jspWriter.println("<br/>" + triggerLettersMessage);
                    httpServletResponse.flushBuffer();
                    logStatus.info(triggerLettersMessage);
                    feedbackTask = new FeedbackTask(jspWriter,
                            httpServletResponse, ".");
                    timer = timerFactory.scheduleTimerTask(feedbackTask, 0,
                            delay);
                    communicationRuleService.triggerAutomaticLetters(
                            calculatedPerson, null, false, null);
                    CommunicationsInputParameter communicationsInput = (CommunicationsInputParameter) beanFactory
                            .getBean("communicationsRuleParameter");
                    communicationsInput.setResultData(calculatedPerson);
                    communicationsInput.setIncomingData(calculatedPerson);
                    communicationsInput.setPristineData(personToProcess);
                    communicationsInput.trigger623RLetter();
                    triggerRouter.processTriggerAware(communicationsInput);
                    timer.cancel();

                    // process all message events
                    String processingEventsMessage = "Processing events and messages";
                    jspWriter.println("<br/>" + processingEventsMessage);
                    httpServletResponse.flushBuffer();
                    logStatus.info(processingEventsMessage);
                    feedbackTask = new FeedbackTask(jspWriter,
                            httpServletResponse, ".");
                    timer = timerFactory.scheduleTimerTask(feedbackTask, 0,
                            delay);
                    eventRuleService.handleCommonPostEvents(calculatedPerson,
                            false);
                    timer.cancel();

                    // save the veteran back to the db
                    String savingVeteranMessage = "Saving veteran to database";
                    jspWriter.println("<br/>" + savingVeteranMessage);
                    httpServletResponse.flushBuffer();
                    logStatus.info(savingVeteranMessage);
                    feedbackTask = new FeedbackTask(jspWriter,
                            httpServletResponse, ".");
                    timer = timerFactory.scheduleTimerTask(feedbackTask, 0,
                            delay);
                    personService.save(calculatedPerson);
                    timer.cancel();
                } else {
                    throw new UnexpectedEnrollmentException();
                }
            }
        } finally {
            if (timer != null) {
                timer.cancel();
            }
        }
    }

    public void setDelay(int delay) {
        this.delay = delay;
    }
}
