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

import gov.va.med.esr.common.persistent.comms.CommsTemplateDAO;

import java.io.File;
import java.io.FileInputStream;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.Validate;


import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.fw.batchprocess.AbstractDataProcess;
import gov.va.med.fw.batchprocess.AcquiredDataProcessor;
import gov.va.med.fw.batchprocess.DataProcessExecutionContext;
import gov.va.med.fw.batchprocess.ProcessStatistics;
import gov.va.med.esr.service.CommsLogService;
import gov.va.med.esr.service.CorrespondenceService;
import gov.va.med.esr.service.DemographicService;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.PersonService;
import gov.va.med.esr.common.model.comms.Correspondence;
import gov.va.med.esr.common.model.comms.CorrespondenceDocument;
import gov.va.med.esr.common.model.lookup.ComLetterTemplateType;
import gov.va.med.esr.common.model.lookup.CorrespondenceStatus;
import gov.va.med.esr.common.model.lookup.ComLetterTemplateType.Code;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.id.PersonIdEntityKey;
import gov.va.med.esr.common.model.person.id.VPIDEntityKey;



/** RTC WI 339894
 * Batch process that reads from incoming CMS Code 1 Reject return file parsing
 * rejected mailings of ACA 1095B mail requests
 *
 *
 *
 * @author DNS   andavm CP+PE
 *
 *
 */
