/**
* 
*/


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


import gov.va.med.repositories.fpds.webservice.FederatingPatientRecordRestService;

import java.util.ArrayList;
import java.util.List;

import gov.va.med.repositories.fpds.validator.ValidationError;
import javax.ws.rs.core.MultivaluedMap;

import org.springframework.util.StringUtils;


/**
 * The default DefaultRequestValidator applies default validation logic to the queryParameters
 * and also using branching logic on a ValidationArgument which by default is a FILTER_ID 
 * and applies Patient centric or Record centric validation
 * 
 * @author vhaislegberb
 *
 */
public class DefaultRequestValidator
    implements
        Validator
{
    public static final String REQUEST_ID = "requestId";
    public static final String CLIENT_NAME = "clientName";
    public static final String EX_ID = "excludeIdentifier";
    public static final String SEPARATOR = "-";
    public static final String ICN = "nationalId";
    public static final String RS_AF = "resolvedAssigningFacility";
    public static final String RS_AA = "resolvedAssigningAuthority";
    public static final String RS_ID = "resolvedIdentifier";
    public static final String RS_AI = "identifier";
    protected static final String TYPE = "_type";
    public static final String FILTER_ID = "filterId";
    public static final String TEMPLATE_ID = "templateId";
    private static final String CLIENT_REQUEST_TIME = "clientRequestInitiationTime";


    @Override
    /*
     * (non-Javadoc)
     * @see gov.va.med.repositories.fprs.validator.ValidatorInterface#validateRequestParameters( java.lang.String, javax.ws.rs.core.MultivaluedMap)
     */
    public List<ValidationError> validateRequestParameters( String filterId, MultivaluedMap<String, String> queryParameters )
    {
    	String command = queryParameters.getFirst( "command" );
    	
    	List<ValidationError> validationErrors = new ArrayList<ValidationError>();
        validationErrors = applyDefaultQueryParameterValidation( queryParameters, validationErrors );

        if ( filterId.equals( "GENERIC_VISTA_LIST_DATA_FILTER" ) || isPatientCentric( filterId ) )
        {
            String icn = StringUtils.trimTrailingWhitespace( queryParameters.getFirst( ICN ) );
            List<String> excludeIds = queryParameters.get( EX_ID );

            if ( !StringUtils.hasText( icn ) )
            {
                validateDfnList(queryParameters, command, validationErrors);
            }

            if ( excludeIds != null && !excludeIds.isEmpty() )
            {
                if ( !StringUtils.hasLength( icn ) )
                {
                    validationErrors.add( new InvalidParameterValidationError( EX_ID,
                                    "Exclude identifier without NationalId is not allowed. Provide NationalId." ) );

                }
                else
                {
                    int firstOccurance = 0;
                    int lastOccurance = 0;
                    String excludeIdentity = null;
                    String excludeFacilityId = null;
                    String excludeAuthority = null;

                    for ( String excludeId : excludeIds )
                    {

                        if ( ( StringUtils.countOccurrencesOf( excludeId, SEPARATOR ) == 0 )
                                        || ( StringUtils.countOccurrencesOf( excludeId, SEPARATOR ) != 2 ) )
                        {
                            validationErrors.add( new InvalidParameterValidationError( EX_ID,
                                            "Exclude identifier format is dfn-facilityid-authority. - is missing." ) );
                        }
                        else
                        {

                            firstOccurance = excludeId.indexOf( SEPARATOR );
                            lastOccurance = excludeId.lastIndexOf( SEPARATOR );
                            excludeIdentity = excludeId.substring( 0, firstOccurance );
                            excludeFacilityId = excludeId.substring( firstOccurance + 1, lastOccurance );
                            excludeAuthority = excludeId.substring( lastOccurance + 1 );

                            if ( StringUtils.hasText( excludeIdentity )
                                            && ( !StringUtils.hasText( excludeAuthority ) || !StringUtils.hasText( excludeFacilityId ) ) )
                            {
                                validationErrors.add( new InvalidParameterValidationError( EX_ID,
                                                "Excluded facility and Excluded assigning authority are required if excluded identity is provided." ) );
                            }
                            else if ( ( !StringUtils.hasText( excludeAuthority ) && StringUtils.hasText( excludeFacilityId ) ) )
                            {
                                validationErrors.add( new InvalidParameterValidationError( EX_ID,
                                                "If excluded facility is provided without excluded assigning authority, excluded assigning authority must be provided." ) );
                            }
                        }

                    }
                }

            }
        }

        return validationErrors;
    }


	protected void validateDfnList(
			MultivaluedMap<String, String> queryParameters, String command,List<ValidationError> validationErrors) 
	{
		
		List<String> dfnList;
		dfnList = queryParameters.get( RS_ID );

		if ( dfnList == null || dfnList.isEmpty() )
		{
		    validationErrors.add( new InvalidParameterValidationError( "NationalId/ResolvedIdentifier",
		                    "Empty NationalId/ResolvedIdentifier. Provide NationalId or ResolvedIdentifier." ) );
		}
		else
		{
			String[] content = null;
			for ( String dfn : dfnList )
			{
				if ( command !=null &&  (command.equals(FederatingPatientRecordRestService.GETUPDATES_COMMAND) || command.equals(FederatingPatientRecordRestService.RESET_COMMAND)))
				{
					/* 
					 * in this scenario we are working with the NON-PATIENT for VPR 1.2
					 * we can ignore validation for the nonPatient dfn - 
					 * the nonPatient dfn is configured within the webservice class and will exist in proper structure
					 */
				}
				else if ( ( StringUtils.countOccurrencesOf( dfn, SEPARATOR ) == 0 ) || ( StringUtils.countOccurrencesOf( dfn, SEPARATOR ) != 2 ) )
			    {
			        validationErrors.add( new InvalidParameterValidationError( RS_ID,
			                        "Incorrect ResolvedIdentifier format, the correct format is identifier-facilityId-authority." ) );
			    }
			    else
			    {
			    	String validationMessage="One or all of these elements are missing in the ResolvedIdentifier:  identifier-facilityId-authority  All of them are required.";
			    	
			        content = dfn.split( SEPARATOR );
			        if ( content != null && content.length > 0 )
			        {
			            if(content.length > 2){
			            	//missing identifier or facility
			            	if ( ( content[0] == null || content[0].length() == 0 ) || ( content[1] == null || content[1].length() == 0 )
			                            || ( content[2] == null || content[2].length() == 0 ) )
			            	{
			            		validationErrors.add( new InvalidParameterValidationError( RS_ID,validationMessage) );
			            	}
			            }
			            else{
			            	//missing authority
			            	validationErrors.add( new InvalidParameterValidationError( RS_ID,validationMessage) );
			            } 
			        }
			        else
			        {
			            validationErrors.add( new InvalidParameterValidationError( RS_ID, "ResolvedIdentifier is required." ) );
			        }
			    }

			}
		}
	}

    protected List<ValidationError> applyDefaultQueryParameterValidation( MultivaluedMap<String, String> queryParameters,
                    List<ValidationError> validationErrors )
    {

        for ( String key : queryParameters.keySet() )
        {
            if ( !StringUtils.hasText( key ) )
            {
                validationErrors.add( new InvalidParameterValidationError( "Invalid Key", "Query Parameter key is empty." ) );
            }
            else
            {
                final List<String> values = queryParameters.get( key );

                if ( !key.equals( "excludeIdentifier" ) && !key.equals( "resolvedIdentifier" ) )
                {
                    if ( values == null || values.size() != 1 || !StringUtils.hasText( values.get( 0 ) ) )
                    {
                        validationErrors.add( new InvalidParameterValidationError( values, String.format(
                                        "Query parameter %s must have a single, non-empty string value.", key ) ) );
                    }
                }
            }
        }

        String responseType = queryParameters.getFirst( TYPE );

        if ( !StringUtils.hasText( responseType ) )
        {
            responseType = "json";
            //queryParameters.add( TYPE, "json" );
        }

        if ( !responseType.equals( "xml" ) && !responseType.equals( "json" ) )
        {
            validationErrors.add( new InvalidParameterValidationError( TYPE, "Response type can only be XML or JSON" ) );
        }

        String requestId = queryParameters.getFirst( REQUEST_ID );

        if ( !StringUtils.hasText( requestId ) )
        {
            validationErrors.add( new InvalidParameterValidationError( requestId, "Request parameter requestId was not provided or was empty." ) );
        }

        String clientName = queryParameters.getFirst( CLIENT_NAME );
        if ( !StringUtils.hasText( clientName ) )
        {
            validationErrors.add( new InvalidParameterValidationError( CLIENT_NAME, String.format(
                            "Request parameter %s was not provided or was empty.", CLIENT_NAME ) ) );
        }

        String clRequestTime = queryParameters.getFirst( CLIENT_REQUEST_TIME );
        if ( !StringUtils.hasText( clRequestTime ) )
        {
            validationErrors.add( new InvalidParameterValidationError( CLIENT_REQUEST_TIME, "clientRequestInitiationTime is empty." ) );
        }

        return validationErrors;
    }

    class InvalidParameterValidationError
        implements
            ValidationError
    {
        private Object invalidValue;

        private String message;


        public InvalidParameterValidationError( Object invalidValue, String message )
        {
            this.invalidValue = invalidValue;
            this.message = message;
        }


        @Override
        public Object getInvalidValue( )
        {
            return this.invalidValue;
        }


        @Override
        public String getMessage( )
        {
            return this.message;
        }
    }


    public List<ValidationError> constructValidationError( String param, String message )
    {
        List<ValidationError> errors = new ArrayList<ValidationError>();
        errors.add( new InvalidParameterValidationError( param, message ) );
        return errors;
    }


    @Override
    public boolean isPatientCentric( String filterId )
    {
        boolean patientCentric = true;
        if ( !filterId.contains( "PATIENT" ) )
        {
            patientCentric = false;
        }
        return patientCentric;
    }

}
