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

import gov.va.med.esr.common.model.lookup.WkfCaseStatusType;
import gov.va.med.esr.service.LookupService;
import gov.va.med.fw.batchprocess.AbstractDataQueryIncrementalProcess;
import gov.va.med.fw.batchprocess.DataQueryProcessExecutionContext;
import gov.va.med.fw.persistent.hibernate.AbstractDAOAction;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.transaction.TransactionTimestampManager;
import gov.va.med.fw.util.StringUtils;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.hibernate.Query;
import org.hibernate.Session;

/**
 * 
1. Guard/Reservist Eligibility Consistency Checks 
2. VADIR Character of Discharge Exception 
3. WebHINQ Character of Discharge Exception 
4. Person Not Found in VADIR and BIRLS 
5. Person Found but No MS Data 
6. Success from One Data Source but Not Found in Other 
 */
/*
1. An update of military service information was received and processed, for a guard/reservists, however, the activation termination reason could not be determined. Investigation is required. 
2. Character of Discharge is Unknown per VADIR 
3. Character of Discharge is Unknown per WebHINQ   
4. A Military Service Message was received, Person Not Found in VADIR and BIRLS  
5. A Military Service Message was received No Military Service Information Found in  VADIR or BIRLS 
6. A Military Service Message was received Success from One Data Source but Not Found in Other    
 */