public class Import1095BResponsesPDF extends
AbstractDataProcess implements AcquiredDataProcessor{

	private CorrespondenceService correspondenceService;
	private PersonService personService;

	private LookupService lookupService;
	private DemographicService demographicService;

	private String inputFileLocation;

	private int numberOfTotalRecords = 0;

    ProcessStatistics stats;
    private CommsTemplateDAO templDAO;
    private CommsLogService commsLogService;

    File folder;



	@SuppressWarnings("rawtypes")
	@Override
	protected List acquireData(DataProcessExecutionContext context)
			throws Exception {
		List PDF_files;

		folder = new File(this.getInputFileLocation());

		PDF_files = listFilesForFolder(folder);

		processData(context, PDF_files);

		//set the PDF_files variable to null to prevent duplicate run
		PDF_files.clear();

		return PDF_files;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private List listFilesForFolder(final File folder) {

	    ArrayList combined = new ArrayList();

        for (String fileEntry : folder.list()) {


            if( fileEntry.contains("1095B_") || fileEntry.contains("LTR_")) {

                combined.add(fileEntry);

            }

        }

        return combined;
  }

	/*
	 * @see gov.va.med.fw.batchprocess.AbstractDataProcess#processData(gov.va.med.fw.batchprocess.DataProcessExecutionContext,
	 *      java.util.List)
	 */
	@SuppressWarnings({ "rawtypes" })
	public  void processData(DataProcessExecutionContext context, List acquiredData) {

		if(acquiredData.isEmpty()) { //acquiredData contains list of PDF files
			logger.info("Import 1095B PDF Job is missing input PDF files");
			return;
		}

		if (stats == null ) {
		    stats = context.getProcessStatistics();
		}

		for (int j=0;j<acquiredData.size();j++) {

	     try {
	    	processDataRecord(context, acquiredData.get(j));
		  }

	     catch (RuntimeException e) {
				if(logger.isErrorEnabled())
				logger.error("RuntimeException in Import 1095B processDataRecords^"+e.getMessage());
		  }
		}
     }

	protected void processDataRecord(DataProcessExecutionContext context, Object acquiredData) {

        File currentPDFFile = new File((String)acquiredData.toString());

        String fileName = currentPDFFile.getName();

        String[] values = fileName.split("_");

        if (values == null || values.length != 6) {
        	logger.error("Invalid pdf file name: " + fileName);
        	context.getProcessStatistics().incrementNumberOfErrorRecords();
        	return;
        }

        String vpid = values[1];
        String type = values[4];
        String taxYear = values[5];
        taxYear = taxYear.substring(0, 4);

        File processingFile = new File("" + folder.getAbsolutePath() + folder.separatorChar + currentPDFFile.getName());

        FileInputStream fileInputStream=null;
        byte[] PDFByteArray = new byte[(int) processingFile.length()];

		try {

			//convert file into array of bytes
			 fileInputStream = new FileInputStream(processingFile);
			 fileInputStream.read(PDFByteArray);
			 fileInputStream.close();

			 } catch(Exception e){
			       logger.error("unable to read pdf file:" + fileName);
			       context.getProcessStatistics().incrementNumberOfErrorRecords();
			       if (fileInputStream != null) {
			    	   IOUtils.closeQuietly(fileInputStream);			    	  
			       }
			       processingFile.delete();
			       return;
			 }

			try {
				VPIDEntityKey key = CommonEntityKeyFactory.createVPIDEntityKey(vpid);
				PersonIdEntityKey personkey = this.getPersonService().getPersonIdByVPID(key);

				Code formType;

				if(type.equalsIgnoreCase("1095B")) {
					formType = ComLetterTemplateType.FORM_NUMBER_800;
				} else if(type.equalsIgnoreCase("LTR")) {
					formType = ComLetterTemplateType.FORM_NUMBER_800A;
				} else {
					throw new Exception("Unknown Form Type in Import 1095B Process:" + currentPDFFile.getName());
				}

				ArrayList<Code> formNumbers = new ArrayList<Code>();
				formNumbers.add(formType);
				List<Correspondence> linkDoc = new ArrayList<Correspondence>();

				//find the correspondence entry that is associated to the single mail response
				List<Correspondence> corrResults = this.getCorrespondenceService().getPersonCorrespondence
						(new BigDecimal(personkey.getKeyValueAsString()), formNumbers);

				if (corrResults != null) {
					for (int i = 0; i < corrResults.size(); i++) {
						if (corrResults.get(i).getCorrespondenceDocument() == null) {
							linkDoc.add(corrResults.get(i));
						}
					}
				}

				//if not found, check for 800D
				if (linkDoc == null || linkDoc.isEmpty()) {
					formNumbers.clear();
					formNumbers.add(ComLetterTemplateType.FORM_NUMBER_800D);
					corrResults = this.getCorrespondenceService().getPersonCorrespondence
						(new BigDecimal(personkey.getKeyValueAsString()), formNumbers);
				}

				if (corrResults != null) {
					for (int i = 0; i < corrResults.size(); i++) {
						if (corrResults.get(i).getCorrespondenceDocument() == null) {
							linkDoc.add(corrResults.get(i));
						}
					}
				}


				//no results in sent status, insert new correspondence and set immediate to mailed status
				if (linkDoc == null || linkDoc.isEmpty()) {
					Person person = this.getPersonService().getPersonWithoutIdentityTraits(key);

					if (person != null) {

						Correspondence ltr = new Correspondence(person);
						DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
						Date date = formatter.parse(taxYear + "-01-01");
				    	ltr.setEffectiveDate(date);
						this.getCorrespondenceService().triggerNewCMSCorrespondence(ltr, formType);

						CorrespondenceDocument cd = new CorrespondenceDocument();
						cd.setDocumentFile(javax.xml.bind.DatatypeConverter.printHexBinary(PDFByteArray));
						cd.setFileName(type + ".pdf");
						ltr.setCorrespondenceDocument(cd);

						this.getCorrespondenceService().updateACACorrespondence(ltr, CorrespondenceStatus.MAILED_BY_CMS, null);
						context.getProcessStatistics().incrementNumberOfSuccessfulRecords();
						processingFile.delete();
						cd = null;

						if (context.getProcessStatistics().getNumberOfSuccessfulRecords() % 100 == 0) {
							this.updateJobResult(context);
						}

						return;

					}
				}

				Correspondence corr = null;

				//find the record to update
				try {

					if (linkDoc.size() == 1) {
						corr = linkDoc.get(0);
					} else {
						//more than one in sent status, use the oldest one
						Collections.sort(linkDoc, new Comparator<Correspondence>() {
							  public int compare(Correspondence o1, Correspondence o2) {
							      return o1.getStatusDate().compareTo(o2.getStatusDate());
							  }
							});

						corr = linkDoc.get(0);
					}

					//update as mailed and set the document
					CorrespondenceDocument cd = new CorrespondenceDocument();
					cd.setDocumentFile(javax.xml.bind.DatatypeConverter.printHexBinary(PDFByteArray));
					cd.setFileName(type + ".pdf");
					corr.setCorrespondenceDocument(cd);

					this.getCorrespondenceService().updateACACorrespondence(corr, null, null);
					processingFile.delete();
					context.getProcessStatistics().incrementNumberOfSuccessfulRecords();
					cd = null;

				} catch (Exception ex) {
					//put the entry into failed status, so it doesn't stay in 'Sent' status forever is something is wrong with it
					if (corr != null) {
						this.getCorrespondenceService().updateACACorrespondence(corr, null, "Unknown Error Importing PDF document");
					}

					throw new Exception("Failed to import PDF file", ex);

				}

			} catch (Exception ex) {
				logger.error("Exception in Import 1095B Process " + ex);
				context.getProcessStatistics().incrementNumberOfErrorRecords();
				processingFile.delete();
				return;
			}

			currentPDFFile = null;
			processingFile = null;
			fileName = null;
			values = null;
			PDFByteArray = null;
			fileInputStream = null;

			if (context.getProcessStatistics().getNumberOfSuccessfulRecords() % 100 == 0) {
				this.updateJobResult(context);
			}

	}

	public void afterPropertiesSet() {
		super.afterPropertiesSet();
		Validate.notNull(personService, "personService is required");
		Validate.notNull(correspondenceService, "correspondenceService is required");
	}

	public PersonService getPersonService() {
		return personService;
	}

	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}

	public CorrespondenceService getCorrespondenceService() {
		return correspondenceService;
	}

	public void setCorrespondenceService(CorrespondenceService correspondenceService) {
		this.correspondenceService = correspondenceService;
	}

	public LookupService getLookupService() {
		return lookupService;
	}

	public void setLookupService(LookupService lookupService) {
		this.lookupService = lookupService;
	}


	public String getInputFileLocation() {
		return inputFileLocation;
	}

	public void setInputFileLocation(String inputFileLocation) {
		this.inputFileLocation = inputFileLocation;
	}



	public int getNumberOfTotalRecords() {
		return numberOfTotalRecords;
	}

	public void setNumberOfTotalRecords(int numberOfTotalRecords) {
		this.numberOfTotalRecords = numberOfTotalRecords;
	}

	@SuppressWarnings("rawtypes")
	@Override
	public void processAcquiredData(DataProcessExecutionContext context,
			List acquiredData) throws Exception {

	}

	public DemographicService getDemographicService() {
		return demographicService;
	}

	public void setDemographicService(DemographicService demographicService) {
		this.demographicService = demographicService;
	}

	public CommsTemplateDAO getTemplDAO() {
		return templDAO;
	}

	public void setTemplDAO(CommsTemplateDAO templDAO) {
		this.templDAO = templDAO;
	}

	public CommsLogService getCommsLogService() {
		return commsLogService;
	}

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