/********************************************************************
 * Copyright © 2005 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.ui.financials.action;

// Java classes
import gov.va.med.esr.common.model.financials.InProcessFinancialInfo;
import gov.va.med.esr.common.model.financials.IncomeTest;
import gov.va.med.esr.common.model.lookup.MeansTestStatus;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.rule.service.impl.RuleValidationMessage;
import gov.va.med.esr.common.ui.form.FinancialDetailsData;
import gov.va.med.esr.common.ui.form.FinancialDetailsForm;
import gov.va.med.esr.common.util.CommonDateUtils;
import gov.va.med.esr.ui.util.SessionManager;
import gov.va.med.fw.util.SerializationUtils;
import gov.va.med.fw.validation.ValidationMessage;
import gov.va.med.fw.validation.ValidationMessages;
import gov.va.med.fw.validation.ValidationServiceException;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.Validate;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * This struts action is used to support the financials dependent add page.
 *
 * @author Priya R.
 * @version 3.0
 */
public class FinancialDetailsEditAction extends FinancialsAction
{
	//CodeCR-13410
	private static final String EXPENSES_EXCEED_INCOME = "error.expensesExceedIncome";

    /**
     * Default constructor.
     */
    public FinancialDetailsEditAction()
    {
        super();
    }

    /**
     * Displays the Demographics Personal page.
     *
     * @param mapping Struts action mapping for this action
     * @param form Struts form bean for this action
     * @param request The Http Request
     * @param response The Http Response
     *
     * @return A struts action forward for where we will go next.
     * @throws Exception If there are any errors during processing.
     */
    public ActionForward display(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        FinancialDetailsForm detailsForm = getFinancialDetailsForm(form);
        boolean isAddAPerson = false;
	    isAddAPerson = SessionManager.isAddAPerson(request);
	    detailsForm.setAddAPerson(isAddAPerson);

    	// Get the income year from the cache and set it on the form
    	Integer incomeYear = getSelectedIncomeYear(request);
    	detailsForm.setIncomeYear(String.valueOf(incomeYear));

   		// Get the Person object from the cache
        Person person = getSelectedPerson(request);

        InProcessFinancialInfo inProcessFinancial = person.getInProcessFinancial(incomeYear);
        String inProcessData = inProcessFinancial == null ? null : inProcessFinancial.getInProcessData();
        if (inProcessData != null) {
            byte[] decodedData = new BASE64Decoder().decodeBuffer(inProcessData);
            Object decodedObj  = SerializationUtils.deserialize(decodedData);
            detailsForm.setFinancialsData((FinancialDetailsData) decodedObj);
            //request.setAttribute("financialDetailsForm", detailsForm);
        }
        else if (conversionService != null)
		{
			conversionService.convert(person,detailsForm);
		}
        boolean canAdd = getFinancialsService().canFinancialsBeAdded(incomeYear, person);
        boolean canEdit = getFinancialsService().canFinancialsBeUpdated(incomeYear, person);
        /* CR9619 VFA-SP1 */
        boolean canUpdateNetWorth = true;
        // CR9619 VFA-SP1 uncomment out if approved and concatenate it upward
        //getFinancialsService().canNetWorthBeUpdated(incomeYear, person);
        detailsForm.setAllowedToAddFinancials(canAdd);
        detailsForm.setAllowedToEditFinancials(canEdit);
        detailsForm.setAllowedToUpdateNetWorth(canUpdateNetWorth);

        return mapping.findForward(FORWARD_FINANCIAL_DETAILS_EDIT);
    }

    /**
     * Handles an optimistic lock exception by resetting the form and redisplaying the page.
     *
     * @param mapping Struts action mapping for this action
     * @param form Struts form bean for this action
     * @param request The Http Request
     * @param response The Http Response
     *
     * @return A struts action forward for where we will go next.
     * @throws Exception If there are any errors during processing.
     */
    protected ActionForward handleOptimisticLockException(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
    	form.reset(mapping,request);
    	return display(mapping,form,request,response);
    }

