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


import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;

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.HandbookMailingResponseErrorFileProcessStatistics;
import gov.va.med.fw.batchprocess.HandbookMailingResponseReturnedByUSPSFileProcessStatistics;
import gov.va.med.fw.batchprocess.ProcessStatistics;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.esr.common.model.party.Address;
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.comms.CommsLogEntry;
import gov.va.med.esr.common.model.comms.CommsTemplate;
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.BadAddressReason;
import gov.va.med.esr.common.model.lookup.ComAACErrorType;
import gov.va.med.esr.common.model.lookup.ComLetterType;
import gov.va.med.esr.common.model.lookup.ComMailingStatusType;
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.PersonChangeLogEntry;
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.common.persistent.comms.CommsTemplateDAO;
import gov.va.med.esr.common.persistent.person.PersonSubmittedAdvice;
import gov.va.med.esr.common.util.CommsLetterConstants;
import gov.va.med.esr.service.CommsLogService;
import gov.va.med.esr.service.DemographicService;
import gov.va.med.esr.service.HandBookService;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.PersonService;
import gov.va.med.esr.common.batchprocess.HandbookMailingResponseReturnedByUSPSFileData;
import gov.va.med.esr.common.infra.ImpreciseDate;

/** CCR10963
 * Batch process that reads from incoming CMS returned By USPS
 * TODO: need data to test in new test db
 * 
 * @author DNS   PICKLJ
 *
 */
