/**
 * Created on 4:44:56 PM Dec 11, 2005
 * Project: UI
 */
package gov.va.med.esr.ui.financials.action;

// Java classes
import gov.va.med.esr.common.model.financials.GMTThreshold;
import gov.va.med.esr.common.model.financials.IncomeThreshold;
import gov.va.med.esr.common.model.lookup.ZipCode;

import gov.va.med.esr.ui.common.action.AbstractAction;
import gov.va.med.esr.ui.common.util.DateUtils;

import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.ServiceOptimisticLockException;
import gov.va.med.fw.util.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
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 org.apache.struts.util.LabelValueBean;

public class ThresholdAction extends AbstractAction {
    
    /* The Valid Income Year list session key */
    public static final String THRESHOLD_INCOME_YEAR_LIST_SESSION_KEY = "thresholdIncomeYearListSessionKey";

    public static final String ZIPCODE_LIST_SESSION_KEY = "ZipCodeList";

    // Message keys
    public static final String OPTIMISTC_LOCK_WF_ERROR_KEY = "error.application.error.title.message";

    // Struts forwards
    public static final String FORWARD_THRESHOLDS = "thresholds";

    public static final String FORWARD_COPAYMENTS = "copayments";

    public static final String FIELD_NAME_ZIP_CODE = "zipCode";
    public static final String FIELD_NAME_STATE = "state";
    public static final String FIELD_NAME_CITY = "city";
    public static final String FIELD_NAME_ADDITIONAL_DEPENDENT = "additionalDependent";
    
    public static final String MESSAGE_ZIP_REQUIRED = "message.zipRequired"; 
    public static final String MESSAGE_CITY_OR_STATE_REQUIRED = "message.cityOrStateRequired";
    public static final String MESSAGE_ZIP_OR_CITY_OR_STATE_REQUIRED = "message.zipOrCityOrStateRequired";
    public static final String MESSAGE_INVALID_ZIP_CODE = "message.invalidZipcode";
    public static final String MESSAGE_NUMBER_OF_DEPS_REQUIRED = "message.depNumberRequired";
    public static final String MESSAGE_ZIP_CODE_NOT_FOUND = "message.zipcodeNotFound";
    public static final String MESSAGE_ZIP_OR_CITY_REQUIRED = "message.zipcodeRequired";

    public ThresholdAction() {
        super();
    }

    public void afterPropertiesSet() {
        // Check for required fields
    }

    /**
     * Handle OptimisticLockException and display an error.
     * 
     * @param request
     *            The request
     * @param ex
     *            a service optimistic lock exception
     * @throws Exception
     *             If any problems were encountered re-retrieving the person
     */
    protected void processOptimisticLockException(HttpServletRequest request,
            ServiceOptimisticLockException ex) throws Exception {
        // Worklist are read only actions, it's error to get here
        log.error("ServiceOptimisticLockException from WorkflowAction", ex);
        
        addActionMessage(request, OPTIMISTC_LOCK_WF_ERROR_KEY);
    }

    /**
     * Displays the financials overview page. If no income year had been
     * previously selected and set into the session, this method will determine
     * and set the income year appropriately. Otherwise, the currently selected
     * income year will be used.
     * 
     * @param mapping
     *            An action mapping
     * @param form
     *            A form bean
     * @param request
     *            A http request from an search.jsp page
     * @param response
     *            A http response to stream data to a next page
     * 
     * @return An action forward class encapsulating information about a next
     *         page
     * @throws Exception
     *             Thrown if failed to process
     */
    public ActionForward display(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // Initialize the valid income years into the session
        Object obj = request.getSession().getAttribute(THRESHOLD_INCOME_YEAR_LIST_SESSION_KEY);
        if (obj == null)
            initializeValidIncomeYears(request);

        // initialize
        request.getSession().setAttribute(ZIPCODE_LIST_SESSION_KEY, null);

        // Go to the financials overview page
        return mapping.findForward(FORWARD_THRESHOLDS);
    }