    /**
     * Updates the personal information.
     *
     * @param mapping Struts action mapping for this action
     * @param form Struts form bean for this action
     * @param request The Http Request
     * @param response The Http Response
     *
     * @return A struts action forward for where we will go next.
     * @throws Exception If there are any errors during processing.
     */
    public ActionForward update(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
		FinancialDetailsForm detailsForm = getFinancialDetailsForm(form);

        // Get the income year from the cache and set it on the form
        Integer incomeYear = getSelectedIncomeYear(request);
        detailsForm.setIncomeYear(String.valueOf(incomeYear));
        boolean isAddAPerson = false;
	    isAddAPerson = SessionManager.isAddAPerson(request);
	    detailsForm.setAddAPerson(isAddAPerson);

    	// Get the Person object from the cache
        Person person = getSelectedPerson(request);

		if(conversionService != null)
		{
			Person clonePerson = (Person)person.clone();

			conversionService.convert(detailsForm,clonePerson);

            //Delete in process - if any
            clonePerson.setInProcessFinancial(incomeYear, null);

		    // Call service layer here
	        if (isAddAPerson){
	        	updateSelectedPerson( request, clonePerson );
	        }
	        else {
	        	//CodeCR-13410: Deductible Expenses Cannot Exceed Patients Income
	        	//Retrieve the assessed person and validate proposed income against expenses

	        	Person calculatedPerson = invokeAssessReviewImpact(detailsForm, incomeYear, clonePerson);

		    	validateIncomesAndDeductibleExpenses(incomeYear, calculatedPerson, request);

		    	//update the person in the database upon successful validation
	        	Person updatedPerson = invokeUpdateFinancialAssessment(detailsForm, incomeYear, clonePerson);

	        	// Store updated person in the cache
	        	updateSelectedPerson( request, updatedPerson );
	        }

		}
		return mapping.findForward(FORWARD_FINANCIALS_SUMMARY);
    }

    /**
     * Saves the in-process data.
     *
     * @param mapping Struts action mapping for this action
     * @param form Struts form bean for this action
     * @param request The Http Request
     * @param response The Http Response
     *
     * @return A struts action forward for where we will go next.
     * @throws Exception If there are any errors during processing.
     */
    public ActionForward saveInProcess(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        FinancialDetailsForm detailsForm = getFinancialDetailsForm(form);
        // Get the income year from the cache and set it on the form
        Integer incomeYear = getSelectedIncomeYear(request);
        detailsForm.setIncomeYear(String.valueOf(incomeYear));
        boolean isAddAPerson = false;
	    isAddAPerson = SessionManager.isAddAPerson(request);
	    detailsForm.setAddAPerson(isAddAPerson);

        // Get the Person object from the cache
        Person person = getSelectedPerson(request);

        Person clonePerson = (Person)person.clone();

        byte[] data = SerializationUtils.serialize(detailsForm.getFinancialsData());
        InProcessFinancialInfo inProcessFinancial = clonePerson.getInProcessFinancial(incomeYear);
        if (inProcessFinancial == null) {
            inProcessFinancial = new InProcessFinancialInfo();
            inProcessFinancial.setIncomeYear(incomeYear);
            clonePerson.setInProcessFinancial(incomeYear, inProcessFinancial);
        }
        inProcessFinancial.setInProcessData(new BASE64Encoder().encode(data));

        if (isAddAPerson){
        	updateSelectedPerson( request, clonePerson );
        }
        else{
        	Person updatedPerson = getFinancialsService().updateInProcess(incomeYear, clonePerson);

        	// Store updated person in the cache
        	updateSelectedPerson( request, updatedPerson );
        }

        return mapping.findForward(FORWARD_FINANCIALS_SUMMARY);
    }

    /**
     * Review the impact to financial information.
     *
     * @param mapping Struts action mapping for this action
     * @param form Struts form bean for this action
     * @param request The Http Request
     * @param response The Http Response
     *
     * @return A struts action forward for where we will go next.
     * @throws Exception If there are any errors during processing.
     */
    public ActionForward reviewImpact(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
		FinancialDetailsForm detailsForm = getFinancialDetailsForm(form);
		boolean isAddAPerson = false;
		isAddAPerson = SessionManager.isAddAPerson(request);
		detailsForm.setAddAPerson(isAddAPerson);

        //Clean up if any...
        removeUpdatedPerson(request);
        SessionManager.removeCalculatedPerson(request);

		// Get the Person object from the cache
        Person person = getSelectedPerson(request);

        // Get the income year from the cache and set it on the form
        Integer incomeYear = getSelectedIncomeYear(request);
        detailsForm.setIncomeYear(String.valueOf(incomeYear));


		if(conversionService != null)
		{
			Person clonePerson = (Person)person.clone();

			conversionService.convert(detailsForm,clonePerson);

		    // Store updated person in the cache
			setUpdatedPerson(request, clonePerson);

		     if (!isAddAPerson){
		    	 //assess the person
		    	 Person calculatedPerson = invokeAssessReviewImpact(detailsForm, incomeYear, clonePerson);

		    	 //CodeCR-13410: Deductible Expenses Cannot Exceed Patients Income
		         //validate assessed income against expenses
		    	 validateIncomesAndDeductibleExpenses(incomeYear, calculatedPerson, request);

		    	 // Store calucualted person in the cache
		    	 SessionManager.setCalculatedPerson( request, calculatedPerson );
		     }
		}

		return mapping.findForward(FORWARD_FINANCIAL_REVIEW_IMPACT_DISPLAY);
    }