public class ProcessClosedMSDSWorkListItems extends
		AbstractDataQueryIncrementalProcess {
	
    private static final String PARAM_CASEID = "caseId";
    private static final String DELETE_WORKCASEASSIGNMENT = "processClosedMSDSWorkListItems.deleteWorkcaseAssignment";
    private static final String DELETE_WORKCASECOMMENTS = "processClosedMSDSWorkListItems.deleteWorkcaseComments";
    private static final String DELETE_WORKCASESTATUS = "processClosedMSDSWorkListItems.deleteWorkcaseStatusDetails";
    private static final String DELETE_WORKCASEMESSAGING = "processClosedMSDSWorkListItems.deleteWorkcaseMessaging";
    private static final String DELETE_WORKCASE = "processClosedMSDSWorkListItems.deleteWorkcase"; 
    public static final String[] msds_exception_rules = {"An update of military service information was received and processed, for a guard/reservists, however, the activation termination reason could not be determined. Investigation is required.",
    	"Character of Discharge is Unknown per VADIR",
    	"Character of Discharge is Unknown per WebHINQ",  
    	"A Military Service Message was received, Person Not Found in VADIR and BIRLS",  
    	"A Military Service Message was received No Military Service Information Found in  VADIR or BIRLS",
    	"A Military Service Message was received Success from One Data Source but Not Found in Other",
    	/* below are from CCR 12893 */
    	"A Military Service Message was received and processed but does not pass data validation checks",
    	"An update of military service information was received and processed, for a guard/reservists, however, the activation termination reason could not be determined. Investigation is required.",
    	"An update of military service information was received and processed; however, the Character of Discharge could not be determined. Investigation is required.",
    	/* this last one is due to a faulty rule implementation that is actually creating with a tab in between the words "could" and "not" */
    	"An update of military service information was received and processed; however, the Character of Discharge could	not be determined. Investigation is required."    	
     };
    private static Set<String> exceptionSet = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
    static {
    	exceptionSet.addAll(Arrays.asList(msds_exception_rules));
    }
    		
	LookupService lookupService;
	TransactionTimestampManager timestampManager;
	
	protected void processData(DataQueryProcessExecutionContext context,
			List acquiredData) {
		
		// System.out.println("process closed msds work item");
		if(logger.isDebugEnabled())
            logger.debug("Process Closed MSDS Worklist Items: Query increment result Size="+(acquiredData == null ? 0 : acquiredData.size()));
        
        if (acquiredData == null)
            return;
        
        WkfCaseStatusType closedStatusType = null;
        try
        {
        	closedStatusType = (WkfCaseStatusType)getLookupService().getWkfCaseStatusTypeByCode(WkfCaseStatusType.CLOSED.getName());
        }
        catch (ServiceException ex)
        {
        	String errMsg = "Error in lookup workcase status type CLOSED!";
        	context.getExceptionData().add(errMsg+ " Reason: "+ ex.getMessage());
        	if(logger.isWarnEnabled())
        		logger.warn(errMsg + " Reason: ", ex);
        	return;
        }
        
        // Add check for isInterrupted so don't have to wait for entire batch to finish
        for (int i = 0; i < acquiredData.size() && !isInterrupted(context); i++) {
            // Object[] returned from get when scrolling (idempotent is false)
            Object[] row = (Object[]) acquiredData.get(i);
            BigDecimal wkfCaseId = (BigDecimal) row[0];
            BigDecimal wkfStatusId = (BigDecimal)row[1];
            String errorText = (String)row[2];
            
           // System.out.println("one record found "+" with id = " + wkfCaseId);
            if(logger.isDebugEnabled())
                logger.debug("Processing Closed Workflow Case # " + (i+1) + " with id = " + wkfCaseId);
            
            try {	 
            	if( this.isWorkItemMeetsOneofMSDSExceptionRules(errorText)) // one of exception rule is met
            	{    
            		/*System.out.println("****found one: " + errorText);
            		logger.info("****found one: " + errorText);*/
            		
                    Map contextData = new HashMap();
                    contextData.put("caseId", wkfCaseId);   
                    
                   // delete work case assignment
                    AbstractDAOAction callback1 = new AbstractDAOAction(contextData) { 
                        public Object execute(Session session) {
                            Query query = session.getNamedQuery(DELETE_WORKCASEASSIGNMENT);
                            query.setParameter(PARAM_CASEID, (BigDecimal)getContextData().get("caseId"));
                            return new Integer(query.executeUpdate());  
                         }
                    };
                    Integer updateCount1 = (Integer) getDao().execute(callback1);
                    if (updateCount1.intValue() < 1) {
                        if(logger.isWarnEnabled())
                            logger.warn(getProcessName() + ": Workcase assignment cannot be deleted for caseId=" + wkfCaseId);
                    }	
                    
                    // delete work case comments 
                    AbstractDAOAction callback2 = new AbstractDAOAction(contextData) { 
                        public Object execute(Session session) {
                            Query query = session.getNamedQuery(DELETE_WORKCASECOMMENTS);
                            query.setParameter(PARAM_CASEID, (BigDecimal)getContextData().get("caseId"));
                            return new Integer(query.executeUpdate());  
                         }
                    };
                    Integer updateCount2 = (Integer) getDao().execute(callback2);
                    if (updateCount2.intValue() < 1) {
                        if(logger.isWarnEnabled())
                            logger.warn(getProcessName() + ": Workcase comments cannot be deleted for caseId=" + wkfCaseId);
                    }	

                    //delete work case status details
                    AbstractDAOAction callback3 = new AbstractDAOAction(contextData) { 
                        public Object execute(Session session) {
                            Query query = session.getNamedQuery(DELETE_WORKCASESTATUS);
                            query.setParameter(PARAM_CASEID, (BigDecimal)getContextData().get("caseId"));
                            return new Integer(query.executeUpdate());  
                         }
                    };
                    Integer updateCount3 = (Integer) getDao().execute(callback3);
                    if (updateCount3.intValue() < 1) {
                        if(logger.isWarnEnabled())
                            logger.warn(getProcessName() + ": Workcase status details cannot be deleted for caseId=" + wkfCaseId);
                    }	
                    
                   // delete work case messaging
                    AbstractDAOAction callback4 = new AbstractDAOAction(contextData) { 
                        public Object execute(Session session) {
                            Query query = session.getNamedQuery(DELETE_WORKCASEMESSAGING);
                            query.setParameter(PARAM_CASEID, (BigDecimal)getContextData().get("caseId"));
                            return new Integer(query.executeUpdate());  
                         }
                    };
                    Integer updateCount4 = (Integer) getDao().execute(callback4);
                    if (updateCount4.intValue() < 1) {
                        if(logger.isWarnEnabled())
                            logger.warn(getProcessName() + ": Workcase messaging cannot be deleted for caseId=" + wkfCaseId);
                    }	
                    
                   // delete work case 
                    AbstractDAOAction callback = new AbstractDAOAction(contextData) { 
                        public Object execute(Session session) {
                            Query query = session.getNamedQuery(DELETE_WORKCASE);
                            query.setParameter(PARAM_CASEID, (BigDecimal)getContextData().get("caseId"));
                            return new Integer(query.executeUpdate());  
                         }
                    };
                    Integer updateCount = (Integer) getDao().execute(callback);
                    if (updateCount.intValue() < 1) {
                        if(logger.isWarnEnabled())
                            logger.warn(getProcessName() + ": Workflow case cannot be deleted for caseId=" + wkfCaseId);
                    }	
                    context.getProcessStatistics().incrementNumberOfSuccessfulRecords();
                    // System.out.println("one record was deleted "+" with id = " + wkfCaseId);
            	}
            } catch (Exception ex) {
                context.getProcessStatistics().incrementNumberOfErrorRecords();
                String errMsg = "Error while executing Process Closed MSDS Work List Item for Workflow Case "
                                + wkfCaseId;
                context.getExceptionData().add(errMsg+ " Reason: "+ ex.getMessage());
                if(logger.isWarnEnabled())
                    logger.warn(errMsg + " Reason: ", ex);
            }
            
            // Update statistics more frequently than once per batch (default size 5)
            if(context.getProcessStatistics().isTotalNumberMod(DEFAULT_JOB_RESULT_UPDATE_INTERVAL))
                this.updateJobResult(context);
        }
        
        if(logger.isDebugEnabled()) {
            logger.debug("Process Closed MSDS Broker Work Items: Processing of current batch complete.");
            logger.debug("Process Closed MSDS Broker Work Items: Successful records count = "+context.getProcessStatistics().getNumberOfSuccessfulRecords());
            logger.debug("Process Closed MSDS Broker Work Items: Failure records count ="+context.getProcessStatistics().getNumberOfErrorRecords());
        }

	}
	public LookupService getLookupService() {
		return lookupService;
	}
	public void setLookupService(LookupService lookupService) {
		this.lookupService = lookupService;
	}
	public TransactionTimestampManager getTimestampManager() {
		return timestampManager;
	}
	public void setTimestampManager(TransactionTimestampManager timestampManager) {
		this.timestampManager = timestampManager;
	}
	
	private boolean isWorkItemMeetsOneofMSDSExceptionRules(String errStr){
		boolean ruleFlag = false;
		if (this.contains(msds_exception_rules, errStr)){
			ruleFlag = true;
		}
		// System.out.println("exception flag = " + ruleFlag);
		return ruleFlag;
	}
	
   private boolean contains(String[] strings, String string)
    {
        if (string == null || StringUtils.isBlank(string)) return false;
        return exceptionSet.contains(string.trim());
        
        /*for (int i = 0; i < strings.length; i++)
        {
            if (StringUtils.equalsIgnoreCase(strings[i], string.trim()))
                return true;
        }
        return false;*/
    }
}