    /**
     * Displays the financials overview page. If no income year had been
     * previously selected and set into the session, this method will determine
     * and set the income year appropriately. Otherwise, the currently selected
     * income year will be used.
     * 
     * @param mapping
     *            An action mapping
     * @param form
     *            A form bean
     * @param request
     *            A http request from an search.jsp page
     * @param response
     *            A http response to stream data to a next page
     * 
     * @return An action forward class encapsulating information about a next
     *         page
     * @throws Exception
     *             Thrown if failed to process
     */
    public ActionForward displayThresholds(ActionMapping mapping,
                                           ActionForm form, 
                                           HttpServletRequest request,
                                           HttpServletResponse response) throws Exception {
        ThresholdForm thresholdForm = (ThresholdForm) form;
        String zip = thresholdForm.getZipCode();
        
        if(StringUtils.isEmpty(zip)) {
            if(StringUtils.isEmpty(thresholdForm.getCity())) {
                addActionMessageForField(request,MESSAGE_ZIP_OR_CITY_REQUIRED,FIELD_NAME_ZIP_CODE);
                addActionMessageForField(request,MESSAGE_ZIP_OR_CITY_REQUIRED,FIELD_NAME_CITY);
                return display(mapping, form, request, response);
            } else {
                return getZipCodes(mapping, form, request, response);
            }
        }
        
        // If the income year is not specified default to current Income Year
        Integer incomeYear = null;
        if (StringUtils.isEmpty(thresholdForm.getIncomeYear())) {
            incomeYear = new Integer(DateUtils.getCurrentYear() - 1);
        } else {
            incomeYear = new Integer(thresholdForm.getIncomeYear());
        }
        // Set the value on the form
        thresholdForm.setIncomeYear(String.valueOf(incomeYear.intValue()));

        ZipCode zipCode = this.getZipCode(request,zip);
        
// new code to fix CCR 10476
        String fipsCode = null;
        // adding this to get a List of gmtThresholds
        try {
            fipsCode = getLookupService().getFipsCode(zipCode.getZipCode());
            logger.info("Found fipsCode="+fipsCode);
        } catch (Exception e) {
            logger.error("Could not retrive fipsCode value", e);
        }        
        
        String msaCode = null;
        //ccr 8862 
        try {
            msaCode = getLookupService().getMsaCode(zipCode.getZipCode());
            if(msaCode !=null && msaCode.equals("0")){
                msaCode="9999";  // from 2007 onwards they changed the msa code from 0 to 9999.
                logger.info("Found msaCode="+msaCode);
            }
        } catch (Exception e) {
            logger.error("Could not retrive msaCode value", e);
        }        
        List gmtThresholds = null;

        if (StringUtils.isEmpty(msaCode)) {
            // No MSA Code
            gmtThresholds = getLookupService().getGMTThreshold(incomeYear,fipsCode);
        } else // with MSA Code
        {
            gmtThresholds = getLookupService().getGMTThreshold(incomeYear,
                    fipsCode, msaCode);
        }
       
        GMTThreshold gmtThreshold = null;
        // Get the first one from the list
        if (gmtThresholds != null && gmtThresholds.size() > 0) {
            gmtThreshold = (GMTThreshold) gmtThresholds.get(0);
        }    
//      end fix CCR 10476
        // If the GMT Threshold not found, create an empty one
        if (gmtThreshold == null) {
            gmtThreshold = new GMTThreshold();
            gmtThreshold.setYear(incomeYear);
        }
        IncomeThreshold incomeThreshold = getFinancialsService().getIncomeThreshold(incomeYear);

        populateForm(thresholdForm, gmtThreshold, incomeThreshold,zipCode);
        thresholdForm.setAdditionalDependent(null);

        return display(mapping, form, request, response);
    }

    /**
     * Handles when the user selects a specific financial year to view.
     * 
     * @param mapping
     *            An action mapping
     * @param form
     *            A form bean
     * @param request
     *            A http request from an search.jsp page
     * @param response
     *            A http response to stream data to a next page
     * 
     * @return An action forward class encapsulating information about a next
     *         page
     * @throws Exception
     *             Thrown if failed to process
     */
    public ActionForward calulateAdditionDepThresholds(ActionMapping mapping,
            ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        
        ThresholdForm thresholdForm = (ThresholdForm) form;
        String zip = thresholdForm.getZipCode();

        if (StringUtils.isEmpty(zip)) {
            return displayThresholds(mapping, form, request, response);
        }

        Integer incomeYear = new Integer(thresholdForm.getIncomeYear());

        if (thresholdForm.getAdditionalDependent() == null || StringUtils.isEmpty(thresholdForm.getAdditionalDependent())) {
            addActionMessageForField(request,MESSAGE_NUMBER_OF_DEPS_REQUIRED,FIELD_NAME_ADDITIONAL_DEPENDENT);
            thresholdForm.setAdditionalDependent(null);
            return display(mapping, form, request, response);
        }

        int addDep = Integer.parseInt(thresholdForm.getAdditionalDependent());

        ZipCode zipCode = this.getZipCode(request,zip);
        GMTThreshold gmtThreshold = null;
        try {
            if(zipCode != null) {
                gmtThreshold = getFinancialsService().getGMTThreshold(incomeYear,zip, addDep);
            }
        } catch (ServiceException ex) {
            log.error("Could not retrive GMT Thresholds", ex);
        }
        
        // If the GMT Threshold not found, create an empty one
        if (gmtThreshold == null) {
            gmtThreshold = new GMTThreshold();
            gmtThreshold.setYear(incomeYear);
        }
        IncomeThreshold incomeThreshold = getFinancialsService().getIncomeThreshold(incomeYear, addDep);
        populateForm(thresholdForm, gmtThreshold, incomeThreshold,zipCode);

        if (gmtThreshold != null) {
            thresholdForm.setAddDepGMTThreshold(gmtThreshold.getThresholdValue(addDep + 7));
        }

        if (incomeThreshold != null) {
            BigDecimal addDepMTThreshold = incomeThreshold.getMeansTestThresholdValue(addDep + 7);
            thresholdForm.setAddDepMTThreshold((addDepMTThreshold != null) ? addDepMTThreshold.setScale(2,BigDecimal.ROUND_HALF_UP) : null);
            
            BigDecimal pThresholdValue = incomeThreshold.getPensionThresholdValue(addDep + 7);
            thresholdForm.setAddDepPensionThreshold((pThresholdValue != null) ? pThresholdValue.setScale(2,BigDecimal.ROUND_HALF_UP) : null);
            
            BigDecimal mdValue = incomeThreshold.getMedicalDeductiblesValue(addDep + 7);
            thresholdForm.setAddDepMedicalDeductible((mdValue != null) ? mdValue.setScale(2,BigDecimal.ROUND_HALF_UP) : null);
        }

        return display(mapping, form, request, response);
    }