    /**
     * Return to Financials Details from review impact.
     *
     * @param mapping Struts action mapping for this action
     * @param form Struts form bean for this action
     * @param request The Http Request
     * @param response The Http Response
     *
     * @return A struts action forward for where we will go next.
     * @throws Exception If there are any errors during processing.
     */
    public ActionForward returnFromReviewImpact(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        FinancialDetailsForm detailsForm = getFinancialDetailsForm(form);
        boolean isAddAPerson = false;
	    isAddAPerson = SessionManager.isAddAPerson(request);
	    detailsForm.setAddAPerson(isAddAPerson);

        // Get the income year from the cache and set it on the form
        Integer incomeYear = getSelectedIncomeYear(request);
        detailsForm.setIncomeYear(String.valueOf(incomeYear));

        // Get the Person object from the cache
        Person person = getUpdatedPerson(request);

        InProcessFinancialInfo inProcessFinancial = person.getInProcessFinancial(incomeYear);
        String inProcessData = inProcessFinancial == null ? null : inProcessFinancial.getInProcessData();
        if (inProcessData != null) {
            byte[] decodedData = new BASE64Decoder().decodeBuffer(inProcessData);
            Object decodedObj  = SerializationUtils.deserialize(decodedData);
            detailsForm.setFinancialsData((FinancialDetailsData) decodedObj);
            //request.setAttribute("financialDetailsForm", detailsForm);
        }
        else if (conversionService != null)
        {
            conversionService.convert(person,detailsForm);

            //Loosing newIncomeTestInd, when the new income is added
            IncomeTest incomeTest = person.getIncomeTest(incomeYear);
            if(incomeTest != null && incomeTest.getEntityKey() == null) {
                detailsForm.setNewIncomeTestInd(true);
            }
        }
        detailsForm.setAllowedToAddFinancials(getFinancialsService().canFinancialsBeAdded(incomeYear, person));
        detailsForm.setAllowedToEditFinancials(getFinancialsService().canFinancialsBeAdded(incomeYear, person));

        return mapping.findForward(FORWARD_FINANCIAL_DETAILS_EDIT);
    }

	public ActionForward delete(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
		FinancialDetailsForm detailsForm = getFinancialDetailsForm(form);
		boolean isAddAPerson = false;
		isAddAPerson = SessionManager.isAddAPerson(request);
		detailsForm.setAddAPerson(isAddAPerson);

  		// Get the Person object from the cache
        Person person = getSelectedPerson(request);

	    // Get the income year from the cache
	    Integer incomeYear = getSelectedIncomeYear(request);

	    //check if we have an incometest to delete
	    if(person.getIncomeTest(incomeYear) != null)
	    {
	        Person clonePerson = (Person)person.clone();

	        //delete income test by setting to null
	        clonePerson.setIncomeTest(incomeYear, null);
            //Also delete in process - if any
            clonePerson.setInProcessFinancial(incomeYear, null);

		     if (isAddAPerson){
		    	 updateSelectedPerson( request, clonePerson );
		     }
		     else{
		        // Call service layer here
		        Person updatedPerson = invokeUpdateFinancialAssessment(detailsForm, incomeYear, clonePerson);

		        // Store updated person in the cache
		    	updateSelectedPerson( request, updatedPerson );
		     }

		}

		return mapping.findForward(FORWARD_FINANCIALS_SUMMARY);
    }

