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

import gov.va.med.esr.common.infra.ImpreciseDate;
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.lookup.AddressType;
import gov.va.med.esr.common.model.lookup.Country;
import gov.va.med.esr.common.model.lookup.State;
import gov.va.med.esr.common.model.person.Name;
import gov.va.med.esr.common.model.person.PersonTraits;
import gov.va.med.esr.common.model.person.id.VPIDEntityKey;
import gov.va.med.esr.service.PSDelegateService;
import gov.va.med.esr.service.PersonHelperService;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.fw.batchprocess.AbstractDataQueryIncrementalProcess;
import gov.va.med.fw.batchprocess.DataQueryDetail;
import gov.va.med.fw.batchprocess.DataQueryProcessExecutionContext;
import gov.va.med.fw.batchprocess.ProcessStatistics;
import gov.va.med.fw.io.writer.FormattedFileWriter;
import gov.va.med.fw.service.ServiceException;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.Validate;

/**
 * Main class containing logic for the Send NCOA Address File Verification use
 * case.
 *
 * @author Rajiv Patnaik Created on Feb 24, 2006
 * @version 1.0
 *
 * Copyright © 2006 VHA. All rights reserved
 */
public class SendNCOAAddressVerificationFileProcess extends
        AbstractDataQueryIncrementalProcess
{
    private static final String FORM_ID = "NCOA";

    private FormattedFileWriter outputFileWriter;

    private PSDelegateService psDelegateService;

    private PersonHelperService personHelperService;

    /**
     * @return Returns the outputFileWriter.
     */
    public FormattedFileWriter getOutputFileWriter()
    {
        return outputFileWriter;
    }

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

    /**
     * @return Returns the psDelegateService.
     */
    public PSDelegateService getPsDelegateService()
    {
        return psDelegateService;
    }

    /**
     * @param psDelegateService
     *            The psDelegateService to set.
     */
    public void setPsDelegateService(PSDelegateService psDelegateService)
    {
        this.psDelegateService = psDelegateService;
    }

    /**
     * @return Returns the personHelperService.
     */
    public PersonHelperService getPersonHelperService()
    {
        return personHelperService;
    }

    /**
     * @param personHelperService
     *            The personHelperService to set.
     */
    public void setPersonHelperService(PersonHelperService personHelperService)
    {
        this.personHelperService = personHelperService;
    }

    /* (non-Javadoc)
     * @see gov.va.med.fw.batchprocess.AbstractDataProcess#createProcessStatistics()
     */
    protected ProcessStatistics createProcessStatistics()
    {
        return new NCOAAddressVerificationFileProcessStatistics();
    }


    /**
     * Processed filtered data from database.
     *
     * The criteria for quer
     *
     * @throws ServiceException
     */
    protected void processData(
            DataQueryProcessExecutionContext executionContext, List data)
    {
        if(logger.isInfoEnabled())
            logger.info("NCOA Number of records to process="+(data == null ? 0 : data.size()));

        List outputFileData = new ArrayList();
        try
        {
            //Create a List of NCOAAddressVerificationFileData used to write to an output file
            outputFileData = createFileOutputData(executionContext, data);

            if(logger.isDebugEnabled())
                logger.debug("NCOA Number of records to write to file="+(outputFileData == null ? 0 : outputFileData.size()));

            //Write to output file
            outputFileWriter.appendData(outputFileData);

            //Set the successful records stats
            executionContext.getProcessStatistics().setNumberOfSuccessfulRecords(
                    executionContext.getProcessStatistics().getNumberOfSuccessfulRecords()
                            + outputFileData.size());
        } catch (Exception e)
        {
            recordExceptionInContext(executionContext, e, outputFileData.size());
        }

        //Set the total records stats
        executionContext.getProcessStatistics().setNumberOfTotalRecords(
                executionContext.getProcessStatistics().getNumberOfTotalRecords()
                        + outputFileData.size());

    }

    private void recordExceptionInContext(DataQueryProcessExecutionContext executionContext, Exception e,
            int nbrErrorRecs)
    {
        // Set error records stats
        executionContext.getProcessStatistics().setNumberOfErrorRecords(
                executionContext.getProcessStatistics()
                        .getNumberOfErrorRecords()
                        + nbrErrorRecs);

        String errorMessage = "Error writing data to file during Send NCOA" +
                " Address file Verification batch process :"
                + " Reason: " + e.getMessage();

        executionContext.getExceptionData().add(errorMessage);

        if(logger.isWarnEnabled())
            logger.warn(errorMessage, e);
    }

    /**
     * Create a List of NCOAAddressVerificationFileData used to write to an output file
     *
     * @param data
     * @return
     */
    private List createFileOutputData(DataQueryProcessExecutionContext executionContext, List data)
    {
        List verificationData = new ArrayList();

        //The data will contain the information from address tables.
        //Call PSIM to get the identity traits based on the vpid value
        for (Iterator iter = data.iterator(); iter.hasNext();)
        {
            Object[] dataRow = (Object[]) iter.next();
            String VPIDValue = (String) dataRow[0];
            String addressLine1 = (String) dataRow[1];
            String addressLine2 = (String) dataRow[2];
            String city = (String) dataRow[3];
            String stateCode = (String) dataRow[4];
            String zipCode = (String) dataRow[5];
            String zipPlus4 = (String) dataRow[6];
            Date dateLastUpdated = (Date) dataRow[7];

            VPIDEntityKey VPIDKey = CommonEntityKeyFactory
                    .createVPIDEntityKey(VPIDValue);

            try
            {
                PersonTraits personTraits = getIdentityTraits(VPIDValue,
                    VPIDKey, executionContext);

                //Skip the record if no identity traits found
                if (personTraits != null)
                {
                    //Set information on a NCOAAddressFileVerificationData object
                    NCOAAddressVerificationFileData addressVerificationData
                    	= new NCOAAddressVerificationFileData();
                    addressVerificationData.setAddressLine1(addressLine1);
                    addressVerificationData.setAddressLine2(addressLine2);
                    addressVerificationData.setCity(city);
                    State state = null;
                    try
                    {
                        state = personHelperService.getStateFromCodeOrName(stateCode);
                    } catch (ServiceException e)
                    {
                        throw new RuntimeException(
                                "Could not get identity traits for VPID value "
                                        + VPIDValue, e);
                    }
                    addressVerificationData.setState(state == null ? null : state
                            .getCode());
                    addressVerificationData.setZipCode(zipCode);
                    addressVerificationData.setZipPlus4(zipPlus4);
                    addressVerificationData.setDateLastUpdated(dateLastUpdated);

                    addressVerificationData.setLastName(personTraits.getFamilyName());
                    addressVerificationData.setFirstName(personTraits.getGivenName());
                    addressVerificationData.setMiddleInitial(personTraits.getMiddleName());

                    addressVerificationData.setSsn(personTraits.getSsn());
                    addressVerificationData.setGender(personTraits.getGender());

                    addressVerificationData.setDateOfBirth(new ImpreciseDate(personTraits.getBirthDate()));

                    //Hardcode this value(as per ICD)
                    addressVerificationData.setFormID(FORM_ID);
                    addressVerificationData.setICN(personHelperService.getICNChecksum(VPIDValue));

                    verificationData.add(addressVerificationData);
                }
            }
            // Don't allow the failure of one record to prevent the others from processing
            catch (Exception e)
            {
                recordExceptionInContext(executionContext, e, 1);
            }

            //--------------------------------------------------------------------------------------
        }
        return verificationData;
    }

    /**
     * Gets traits from PSIM web service call
     * If more than one record or no data returned, then return null.
     * @param VPIDValue
     * @param VPIDKey
     * @param identityTraits
     * @return
     */
    private PersonTraits getIdentityTraits(String VPIDValue, VPIDEntityKey VPIDKey,
            DataQueryProcessExecutionContext executionContext)
    {
        PersonTraits personTraits = null;

        try
        {
            //CCR12190 Remove direct dependency on RPT_PSIM_TRAITS table
            //below service method getPersonTraitsByVPID() has been updated to get traits
            //through MVI web service call instead of direct query to local PSIM table
            personTraits = personHelperService.getPersonTraitsByVPID(VPIDKey.getVPID());

            if (personTraits == null)
            {
                ((NCOAAddressVerificationFileProcessStatistics) executionContext.getProcessStatistics()).incrementNumberOfNoIdentityTraitsRecords();
            }
            else
            {
                if (logger.isDebugEnabled())
                {
                    logger.debug("PSIM returned: " + personTraits);
                }
            }
        }
        catch (ServiceException e)
        {
            throw new RuntimeException(
                    "Could not get identity traits for VPID value " + VPIDValue,
                    e);
        }


        return personTraits;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public void afterPropertiesSet()
    {
        super.afterPropertiesSet();
        Validate.notNull(outputFileWriter, "A File Writer is needed");
        Validate.notNull(psDelegateService, "A PSDelegateService is needed");
        Validate.notNull(personHelperService, "A PersonHelperService is needed");
    }

}