public class HandbookMailingResponseReturnedByUSPSFileProcess extends
		AbstractDataFileSingleRowIncrementProcess {
	

	protected PersonService personService;
	private HandBookService handBookService;
	private LookupService lookupService;
	private DemographicService demographicService;
	private CommsLogService commsLogService;
	private CommsTemplateDAO templDAO;	
	private final String ADDRESS_TYPE_PERM = "P";
	private final String ADDRESS_TYPE_TEMP = "C";
	private final String ADDRESS_TYPE_CONF = "CNF";
	private String rawData = null;
	private HandbookMailingResponseReturnedByUSPSFileProcessStatistics stats;
	
	/*
	 * (non-Javadoc)
	 * 
	 * @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())
			return;
		logger.info("processing a new record"); 

	    //if (stats == null ) { 
		      try {		
		          stats = (HandbookMailingResponseReturnedByUSPSFileProcessStatistics)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"); 
				  updateJobResultsLast(context);
				  break;				  
			    }
			  } catch (RuntimeException e) {
					if(logger.isErrorEnabled())
					logger.error("RuntimeException processDataRecords^"+e.getMessage()+"^"+rawData);
					context.getExceptionData().add(createExceptionObject("RuntimeException processDataRecords^"+rawData+"^"+e.fillInStackTrace()));
			  }
			}
		
	}	
	
	
	protected boolean processDataRecord(DataFileProcessExecutionContext context, Object bean) {
		
		HandbookMailingResponseReturnedByUSPSFileData dataRecord = (HandbookMailingResponseReturnedByUSPSFileData)bean;
		boolean success = true;
        this.rawData = context.getCurrentRowRawData();
        SimpleDateFormat dateFormat = new SimpleDateFormat("MMddyyyy");
		try { 
			// validate record
			// Missing Required fields
			if (dataRecord.validateData().size() > 0) {
				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 R400H, R400B, R400F		
			if (formName.equals("R400H")) {
               // no action					
			} else if (formName.equals("R400B")) {
			   // no action	
			} else if (formName.equals("R400F")) {
			   // no action
			} else {
					logger.info("Invalid Product ID^"+rawData);
					context.getExceptionData().add(createExceptionObject("Invalid Product ID^"+rawData));
					context.getProcessStatistics().incrementNumberOfErrorRecords();
					// keep processing?
					return success;					
			}					
			// this will be a db access call
			HandBookMailQueue mailQueue = new HandBookMailQueue();
            // get the mailQueue object
			try {
                 mailQueue = handBookService.findHandBookByIdentifier(dataRecord.getUID());
			} catch (Exception ex) {
				 logger.error("Exception - handBookService.findHandBookByIdentifier^"+ex.getMessage()+"^"+rawData);
				 context.getExceptionData().add("Exception - handBookService.findHandBookByIdentifier^"+ex.getMessage()+"^"+rawData);
				 context.getProcessStatistics().incrementNumberOfErrorRecords();
				 // keep processing?
				 return success;				 
			}
            // check for failure
			if (mailQueue == null) {
				logger.info("Invalid Request ID^"+rawData);
				context.getExceptionData().add(createExceptionObject("Invalid Request ID^"+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 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 status			
			// update Returned By USPS in go get a HandbookMailQueue record using uid
			// ADR.HANDBOOK_MAIL_QUEUE.HANDBOOK_MAIL_QUEUE_ID			
            String uid = dataRecord.getUID();
            try {
            	// update status 
                HandBookMailStatusType statusType = lookupService.getHandBookMailingStatusTypeByCode(HandBookMailStatusType.RETURN_BY_POST_OFFICE.getCode());
                Date returnedDate = dateFormat.parse(dataRecord.getDatereturned());
                mailQueue.getHandBookMailStatus().setReturnedFromUPSDate(returnedDate);
                handBookService.saveHandbookMailStatus(mailQueue, statusType);
                // call ILOG rule
                handBookService.processUndeliverableHandbook(mailQueue);
                stats.incrementNumberOfSuccessfulRecords(); 
            } catch (Exception ex) {
    			logger.error("Exception - updateStatus update Exception uid="+uid+" : " + rawData, ex);
    			context.getExceptionData().add("Exception - updateStatus"+rawData+"^"+ex.fillInStackTrace());
    		    context.getProcessStatistics().incrementNumberOfErrorRecords();
				// keep processing records?				
				return success;     		    
            }			

			/*if (addrTypeCode.equals(ADDRESS_TYPE_PERM)) {	
			 // update Bad Address to Undeliverable
		     // send Z05 message	
			 // following AacImportProcess for incoming rejected letters
			 try {	
				CommsLogEntry log = new CommsLogEntry();
				Person person = this.matchPerson(dataRecord.getVPID());
				mailQueue.setPersonId(new BigDecimal(dataRecord.getUID()));
				ComMailingStatusType comStatusType = 
					lookupService.getComMailingStatusTypeByCode(ComMailingStatusType.RETURN_BY_POST_OFFICE.getCode());
				addr.setBadAddressReason(lookupService.getBadAddressReasonByCode(BadAddressReason.CODE_UNDELIVERABLE.getCode()));
    		    log.setAddress(addr);
                log.setLogXML("This is logXML");
                log.setPersonId(mailQueue.getPersonId());	
                log.setRecipient(CommsLetterConstants.RECIPIENT_VETERAN);
                log.setLetterType(this.getLookupService().
                		getComLetterTypeByCode(ComLetterType.CODE_HANDBOOK.getCode()));
                CommsTemplateDAO dao = this.getTemplDAO(); // mailQueue.getFormNumber() should look like "742-400H"
                CommsTemplate templ = (CommsTemplate)dao.findTemplateListByFormNumber(mailQueue.getFormNumber()).get(0);
                log.setTemplate(templ);
                log.addMailingStatus(comStatusType);
                this.commsLogService.insert(log);
                List entryList = new ArrayList();
                entryList.add(log);		            
                this.commsLogService.handleMailingResponses(entryList);            
                logger.info("CommsLogEntry id=" + log.getCommsLogIdString());
			  } catch (Exception ex) {
				logger.error("Exception - commsLogService.handleHandBookBadAddress : "+ex);
    			context.getExceptionData().add("Exception - commsLogService.handleHandBookBadAddress^"+rawData+"^"+ex);
    		    context.getProcessStatistics().incrementNumberOfErrorRecords();	
				// keep processing?
				return success;    		    
			  }	
			  
			} else if (addrTypeCode.equals(ADDRESS_TYPE_TEMP)) {
				/*
				 * SUC If the address of the handbook communication returned was the Temporary Address 
				 *  the system will automatically set the Temporary Address End Date to the current 
				 *  date the handbook was processed as undeliverable.
				 */  		
			/*	ImpreciseDate mailedDate = (ImpreciseDate)new ImpreciseDate(dataRecord.getDatemailed()).clone();
				addr.setEndDate(mailedDate);
				mailQueue.setAddress(addr);
				handBookService.saveHandBookMailQueue(mailQueue);
			} else if (addrTypeCode.equals(ADDRESS_TYPE_CONF)) {
				/*
				 * SUC If the address of the handbook communication returned was the Confidential Address 
				 *  the system will automatically set the Confidential Address End Date to the current 
				 *  date the handbook communication was processed as undeliverable.
				 */
				/*ImpreciseDate mailedDate = (ImpreciseDate)new ImpreciseDate(dataRecord.getDatemailed()).clone();
				addr.setEndDate(mailedDate);
				mailQueue.setAddress(addr);
				handBookService.saveHandBookMailQueue(mailQueue);	 			  	
				
	    	} else {
				   // address type is unknown
				/*
				 * SUC If the address of the handbook communication returned was the Confidential Address 
				 *  the system will automatically set the Confidential Address End Date to the current 
				 *  date the handbook communication was processed as undeliverable. 
				 * SUC If the Bad Address Reason was set then the system executes the Send Update Message 
				 *  (Z05) use case to share the bad address reason information with sites. 
				 * SUC If the system did not set the Bad Address Reason or set the Temporary or 
				 *  Confidential Address end date then the system executes the 
				 *  Trigger Handbook Communication Use Case to automatically send the 
				 *  handbook to the more current address.
				 *  this logic not clear really? send another handbook?
				 */	 
				/*logger.error("failure - unkown address type ["+addrTypeCode+"]");
				context.getExceptionData().add("failure - unkown address type^"+addrTypeCode+"^"+rawData);
    		    context.getProcessStatistics().incrementNumberOfErrorRecords();	
				// keep processing?
				return success; 	    		
			}*/
		    
	       return success;
			
		} catch (Exception ex) {
			// no docs on exceptions yet - writing to a file
			logger.error("processDataRecord - Exception processing data record: " + rawData, ex);
			context.getProcessStatistics().incrementNumberOfErrorRecords();
			context.getExceptionData().add(createExceptionObject(rawData));
			success = false;
		}
		return success;
	}

	// update stats last
	private void updateJobResultsLast(DataFileProcessExecutionContext context) {
		  stats.setProcessingEndDate(new Date());
		  updateJobResult(context);		
	}	
	
	public void afterPropertiesSet() {
		super.afterPropertiesSet();
		Validate.notNull(personService, "personService is required");
	}

	public PersonService getPersonService() {
		return personService;
	}

	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;
	}	
	
	public CommsLogService getCommsLogService() {
		return commsLogService;
	}

	public void setCommsLogService(CommsLogService commsLogService) {
		this.commsLogService = commsLogService;
	}		

	public CommsTemplateDAO getTemplDAO() {
		return templDAO;
	}

	public void setTemplDAO(CommsTemplateDAO templDAO) {
		this.templDAO = templDAO;
	}
	protected ProcessStatistics createProcessStatistics() {
        return new HandbookMailingResponseReturnedByUSPSFileProcessStatistics ();
    }	
	
}