    /**
     * Adds Income tests
     *
     * @param mapping Struts action mapping for this action
     * @param form Struts form bean for this action
     * @param request The Http Request
     * @param response The Http Response
     *
     * @return A struts action forward for where we will go next.
     * @throws Exception If there are any errors during processing.
     */
    public ActionForward addIncomeTest(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        FinancialDetailsForm detailsForm = getFinancialDetailsForm(form);
        boolean isAddAPerson = false;
		isAddAPerson = SessionManager.isAddAPerson(request);
		detailsForm.setAddAPerson(isAddAPerson);

        //set the indicator for new income test record
        detailsForm.setNewIncomeTestInd(true);

        //set income test available indicator
        detailsForm.setIncomeTestAvaliableInd(true);

		// Get the Person object from the cache
        //Person person = getSelectedPerson(request);

        // Get the income year from the cache and set it on the form
        Integer incomeYear = getSelectedIncomeYear(request);
        detailsForm.setIncomeYear(String.valueOf(incomeYear));

        /* CR9619_VFA-SP1 SUC1434.28.5 & 6 */
        boolean canUpdateNetWorth = true;
        // CR9619 VFA-SP1 uncomment out if approved and concatenate it upward
        // getFinancialsService().canNetWorthBeUpdated(incomeYear, null);
        detailsForm.setAllowedToUpdateNetWorth(canUpdateNetWorth);

        return mapping.findForward(FORWARD_FINANCIAL_DETAILS_EDIT);
    }

