

package gov.va.med.cds.exceptionframework;


import gov.va.med.cds.util.LogMessageUtil;

import org.apache.commons.logging.Log;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;


/**
 *
 * Handle the creation of the error message and error id of the exception.
 * The sanitized versions will contain no identifiable health information - only enough info
 * to look up more detailed exception info.
 *
 */
public class ExceptionUtil
{
    private static final int INIT_BUFFER_SIZE = 4500;


    /**
     * Generates the error message based on the message extracted from the exception, logging severity, loggedMessage id
     * and logs to the specified logger.
     *
     * @param e ExceptionInfo placeholder for exception details.
     * @param loggingSeverity Logging severity level of the exception message.
     * @param logger Specifies the logger - default.
     * @param requestId - Client provided unique request ID
     */
    public static void logException( List<ExceptionInfo> e, LoggingSeverity loggingSeverity, Log logger, String entryPoint, String requestId,
                    String applicationName )
    {
        logException( e, loggingSeverity, logger, null, entryPoint, requestId, applicationName );
    }


    /**
     * Generates the error message based on the message extracted from the exception, logging severity, loggedMessage id
     * and logs to the specified logger.
     *
     * @param e Exception that is handled.
     * @param loggingSeverity Logging severity level of the exception message.
     * @param logger Specifies the logger - default.
     * @param requestId - Client provided unique request ID
     * @param requestId - Application name
     */
    public static void logException( Exception exception, Log logger, String message, String entryPoint, String requestId, String applicationName )
    {
        logException( exception, LoggingSeverity.FATAL, logger, message, entryPoint, requestId, applicationName );
    }

    /**
     * Generates the error message based on the message extracted from the exception, additional message, loggedMessage
     * id and logs to the specified logger.
     *
     * @param e - Exception that is handled.
     * @param message - Additional message to be embedded in the exception message.
     * @param requestId - Client provided unique request ID
     * @param applicationName - Application name
     */
    public static void logException( ExceptionInfo e, Log logger, String message, String entryPoint, String requestId, String applicationName )
    {
        List<ExceptionInfo> exceptionInfoList = new ArrayList<ExceptionInfo>();
        exceptionInfoList.add( e );

        logException( exceptionInfoList, LoggingSeverity.FATAL, logger, message, entryPoint, requestId, applicationName );
    }

    private static void logException( List<ExceptionInfo> e, LoggingSeverity loggingSeverity, Log logger, String message, String entryPoint,
                    String requestId, String applicationName )
    {

        for ( ExceptionInfo exceptionInfo : e )
        {
            String exceptionTrace = getExceptionLog( exceptionInfo, message, requestId );
            logMessage( logger, loggingSeverity, LogMessageUtil.buildMessage( entryPoint, requestId, applicationName, exceptionTrace ),
                            exceptionInfo.getException() );
        }

    }


    /**
     * Generates the error message based on the message extracted from the exception, logging severity, loggedMessage id
     * and logs to the specified logger.
     *
     * @param e Exception.
     * @param loggingSeverity Logging severity level of the exception message.
     * @param logger Specifies the logger.
     * @param requestId - Client provided unique request ID
     * @param applicationName - Application name
     */
    public static void logException( Exception exception, LoggingSeverity loggingSeverity, Log logger, String message, String entryPoint,
                    String requestId, String applicationName )
    {
        String exceptionTrace = getExceptionLog( exception, message, requestId );
        logMessage( logger, loggingSeverity, LogMessageUtil.buildMessage( entryPoint, requestId, applicationName, exceptionTrace ), exception );
    }

    private static void logMessage( Log logger, LoggingSeverity loggingSeverity, String message, Throwable exception )
    {
        switch ( loggingSeverity )
        {
        case WARNING:
            logger.warn( message, exception );
            break;
        case ERROR:
            logger.error( message, exception );
            break;
        case FATAL:
        default:
            logger.fatal( message, exception );
        }
    }


    private static void logMessage( Log logger, LoggingSeverity loggingSeverity, Object message )
    {
        switch ( loggingSeverity )
        {
        case WARNING:
            logger.warn( message );
            break;
        case ERROR:
            logger.error( message );
            break;
        case FATAL:
        default:
            logger.fatal( message );
        }
    }


    private static String getExceptionLog( ExceptionInfo exception, String message, String requestId )
    {
        StringBuilder logMessage = new StringBuilder( INIT_BUFFER_SIZE );

        logMessage.append( exception.getClass().getName() ).append( "," );
        logMessage.append( message ).append( "," ); // exception name
        // Appended a more readable cause
        try
        {
            if ( !exception.getClientMessage().equals( null ) )
            {
                logMessage.append( exception.getException() + " " + exception.getClientMessage() ).append( "," ); //
            }
        }
        catch ( NullPointerException npe )
        {
            logMessage.append( exception.getExceptionMessage() ).append( "," ); // exception
        }

        // reason
        logMessage.append( requestId ).append( "," ); // request id
        logMessage.append( getDetailedMessage( exception.getException() ) );
        return logMessage.toString();
    }


    private static String getExceptionLog( Exception exception, String message, String requestId )
    {
        StringBuilder logMessage = new StringBuilder( INIT_BUFFER_SIZE );

        logMessage.append( exception.getClass().getName() ).append( "," );
        logMessage.append( message ).append( "," ); // exception name
        // Appended a more readable cause
        try
        {
            if ( message != null )
            {
                logMessage.append( exception + " " + message ).append( "," ); //
            }
        }
        catch ( NullPointerException npe )
        {
            logMessage.append( exception.getMessage() ).append( "," ); // exception
        }

        // reason
        logMessage.append( requestId ).append( "," ); // request id
        logMessage.append( getDetailedMessage( exception ) );
        return logMessage.toString();
    }


    private static StringBuffer getDetailedMessage( Throwable exception )
    {
        StringBuffer detailedMessage = new StringBuffer();

        try
        {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter( sw );
            exception.printStackTrace( pw );
            detailedMessage.append( sw.toString() );
            pw.close();
            sw.close();
        }
        catch ( Exception e )
        {
            detailedMessage.append( e.toString() );
        }
        return detailedMessage;
    }


    /**
     * Formats a client message to add dynamic values like templateId/filterId to the message.
     *
     * @param userMessage Message with markers where args are to be inserted.
     * @param dataValues Argument list to populate in the user message.
     * @return Properly formatted message.
     */
    public static String formatMessage( String userMessage, String[] dataValues )
    {
        if ( dataValues != null && dataValues.length > 0 )
        {
            MessageFormat messageFormat = new MessageFormat( userMessage );
            userMessage = messageFormat.format( dataValues );
        }
        return userMessage;
    }
}