    public ActionForward displayCopayments(ActionMapping mapping,
            ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        // Get the threshold form
        ThresholdForm thresholdForm = (ThresholdForm)form;
        thresholdForm.clearLists();

        Integer incomeYear = null;
        if (StringUtils.isEmpty(thresholdForm.getIncomeYear())) {
            incomeYear = new Integer(DateUtils.getCurrentYear() - 1);
        } else {
            incomeYear = new Integer(thresholdForm.getIncomeYear());
        }
        // Set the value on the form
        thresholdForm.setIncomeYear(String.valueOf(incomeYear.intValue()));
        IncomeThreshold income = getFinancialsService().getIncomeThreshold(incomeYear);
        thresholdForm.setIncomeThreshold(income);

        // Go to the financials overview page
        return mapping.findForward(FORWARD_COPAYMENTS);
    }

    /**
     * Handles when the user selects a specific financial year to view.
     * 
     * @param mapping
     *            An action mapping
     * @param form
     *            A form bean
     * @param request
     *            A http request from an search.jsp page
     * @param response
     *            A http response to stream data to a next page
     * 
     * @return An action forward class encapsulating information about a next
     *         page
     * @throws Exception
     *             Thrown if failed to process
     */
    public ActionForward clear(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // Get the threshold form
        ThresholdForm thresholdForm = (form instanceof ThresholdForm) ? (ThresholdForm) form
                : null;

        Validate.notNull(thresholdForm, "ThresholdForm bean is null.");

        thresholdForm.setGmtThreshold(null);
        thresholdForm.setZipCode(null);
        thresholdForm.setCity(null);
        thresholdForm.setIncomeYear(null);
        thresholdForm.setState(null);

        // Call display to get the income test and convert the data to the form
        // for the newly selected year
        return display(mapping, form, request, response);
    }

    /**
     * Handles when the user selects a specific financial year to view.
     * 
     * @param mapping
     *            An action mapping
     * @param form
     *            A form bean
     * @param request
     *            A http request from an search.jsp page
     * @param response
     *            A http response to stream data to a next page
     * 
     * @return An action forward class encapsulating information about a next
     *         page
     * @throws Exception
     *             Thrown if failed to process
     */
    public ActionForward cancel(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // Get the threshold form
        ThresholdForm thresholdForm = (form instanceof ThresholdForm) ? (ThresholdForm) form
                : null;

        Validate.notNull(thresholdForm, "ThresholdForm bean is null.");
        thresholdForm.setGmtThreshold(null);

        // Call display to get the income test and convert the data to the form
        // for the newly selected year
        return display(mapping, form, request, response);
    }

    public ActionForward getZipCodes(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // Get the threshold form
        ThresholdForm thresholdForm = (ThresholdForm) form;

        if (StringUtils.isEmpty(thresholdForm.getCity())) {
            addActionMessageForField(request, MESSAGE_CITY_OR_STATE_REQUIRED,FIELD_NAME_CITY);
            //addActionMessageForField(request, MESSAGE_CITY_OR_STATE_REQUIRED,FIELD_NAME_STATE);
        } else {
            List ls = null;
            if (StringUtils.isNotEmpty(thresholdForm.getCity())&& StringUtils.isNotEmpty(thresholdForm.getState())) {
                ls = getLookupService().getPartialZipCodeByCityState(thresholdForm.getCity(), thresholdForm.getState());
            } else if (StringUtils.isNotEmpty(thresholdForm.getCity())) {
                ls = getLookupService().getPartialZipCodeByCity(thresholdForm.getCity());
            }

            if(ls == null || ls.isEmpty()) {
                addActionMessage(request, MESSAGE_ZIP_CODE_NOT_FOUND);
            } else {
                request.setAttribute(ZIPCODE_LIST_SESSION_KEY, ls);
            }
        }
        // Call display to get the income test and convert the data to the form
        // for the newly selected year
        return mapping.findForward(FORWARD_THRESHOLDS);

    }

