

package gov.va.med.repositories.fpds.webservice;


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MultivaluedMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

import gov.va.med.repositories.fpds.filter.FilterBuilder;
import gov.va.med.repositories.fpds.validator.ValidationError;
import gov.va.med.repositories.fpds.validator.ValidationException;
import gov.va.med.repositories.fpds.validator.Validator;


@Path( "/" )
public class BaseParentFederatedPatientRestService
{
    public static final String DEFAULT_RESPONSE_TYPE = "json";
    public static final String XML_RESPONSE_TYPE = "xml";
    public static final String REQUEST_ID = "requestId";
    public static final String ICN = "nationalId";
    public static final String RESOLVED_IDENTIIFER = "resolvedIdentifier";
    public static final String TYPE = "_type";
    public static final String TEMPLATE_ID = "templateId";
    public static final String FILTER_ID = "filterId";
    public static final String DEFAUlT_TEMPLATE_ID = "GenericObservationRead1";
    public static final String DEFAULT_FILTER_ID = "GENERIC_VISTA_LIST_DATA_FILTER";
    public static final String RS_ID = "resolvedIdentifier";
    public static final String SEPARATOR = "-";

    private Log logger = LogFactory.getLog( BaseParentFederatedPatientRestService.class );

    //protected ClinicalDataServiceInterface clinicalDataService;
    protected gov.va.med.cds.client.webservice.ClinicalDataServiceSynchronousInterface clinicalDataServiceWS;

    protected Validator requestValidator;

    protected FilterBuilder filterBuilder;

    protected Map<String, FilterBuilder> filterBuilderMap;

    protected String applicationName;

    protected Document errorTemplateDocument;


    @GET
    @Path( "isAlive" )
    @Produces( { "text/plain" } )
    public boolean isAlive( )
    {
        if ( logger.isDebugEnabled() )
            logger.debug( "GenericRestService - isAlive()" );
        return clinicalDataServiceWS.isAlive();
    }


    protected String trimTrailingWhitespace( String str )
    {

        return StringUtils.trimTrailingWhitespace( str );
    }


