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

import java.util.ArrayList;

import gov.va.med.fw.batchprocess.AbstractDataFileSingleRowIncrementProcess;
import gov.va.med.fw.batchprocess.DataFileProcessExecutionContext;
import gov.va.med.fw.batchprocess.ProcessStatistics;
import gov.va.med.fw.io.writer.FormattedFileWriter;

import gov.va.med.esr.common.model.lookup.SSNType;
import gov.va.med.esr.common.model.person.Name;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.SSN;
import gov.va.med.esr.common.model.person.id.VPIDEntityKey;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.MilitaryInfoService;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.esr.service.PersonService;

/**
 * @author DNS   KATIKM
 * Main process that load the VSSC data(OEF/OIF) file
 */
public class LoadVSSCDataProcess extends
		AbstractDataFileSingleRowIncrementProcess {
	
	public static final String PUNCTUATION_CHARS = "[,.'`]";

    private FormattedFileWriter exceptionFileWriter;

    private MilitaryInfoService militaryService = null;
    private PersonService personService = null;
    private LookupService lookupService = null;
    
    protected ProcessStatistics createProcessStatistics()
    {
        return new LoadVSSCDataProcessStatistics();
    }
    
	/* (non-Javadoc)
	 * @see gov.va.med.fw.batchprocess.AbstractDataFileSingleRowIncrementProcess#processDataRecord(gov.va.med.fw.batchprocess.DataFileProcessExecutionContext, java.lang.Object)
	 */
	protected boolean processDataRecord(DataFileProcessExecutionContext context, Object bean) {
        try
        {
        	//Get file data
        	VSSCFileData fileData = (VSSCFileData) bean;
        	LoadVSSCDataResult result = null;
            LoadVSSCDataProcessStatistics statistics = 
            	(LoadVSSCDataProcessStatistics) context.getProcessStatistics();
        	
        	//Get Matching Person
        	Person person = getMathcingPerson(fileData);
        	
	        //CCR 11403: for Vista inbound messages, check enrollment records, add 200ESRCorreltion if necessary
            this.getPersonService().checkAndAddESRCorrelation(person);
        	
        	if (person == null) {
        		//update no match count and return
        		//TODOfileData.setExceptionReason(LoadVSSCDataResult.EXCEPTION_REASON_NO_MATCH);
        		writeExceptionToFile(context, LoadVSSCDataResult.EXCEPTION_REASON_NO_MATCH);
        		result = new LoadVSSCDataResult();
        		result.incrementCountNoMatchRejected();
        		setStatisticalData(result, statistics);
        		return false;
        	}
        	else 
        	{            
                //update combat episodes
        		person.getMilitaryService().removeAllCombatEpisodes();
        		person.getMilitaryService().addCombatEpisode(fileData.getCombatEpisode());

        		//Execute rules
        		result = militaryService.processVSSCData(person);

	            //Set statistical data
	            setStatisticalData(result, statistics);
	            
        		return true;
        	}

        } catch (Exception e)
        {
            logger.error("Unable to process VSSC file record ", e);
            throwIllegalStateException("Unable to process VSSC file record ", e);
        }

        return true;
	}

	/**
	 * @return Returns the exceptionFileWriter.
	 */
	public FormattedFileWriter getExceptionFileWriter() {
		return this.exceptionFileWriter;
	}

	/**
	 * @param exceptionFileWriter The exceptionFileWriter to set.
	 */
	public void setExceptionFileWriter(FormattedFileWriter exceptionFileWriter) {
		this.exceptionFileWriter = exceptionFileWriter;
	}

	/**
	 * @return Returns the militaryService.
	 */
	public MilitaryInfoService getMilitaryService() {
		return this.militaryService;
	}

	/**
	 * @param militaryService The militaryService to set.
	 */
	public void setMilitaryService(MilitaryInfoService militaryService) {
		this.militaryService = militaryService;
	}

	/**
	 * @return Returns the personService.
	 */
	public PersonService getPersonService() {
		return this.personService;
	}

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

	/**
	 * @return Returns the lookupService.
	 */
	public LookupService getLookupService() {
		return this.lookupService;
	}

	/**
	 * @param lookupService The lookupService to set.
	 */
	public void setLookupService(LookupService lookupService) {
		this.lookupService = lookupService;
	}

	/* (non-Javadoc)
	 * @see gov.va.med.fw.batchprocess.AbstractDataFileListenerProcess#beanCreationFailure(gov.va.med.fw.batchprocess.DataFileProcessExecutionContext, java.lang.String, java.lang.Exception)
	 */
	public boolean beanCreationFailure(DataFileProcessExecutionContext context, String nextRowRawData, Exception e) {
        //Even if there was a problem, treat it as a successful record for
        // statistical purposes
        //since the record will be logged in the exception file 
		LoadVSSCDataProcessStatistics statistics = 
			(LoadVSSCDataProcessStatistics)context.getProcessStatistics();
		
		LoadVSSCDataResult result = new LoadVSSCDataResult();
		
		String reason = e.getMessage();
		if (LoadVSSCDataResult.EXP_CONSISTENCY_CHECKS_FAILED.equals(reason)){
			result.incrementCountCCRejected();
		}
		else if (LoadVSSCDataResult.EXP_REQUIRED_FIELDS_MISSING_INVALID.equals(reason)){
			result.incrementCountMissingInvalidRejected();
		}
		else {
			//Runtime exceptins Invalid lookup codes
			result.incrementCountMissingInvalidRejected();
		}
		writeExceptionToFile(context,reason);
		setStatisticalData(result,statistics);
        return true;
	}
	
    private void setStatisticalData(LoadVSSCDataResult result, 
    		LoadVSSCDataProcessStatistics statistics)
    {
    	//update statistics
    	statistics.updateStatistics(result);
    }
	/**
	 * Load exception data to error file
	 * @param context
	 * @param exceptionReason
	 */
    private void writeExceptionToFile(DataFileProcessExecutionContext context,
            String exceptionReason)
    {
        ArrayList exceptionOutputDataList;
        LoadVSSCDataException exceptionData = new LoadVSSCDataException();
        exceptionData.setInputRawData(context.getCurrentRowRawData());
        exceptionData.setExceptionReason(exceptionReason);

        exceptionOutputDataList = new ArrayList();
        exceptionOutputDataList.add(exceptionData);
        exceptionFileWriter.appendData(exceptionOutputDataList);
    }
    
    private Person getMathcingPerson(VSSCFileData vsscFileData) throws Exception{
        // Get the current person on file
        PersonIdentityTraits traits = new PersonIdentityTraits();
        SSN officialSSN = new SSN();
        officialSSN.setSsnText(vsscFileData.getSsn().replaceAll("-",""));
        officialSSN.setType(getLookupService().getSSNTypeByCode(SSNType.CODE_ACTIVE.getCode()));
        traits.setSsn(officialSSN);
        Person person = this.getPersonService().find(traits);
        return matchPerson(person,vsscFileData) ? person : null;
    }
        
    /**
     * Match SSN,ICN, Name and DOB and return true if match is found
     * @param person
     * @param vsscFileData
     * @return
     */
    private boolean matchPerson(Person person, VSSCFileData vsscFileData){
    	
    	//SSN is the search criteria no need to compare    	
    	//SSN and ICN Match
    	if (getICN(person.getVPIDEntityKey()).equals(vsscFileData.getIcn())){
    		//SSN ICN
    		return true;
    	}
    	
    	//Name and DON Match
    	String firstNamefromFile = vsscFileData.getFirstName() == null ? "": vsscFileData.getFirstName();
    	String lastNamefromFile = vsscFileData.getLastName() == null ? "": vsscFileData.getLastName();
    	String yearFromFile = vsscFileData.getDateOfBirth().getYear();
    	
    	Name name = person.getLegalName();
    	String firstNamefromDB = name.getGivenName() == null ? "": name.getGivenName();
    	String lastNamefromDB = name.getFamilyName() == null ? "": name.getFamilyName();    	
    	String yearFromDB = person.getBirthRecord().getBirthDate().getYear();
    	
    	//convert names to upper case for comparison
    	firstNamefromFile = firstNamefromFile.toUpperCase().replaceAll(PUNCTUATION_CHARS,"");
    	firstNamefromDB = firstNamefromDB.toUpperCase().replaceAll(PUNCTUATION_CHARS,"");
    	lastNamefromFile = lastNamefromFile.toUpperCase().replaceAll(PUNCTUATION_CHARS,"");
    	lastNamefromDB = lastNamefromDB.toUpperCase().replaceAll(PUNCTUATION_CHARS,"");
    	
    	//SSN first name and dob year
    	if (match(firstNamefromFile,firstNamefromDB) && yearFromFile.equals(yearFromDB)){
    		return true;
    	}
    	//SSN last name and dob year
    	if (match(lastNamefromFile,lastNamefromDB) && yearFromFile.equals(yearFromDB)){
    		return true;
    	}    	
    	//SSN last name and first
    	if (match(lastNamefromFile,lastNamefromDB) && match(firstNamefromFile,firstNamefromDB)){
    		return true;
    	}
    	    	
    	return false;
    }
    
    private boolean match(String src, String dest){
    	if (src.equals(dest)){
    		return true;
    	}

    	//Split the hyphenated strings and compare individually
		String[] srcList = src.split("-");
		String[] destList = dest.split("-");
		
    	//if both are hyphenated strings return false as exact match failed   			
		if (srcList.length > 1 && destList.length > 1){
			return false;
    	}

		//src is hyphenated
		if (srcList.length == 2 && (dest.equals(srcList[0]) || dest.equals(srcList[1]))) {
			return true;
		}
		
		//dest is hyphenated
		if (destList.length == 2 && (src.equals(destList[0]) || src.equals(destList[1]))) {
			return true;
		}
		
		//multi-hyphenated strings need to be matched exactly    	
    	return false;
    }
    private String getICN(VPIDEntityKey vpidKey){
    	String vpid = vpidKey.getShortVPID();
    	//find the location of v and extract the characters before that
    	int loc = vpid.indexOf('V');
    	return vpid.substring(0,loc);
    }    
}