    /**
     * Initializes the list of valid income years that has the threshold info
     * and store in the session.
     * 
     * @param request
     *            The HttpServletRequest
     * @param person
     *            The person
     */
    protected void initializeValidIncomeYears(HttpServletRequest request)
            throws Exception {
        // Get the list of LabelValueBeans if they were previous stored in the
        // session
        List incomeYearBeans = new ArrayList();

        // Get the list of valid income years
        List ls = getLookupService().getAllIncomeThresholds();

        // Iterate through each valid year and create/add a LabelValueBean to
        // the incomeYearBean list
        for (Iterator iterator = ls.iterator(); iterator.hasNext();) {
            Integer value = ((IncomeThreshold) iterator.next()).getYear();
            String year = String.valueOf(value.intValue());
            incomeYearBeans.add(new LabelValueBean(year, year));
        }

        // Store the list of years (as LabelValueBeans) in the session
        request.getSession().setAttribute(
                THRESHOLD_INCOME_YEAR_LIST_SESSION_KEY, incomeYearBeans);
    }

    /**
     * Initialize and get the Struts lookup dispatch method map hashmap.
     * 
     * @return the method map
     * @see org.apache.struts.actions.LookupDispatchAction#getKeyMethodMap()
     */
    protected Map getKeyMethodMap() {
        Map map = new HashMap();
        map.put("title.thresholds", "display");
        map.put("button.search", "displayThresholds");
        map.put("button.cancel", "cancel");
        map.put("button.clear", "clear");
        map.put("button.calculateThresholds", "calulateAdditionDepThresholds");
        map.put("title.copayments", "displayCopayments");
        map.put("button.viewData", "displayCopayments");
        map.put("button.getZipCodes", "getZipCodes");
        return map;
    }

    private void populateForm(ThresholdForm thresholdForm,
                              GMTThreshold gmtThreshold, 
                              IncomeThreshold incomeThreshold,
                              ZipCode zip) throws Exception {
        if(zip != null) {
            thresholdForm.setCity(zip.getPreferredCity().getName());
            thresholdForm.setState(zip.getState().getCode());
            thresholdForm.setCounty(zip.getCounty().getName());
            thresholdForm.setFIPSCode(zip.getState().getFipsCode());
        } else {
            thresholdForm.setCity(null);
            thresholdForm.setState(null);
            thresholdForm.setCounty(null);
            thresholdForm.setFIPSCode(null);
        }
        
        thresholdForm.setGmtThreshold(gmtThreshold);
        thresholdForm.setIncomeThreshold(incomeThreshold);

        // set values in the form
        thresholdForm.clearLists();
        for (int j = 0; j <= 7; j++) {
            BigDecimal thresholdValue = gmtThreshold.getThresholdValue(j);
            thresholdValue = (thresholdValue != null) ? thresholdValue.setScale(2,BigDecimal.ROUND_HALF_UP) : null;
            gmtThreshold.setThresholdValue(j, thresholdValue);
            thresholdForm.addGMTThresholdValue(thresholdValue);

            if (incomeThreshold != null) {
                BigDecimal mtThresholdValue = incomeThreshold.getMeansTestThresholdValue(j);
                thresholdForm.addMTThresholdValue((mtThresholdValue != null) ? mtThresholdValue.setScale(2,BigDecimal.ROUND_HALF_UP) : null);
                
                BigDecimal pThresholdValue = incomeThreshold.getPensionThresholdValue(j);
                thresholdForm.addPensionThresholdValue((pThresholdValue != null) ? pThresholdValue.setScale(2,BigDecimal.ROUND_HALF_UP) : null);
                
                BigDecimal mdThresholdValue = incomeThreshold.getMedicalDeductiblesValue(j);
                thresholdForm.addMedicalDeductibleValue((mdThresholdValue != null) ? mdThresholdValue.setScale(2,BigDecimal.ROUND_HALF_UP) : null); 
            }
        }
    }
    
    private ZipCode getZipCode(HttpServletRequest request, String zipCode) {
        try {
            return getLookupService().getZipCodeByCode(zipCode);
        } catch(Exception ex) {
            addInformationMessage(request,MESSAGE_INVALID_ZIP_CODE);
        }
        return null;
    }
}