    protected String buildErrorResponse( Exception e, String templateId, String requestId, String patientOrRecordIdentifier,
                    MultivaluedMap<String, String> queryParameters, boolean patientCentric )
    {

        /*
         * String.format( "{\"errorSection\": [{\"fatalErrors\": [{\"fatalError\": {\"errorId\": \"%s\",\"exception\": \"%s\",\"exceptionMessage\": \"CDS_GENERIC_WEB_SERVICE_ERROR\",\"errorCode\": \"CDS_GENERIC_WEB_SERVICE_ERROR\",\"displayMessage\": \"%s\"}}]}]}",
         *                   reqId, e.getClass().getName(), e.getMessage() );
         */
        Document errorDocument = ( Document )this.errorTemplateDocument.clone();
        Element errorsElement = errorDocument.getRootElement().element( "errorSection" ).element( "fatalErrors" );
        errorDocument.getRootElement().element( "templateId" ).setText( templateId );
        errorDocument.getRootElement().element( "requestId" ).setText( requestId );
        String[] content = null;
        ArrayList<String> idsList = null;
        if ( patientCentric )
        {
            if ( StringUtils.hasLength( patientOrRecordIdentifier ) )
            {

                if ( StringUtils.countOccurrencesOf( patientOrRecordIdentifier, SEPARATOR ) > 0 )
                {
                    if ( patientOrRecordIdentifier.contains( "," ) )
                    {

                        idsList = new ArrayList<String>( Arrays.asList( patientOrRecordIdentifier.split( "," ) ) );
                        int i = 0;
                        for ( String dfn : idsList )
                        {
                            content = dfn.split( SEPARATOR );
                            if ( ( content != null && content.length > 0 ) && ( i == 0 ) )
                            {
                                if ( content[0] != null || content[0].length() > 0 )
                                {
                                    errorDocument.getRootElement().element( "patients" ).element( "patient" ).element( "resultantIdentifiers" )
                                                    .element( "resultantIdentifier" ).element( "identity" ).setText( content[0] );

                                    if ( content.length > 1 )
                                    {
                                        errorDocument.getRootElement().element( "patients" ).element( "patient" ).element( "resultantIdentifiers" )
                                                        .element( "resultantIdentifier" ).element( "assigningFacility" ).setText( content[1] );
                                    }
                                    if ( content.length > 2 )
                                    {
                                        errorDocument.getRootElement().element( "patients" ).element( "patient" ).element( "resultantIdentifiers" )
                                                        .element( "resultantIdentifier" ).element( "assigningAuthority" ).setText( content[2] );
                                    }
                                }
                            }

                            if ( ( content != null && content.length > 0 ) && ( i > 0 ) )
                            {
                                Element resultId = null;
                                if ( content[0] != null || content[0].length() > 0 )
                                {
                                    resultId = errorDocument.getRootElement().element( "patients" ).element( "patient" )
                                                    .element( "resultantIdentifiers" ).addElement( "resultantIdentifier" );
                                    resultId.addElement( "identity" ).setText( content[0] );

                                    if ( content.length > 1 )
                                    {
                                        resultId.addElement( "assigningFacility" ).setText( content[1] );
                                    }
                                    if ( content.length > 2 )
                                    {
                                        resultId.addElement( "assigningAuthority" ).setText( content[2] );
                                    }
                                }
                            }
                            i++ ;
                        }
                    }
                    else
                    {
                        content = patientOrRecordIdentifier.split( SEPARATOR );
                        if ( content != null && content.length > 0 )
                        {
                            if ( content[0] != null || content[0].length() > 0 )
                            {
                                errorDocument.getRootElement().element( "patients" ).element( "patient" ).element( "resultantIdentifiers" )
                                                .element( "resultantIdentifier" ).element( "identity" ).setText( content[0] );

                                if ( content.length > 1 )
                                {
                                    errorDocument.getRootElement().element( "patients" ).element( "patient" ).element( "resultantIdentifiers" )
                                                    .element( "resultantIdentifier" ).element( "assigningFacility" ).setText( content[1] );
                                }
                                if ( content.length > 2 )
                                {
                                    errorDocument.getRootElement().element( "patients" ).element( "patient" ).element( "resultantIdentifiers" )
                                                    .element( "resultantIdentifier" ).element( "assigningAuthority" ).setText( content[2] );
                                }
                            }
                        }
                    }

                }
            }

        }
        else
        {
            errorDocument.getRootElement().element( "uniqueId" ).setText( patientOrRecordIdentifier );
        }

        if ( e instanceof ValidationException )
        {
            ValidationException ve = ( ValidationException )e;
            List<? extends ValidationError> verrs = ve.getViolations();

            for ( ValidationError verr : verrs )
            {
                // add a fatal exception to the document.
                Element error = DocumentHelper.createElement( "fatalError" );
                error.add( DocumentHelper.createElement( "errorId" ).addText( requestId.trim() ) );
                error.add( DocumentHelper.createElement( "exception" ).addText( ( String )verr.getClass().getName() ) );
                error.add( DocumentHelper.createElement( "exceptionMessage" ).addText( "FPDS_ERROR" ) );
                error.add( DocumentHelper.createElement( "errorCode" ).addText( "FPDS_ERROR" ) );
                error.add( DocumentHelper.createElement( "displayMessage" ).addText( ( String )verr.getMessage() ) );
                errorsElement.add( error );
            }
        }
        else
        {
            // add a fatal exception to the document.
            Element error = DocumentHelper.createElement( "fatalError" );
            error.add( DocumentHelper.createElement( "errorId" ).addText( requestId.trim() ) );
            error.add( DocumentHelper.createElement( "exception" ).addText( ( String )e.getClass().getName() ) );
            error.add( DocumentHelper.createElement( "exceptionMessage" ).addText( "FPDS_ERROR" ) );
            error.add( DocumentHelper.createElement( "errorCode" ).addText( "FPDS_ERROR" ) );
            String detailedMessage = e.getMessage();
            if ( detailedMessage == null )
            {
                /*sometimes a throwable message is null - we can't set text to null*/
                detailedMessage = "null";

            }
            error.add( DocumentHelper.createElement( "displayMessage" ).addText( detailedMessage ) );

            errorsElement.add( error );
        }

        // If the type is not XML or JSON than default the error message format to JSON
        /*if ( !queryParameters.get( "_type" ).equals( "xml" ) && !queryParameters.get( "_type" ).equals( "json" ) )
        {
            return XML.toJSONObject( errorDocument.asXML() ).toString();
        }*/

        return errorDocument.asXML();
    }


