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

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.HashMap;
import gov.va.med.esr.common.model.lookup.ComAACRejectReasonType;
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.comms.HandBookMailQueue;
import gov.va.med.esr.common.model.comms.HandBookMailStatus;
import gov.va.med.esr.common.model.lookup.HandBookMailStatusType;
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.model.person.id.PersonIdEntityKey;
import gov.va.med.esr.service.HandBookService;
import gov.va.med.esr.service.DemographicService;
import gov.va.med.esr.service.PersonService;
import gov.va.med.esr.service.LookupService;
import gov.va.med.fw.batchprocess.AbstractDataFileSingleRowIncrementProcess;
import gov.va.med.fw.batchprocess.DataFileProcessExecutionContext;
import gov.va.med.fw.batchprocess.HandbookMailingResponseAddressFileProcessStatistics;
import gov.va.med.fw.batchprocess.ProcessStatistics;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.batchprocess.HandbookMailingResponseErrorFileProcessStatistics;
import gov.va.med.fw.batchprocess.DataFileProcessStatistics;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
/*
 * cr10961
 * 
 * TODO : 
 * 
 */
public class HandbookMailingResponseErrorFileProcess extends
           AbstractDataFileSingleRowIncrementProcess implements Serializable {

	private PersonService personService;
	private DemographicService demographicService;
	private HandBookService handBookService;
	private LookupService lookupService;
	private String rawData = null;    
	private HandbookMailingResponseErrorFileProcessStatistics stats;

	/*
	 * (non-Javadoc)
	 *  TODO database changes coming
	 *       
	 * @see gov.va.med.fw.batchprocess.AbstractDataProcess#processData(gov.va.med.fw.batchprocess.DataProcessExecutionContext,
	 *      java.util.List)
	 */

	public void processDataRecords(DataFileProcessExecutionContext context, List acquiredData) {

		if(acquiredData.isEmpty()) {
			logger.info("no records"); 
			return;
		}
		logger.info("processing a new record"); 

	    if (stats == null ) { 
		      try {		
		          stats = (HandbookMailingResponseErrorFileProcessStatistics)context.getProcessStatistics();
		      } catch (java.lang.ClassCastException cex) {
	                  logger.error("ClassCastException : ", cex);
		      }	  
		}
		
		   for (int i=0;i<acquiredData.size();i++) {
		     try {
	        	Object obj = acquiredData.get(i);
	            // if no exception continue reading file data
			    if (processDataRecord(context, obj)) {
				    if (isKeepProcessedData()) {
					  context.getProcessedData().add(obj);
					  logger.info("adding processDataRecord "+i);
				    } else {
					  logger.info("NO MORE DATA run any clean up");	
					  updateJobResultsLast(context);
				    }
				  
			    } else {
				  // some error while calling processDataRecord so stop
				  logger.info("one record sent back false - stopping processDataRecords"); 
				  context.getProcessStatistics().incrementNumberOfErrorRecords();
				  updateJobResultsLast(context);
				  break;				  
			    }
			  } catch (RuntimeException e) {
					if(logger.isErrorEnabled())
					logger.error("RuntimeException processDataRecords^"+e.getMessage()+"^"+rawData);
					context.getProcessStatistics().incrementNumberOfErrorRecords();
					context.getExceptionData().add(createExceptionObject("RuntimeException processDataRecords^"+rawData+"^"+e.fillInStackTrace()));
			  }
			}		
		
	}	
	
	protected boolean processDataRecord(DataFileProcessExecutionContext context, Object bean)
    {
		HandbookMailingResponseErrorFileData dataRecord = (HandbookMailingResponseErrorFileData) bean;		
		boolean success = true;
		this.rawData = context.getCurrentRowRawData();
        addReason(dataRecord.getProdNumText(),dataRecord.getFailed());
		try {
			// validate record
			// Missing Required fields
			if (dataRecord.validateData().size() > 0) {
				// collect error info
				List invalidData = dataRecord.validateData();
				String errMsg = "";
				for (int i=0;i<invalidData.size();i++) {
					errMsg = errMsg+"^"+(String)invalidData.get(i);
				}				
				logger.error("Failed - Missing Required Fields"+errMsg);
				context.getExceptionData().add(createExceptionObject("Failed - Missing Required Fields"+errMsg+"^"+rawData));
				context.getProcessStatistics().incrementNumberOfErrorRecords();
				// keep processing?
				return success;
			}
			// Invalid Product ID
			String formName = dataRecord.getProdNumText();
			// check Product ID update stats//F400H, F400B, F400F			
			if (formName.equals("F400H")) {
					stats.incrementH400HRecordsProcessed();
			} else if (formName.equals("F400B")) {
					stats.incrementH400BRecords();
			} else if (formName.equals("F400F")) {
					stats.incrementH400FRecords();				
			} else {
					logger.info("Invalid Product ID^"+rawData);
					context.getExceptionData().add(createExceptionObject("Invalid Product ID^"+rawData));
					context.getProcessStatistics().incrementNumberOfErrorRecords();
					// keep processing?
					return success;					
			}
							
			/*
			 * SUC62.5 For each record received on this file the system will update the Communication Status to 
			 *  Error by AAC or Error by CMS for handbook.
			 * SUC62.5.1 The system sets the Date/Time Updated to the Date/Time the Communication Status was set.
			 * SUC62.5.2 The system updates the Error File Reason with the description of the code or 
			 *  codes received with each record. ADR.HANDBOOK_MAIL_STATUS.ERR_TXT get set with codes
			 * SUC62.5.3 The system captures and stores the following statistics that will be used to generate a report:
                Date/Time File Received
                File Name
                Number of Error Records Per File
                Break out the communication by type and how many per type
                For each communication type, the error reason(s) 
			 * */
			// find mailQueue 
			HandBookMailQueue mailQueue = new HandBookMailQueue();
			// get ERROR_TEXT
			String errorText = null;
			try {
				 mailQueue = handBookService.findHandBookByIdentifier(dataRecord.getUID());
				 errorText = mailQueue.getHandBookMailStatus().getErrorText();
				 if (errorText == null || errorText.length() ==0) {
					 errorText = "Invalid fields in the CMS data extract^"+dataRecord.getFailed()+"^";
				 } else {
					 errorText = errorText+dataRecord.getFailed()+"^";
				 }
				 mailQueue.getHandBookMailStatus().setErrorText(errorText);
			} catch (Exception ex) {
			     logger.error("Invalid Request ID^Exception - handBookService.findHandBookByIdentifier^"+ex.getMessage()+"^"+rawData);
				 context.getExceptionData().add("Invalid Request ID^Exception - handBookService.findHandBookByIdentifier^"+rawData+"^"+ex.fillInStackTrace());
				 context.getProcessStatistics().incrementNumberOfErrorRecords();
				 // keep processing?
				 return success;	
			}							
            if (mailQueue == null) {
			     logger.error("Invalid Request ID^mailQueue is null^"+rawData);
				 context.getExceptionData().add("Invalid Request ID^mailQueue is null^"+rawData);
				 context.getProcessStatistics().incrementNumberOfErrorRecords();
				 // keep processing?
				 return success;            	
            }  
			// VPID not matching the one sent in the request
			// there is no VPID in mailQueue but we can get VPID from create personService
			// failure to create is Invalid Veteran Id	
			// String vpidFromDB = personService.getVPIDByPersonId(mailQueue.getEntityKey().
			String vpidCompare = null;
			String icnCompare = null;
			try {
				String personId = String.valueOf(mailQueue.getPersonId());
				PersonIdEntityKey pkey = CommonEntityKeyFactory.createPersonIdEntityKey(personId); 
				vpidCompare = (personService.getVPIDByPersonId(pkey)).getVPID();
				icnCompare  = handBookService.getICNChecksumForVpid(vpidCompare);
				
				logger.info("does ["+dataRecord.getVPID()+"] = ["+icnCompare+"]");
			} catch (Exception ex) {
				logger.error("Exception - getVPIDByPersonId failed^"+rawData+"^"+ex.fillInStackTrace());
				context.getExceptionData().add("Exception - getVPIDByPersonId failed^"+rawData+"^"+ex.fillInStackTrace());
				context.getProcessStatistics().incrementNumberOfErrorRecords();
				// keep processing records?				
				return success; 				
			}			           
            // match icnCompare
			if (!icnCompare.equals(dataRecord.getVPID())) {
				logger.error("Invalid Veteran ID^"+rawData);
				context.getExceptionData().add("Invalid Veteran Id^"+rawData);
				context.getProcessStatistics().incrementNumberOfErrorRecords();
				// keep processing records?				
				return success; 				
			}		
    		// update HandBookMailStatus
            // this has to be changed to "Error By CMS"
            String uid = dataRecord.getUID();
            try {
            	// update status    
                HandBookMailStatusType statusType = lookupService.getHandBookMailingStatusTypeByCode(HandBookMailStatusType.ERROR_BY_CMS.getCode());                              
                handBookService.saveHandbookMailStatus(mailQueue, statusType);           
                context.getProcessStatistics().incrementNumberOfSuccessfulRecords(); 
            } catch (Exception ex) {
    			logger.error("processDataRecord updateStatus update Exception uid="+uid+" : " + rawData, ex);
    			context.getExceptionData().add("Exception - updateStatus^"+rawData+"^"+ex.fillInStackTrace());
    		    context.getProcessStatistics().incrementNumberOfErrorRecords();
            }            			
			
		} catch (Exception ex) {
			context.getProcessStatistics().incrementNumberOfErrorRecords();
			logger.error("processDataRecord - Exception processing data record^"+rawData+"^"+ex.fillInStackTrace());
			context.getExceptionData().add(createExceptionObject("processDataRecord - Exception processing data record^"+rawData+"^"+ex.fillInStackTrace()));
			// stop processing if the exception bubles up here?
			//success = false;
		}			
	
		return success;
    }	
	
	private void setCustomStats(DataFileProcessExecutionContext context)
	{
		// use custom stats for stats report
		stats = new HandbookMailingResponseErrorFileProcessStatistics();
		stats.appendStats(context.getProcessStatistics());
		context.setProcessStatistics(stats);			
	}
			
	private void addReason(String formType, String reason) {
		if (formType.equals("F400H")) {
			stats.appendH400HReason(reason);
		} else if (formType.equals("F400B")) {
			stats.appendH400BReason(reason);
		} else {
			stats.appendH400FReason(reason);
		}
	}
	
	// update stats last
	private void updateJobResultsLast(DataFileProcessExecutionContext context) {
		  stats.setProcessingEndDate(new Date());
		  updateJobResult(context);		
	}
	
	/**
	 * @return Returns the personService.
	 */
	public PersonService getPersonService() {
		return personService;
	}

	/**
	 * @param personService
	 *            The personService to set.
	 */
	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}
	
	public HandBookService getHandBookService() {
		return handBookService;
	}

	public void setHandBookService(HandBookService handBookService) {
		this.handBookService = handBookService;
	}	
	
	public LookupService getLookupService() {
		return lookupService;
	}

	public void setLookupService(LookupService lookupService) {
		this.lookupService = lookupService;
	}
	public DemographicService getDemographicService() {
		return demographicService;
	}

	public void setDemographicService(DemographicService demographicService) {
		this.demographicService = demographicService;
	}
	protected ProcessStatistics createProcessStatistics() {
        return new HandbookMailingResponseErrorFileProcessStatistics ();
    } 	
}