    /**
     * Cancels this page and goes to the demographics overview screen.
     *
     * @param mapping Struts action mapping for this action
     * @param form Struts form bean for this action
     * @param request The Http Request
     * @param response The Http Response
     *
     * @return A struts action forward for where we will go next.
     * @throws Exception If there are any errors during processing.
     */
    public ActionForward cancel(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {

    	// FinancialDetailsForm detailsForm = getFinancialDetailsForm(form);
        boolean isAddAPerson = false;
 		isAddAPerson = SessionManager.isAddAPerson(request);
 		// detailsForm.setAddAPerson(isAddAPerson);

        // Get the income year from the cache
        Integer incomeYear = getSelectedIncomeYear(request);

        // Get the Person object from the cache
        Person person = getSelectedPerson(request);
        if (person.getInProcessFinancial(incomeYear) != null) {
            Person clonePerson = (Person)person.clone();
            clonePerson.setInProcessFinancial(incomeYear, null);

            if (isAddAPerson){
            	updateSelectedPerson( request, clonePerson);
            }
            else{
            	Person updatedPerson = getFinancialsService().updateInProcess(incomeYear, clonePerson);

            	// Store updated person in the cache
            	updateSelectedPerson( request, updatedPerson);
            }
        }
		return mapping.findForward(FORWARD_CANCEL);
    }

    public ActionForward gotoAdjudicate(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        //populate the selection list in the
        Person person = getSelectedPerson(request);
        Integer incomeYear = getSelectedIncomeYear(request);

        validateInProcessFinancials(incomeYear, person);

    	return mapping.findForward(FORWARD_ADJUDICATION);
    }

    public ActionForward displayAdjudicateIncomeTest(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
    		//populate the selection list in the
    		Person person = getSelectedPerson(request);
    		Integer incomeYear = getSelectedIncomeYear(request);

    		IncomeTest test = person.getIncomeTest(incomeYear);
    		AdjudicationForm adjudicationForm = (AdjudicationForm)form;
    		adjudicationForm.setIncomeYear(String.valueOf(incomeYear));

    		if (test != null) {
    			BigDecimal gmtThreshold = test.getGmtThresholdAmount();
    			BigDecimal mtThreshold = test.getThresholdA(); //Check
    			adjudicationForm.setGmtThresholdAmount(gmtThreshold);
    			adjudicationForm.setMeansTestThresholdAmount(mtThreshold);

    			if (gmtThreshold == null) gmtThreshold = new BigDecimal(0);
    			if (mtThreshold == null) mtThreshold = new BigDecimal(0);

    			//GMT Threshold > MT Threshold
    			List mtStatusList = new ArrayList ();
    			if (gmtThreshold.compareTo(mtThreshold) > 0) {

    				mtStatusList.add(getLookupCacheService().getByCodeFromCache(
    				    MeansTestStatus.class.getName(),
    				    MeansTestStatus.MT_STATUS_GMT_COPAY_REQUIRED.getName()));
    			}
    			else {
       				mtStatusList.add(getLookupCacheService().getByCodeFromCache(
        				    MeansTestStatus.class.getName(),
        				    MeansTestStatus.MT_STATUS_MT_COPAY_REQUIRED.getName()));
    			}
    			//MT Copy exempt is common for both
    			mtStatusList.add(getLookupCacheService().getByCodeFromCache(
    				    MeansTestStatus.class.getName(),
    				    MeansTestStatus.MT_STATUS_MT_COPAY_EXEMPT.getName()));

    			request.getSession().setAttribute("meansTestStatusList",mtStatusList);
    			//set the income year and
    			return mapping.findForward(FORWARD_ADJUDICATION);
    		}
    		else {
    			return mapping.findForward(FORWARD_FINANCIALS_SUMMARY);
    		}
    }

    protected void validateInProcessFinancials(Integer incomeYear, Person incoming) throws ValidationServiceException
    {
        Validate.notNull(incoming, "Person must not be null.");
        if (incoming.getInProcessFinancial(incomeYear) != null)
        {
            ValidationMessages validationMessages = new ValidationMessages();
            validationMessages.add(new ValidationMessage(RuleValidationMessage.FINANCIALS_INPROCESS_STATE.getCode()));
            throw new ValidationServiceException(validationMessages);
        }
    }

    //CodeCR-13410: Deductible Expenses Cannot Exceed Patients Income
	//validate a veteran's proposed/assessed total income against total expenses
    protected void validateIncomesAndDeductibleExpenses(Integer incomeYear, Person assessedPerson, HttpServletRequest request) throws ValidationServiceException {

        Validate.notNull(assessedPerson, "Person must not be null.");
        Validate.notNull(incomeYear, "income year must not be null.");

        IncomeTest incomeTest = assessedPerson.getIncomeTest(Integer.valueOf(incomeYear));
        if (incomeTest != null){
 		   logger.info("incomeTest: " + incomeTest.toString());
     	   BigDecimal incomes = incomeTest.getTotalIncome();

 		   logger.info("total income: " + incomes);
 		   BigDecimal expenses =  incomeTest.getDeductibleExpenses();
 		   logger.info("total expenses: " + expenses);

 	       if (expenses.compareTo(incomes) == 1){  //expense greater than incomes
 			    ValidationMessages validationMessages = new ValidationMessages();
 	            validationMessages.add(new ValidationMessage(EXPENSES_EXCEED_INCOME));
 	            throw new ValidationServiceException(validationMessages);
 	        }
        }
    }

    public ActionForward adjudicateIncomeTest(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
    		AdjudicationForm adjudicationForm = (AdjudicationForm)form;

    		//Update financials
    		Person updatedPerson = (Person)getSelectedPerson(request).clone();
    		MeansTestStatus meansTestStatus =(MeansTestStatus)
    			getLookupCacheService().getByCodeFromCache(
    				    MeansTestStatus.class.getName(),
    				    adjudicationForm.getMeansTestStatus());

    		boolean isAddAPerson = false;
    		 isAddAPerson = SessionManager.isAddAPerson(request);
		     if (!isAddAPerson){
		    	 getFinancialsService().updatePendingAdjudicationStatus(
    			getSelectedIncomeYear(request),updatedPerson,meansTestStatus);
		     }
    		//Reload the person and forward to financials summary screen
    		updateSelectedPerson( request, updatedPerson );
    		return mapping.findForward(FORWARD_FINANCIALS_SUMMARY);
    }

    /**
     * Initialize and get the Struts lookup dispatch method map hashmap.
     *
     * @see org.apache.struts.actions.LookupDispatchAction#getKeyMethodMap()
     * @return the method map
     */
   protected Map getKeyMethodMap()
    {
        Map map = new HashMap();
        map.put("button.acceptChanges", "update");
        map.put("button.cancel", "cancel");
        map.put("button.reviewImpact", "reviewImpact");
        map.put("button.display","display");
        map.put("link.returnFromReviewImpact","returnFromReviewImpact");
		map.put("button.deleteIncomeTest", "delete");
		map.put("button.addIncomeTest", "addIncomeTest");
		//adjudication
		map.put("button.adjudicateIncomeTest", "gotoAdjudicate");
		map.put("link.disaplayAdjudication", "displayAdjudicateIncomeTest");
		map.put("button.update", "adjudicateIncomeTest");
        map.put("button.saveInProcess", "saveInProcess");
        return map;
    }
}