    public void setApplicationName( String applicationName )
    {
        this.applicationName = applicationName;
    }


    /*public void setClinicalDataService( ClinicalDataServiceInterface clinicalDataService )
    {
        this.clinicalDataService = clinicalDataService;
    }*/


    public void setFilterBuilder( FilterBuilder filterBuilder )
    {
        this.filterBuilder = filterBuilder;
    }


    public void setFilterBuilderMap( Map<String, FilterBuilder> filterBuilderMap )
    {
        this.filterBuilderMap = filterBuilderMap;
    }


    public void setRequestValidator( Validator requestValidator )
    {
        this.requestValidator = requestValidator;
    }


    public void setErrorTemplateDocument( String errorTemplateDocument )
        throws Exception
    {
        this.errorTemplateDocument = loadDocumentFromResouce( new ClassPathResource( errorTemplateDocument ) );
    }


    private Document loadDocumentFromResouce( Resource resource )
        throws Exception
    {
        BufferedReader br = null;

        try
        {
            br = new BufferedReader( new InputStreamReader( resource.getInputStream() ) );
            StringBuffer sbResouce = new StringBuffer();
            String line = null;

            while ( ( line = br.readLine() ) != null )
            {
                sbResouce.append( line.trim() ).append( '\n' );
            }

            return DocumentHelper.parseText( sbResouce.toString() );
        }
        finally
        {
            if ( br != null )
            {
                br.close();
            }
        }
    }


    protected String getTemplateId( MultivaluedMap<String, String> queryParameters, String defaultTemplateId )
    {
        String templateId;
        templateId = queryParameters.getFirst( BaseParentFederatedPatientRestService.TEMPLATE_ID );
        templateId = templateId != null ? templateId : defaultTemplateId;
        return templateId;
    }


    protected String getFilterId( MultivaluedMap<String, String> queryParameters, String defaultFilterId )
    {
        String filterId = queryParameters.getFirst( BaseParentFederatedPatientRestService.FILTER_ID );
        filterId = filterId != null ? filterId : defaultFilterId;
        return filterId;
    }


    protected String handleException( Exception e, String requestId, String domain, String templateId, String patientId,
                    MultivaluedMap<String, String> queryParameters )
    {
        requestId = StringUtils.hasText( requestId ) ? requestId : "requestId";
        String msg = "Exception occured while processing read request in FPDS. ";
        StringBuilder validationErrorMessage = new StringBuilder();

        if ( e instanceof ValidationException )
        {
            ValidationException ve = ( ValidationException )e;
            List<? extends ValidationError> verrs = ve.getViolations();

            for ( ValidationError verr : verrs )
            {
                validationErrorMessage.append( verr.getMessage() );
            }

            msg = msg.concat( validationErrorMessage.toString() );

        }

        logger.fatal( gov.va.med.cds.util.LogMessageUtil.buildMessage( domain, requestId, this.applicationName, msg ), e );
        return buildErrorResponse( e, templateId, requestId, patientId, queryParameters, true );

    }


    protected String getPatientId( MultivaluedMap<String, String> queryParameters )
    {
        String patientId = StringUtils.trimTrailingWhitespace( queryParameters.getFirst( ICN ) );
        List<String> dfnList = null;
        if ( !StringUtils.hasLength( patientId ) )
        {
            dfnList = queryParameters.get( RS_ID );

            if ( dfnList == null || dfnList.isEmpty() )
            {
                patientId = null;
            }
            else
            {
                StringBuffer dfns = new StringBuffer();
                boolean isFirst = true;
                int i = 0;
                for ( String dfn : dfnList )
                {
                    if ( !isFirst && i != dfnList.size() )
                    {
                        dfns.append( ',' );
                    }
                    else
                    {
                        isFirst = false;

                    }
                    dfns.append( dfn );
                    i++ ;
                }

                patientId = dfns.toString();
            }

        }
        return patientId;
    }

	public gov.va.med.cds.client.webservice.ClinicalDataServiceSynchronousInterface getClinicalDataServiceWS() {
		return clinicalDataServiceWS;
	}


	public void setClinicalDataServiceWS(gov.va.med.cds.client.webservice.ClinicalDataServiceSynchronousInterface clinicalDataServiceWS) {
		this.clinicalDataServiceWS = clinicalDataServiceWS;
	}
	
}
