package com.oguerreiro.resilient.activity;

import java.time.Instant;
import java.util.Locale;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;

import com.oguerreiro.resilient.domain.AbstractResilientLongIdEntity;
import com.oguerreiro.resilient.domain.ResilientLog;
import com.oguerreiro.resilient.domain.enumeration.ResilientLogLevel;
import com.oguerreiro.resilient.error.ResilientException;
import com.oguerreiro.resilient.error.ResilientRuntimeException;
import com.oguerreiro.resilient.service.ResilientLogService;

/**
 * @param <AR> the {@link ActivityResult} to use as response for this {@link Activity}
 * @param <AG> the {@link ActivityGuide} to use as configuration for this {@link Activity}
 * @param <AI> the interface that defines the Activity bean. Will be used to automatically infer the bean and supply the
 *             self() bean reference
 */
public abstract class AbstractActivity<AR extends ActivityResult, AG extends ActivityGuide, AI extends Activity<AR, AG>>
    extends AbstractSelfEnabler<AI> implements Activity<AR, AG> {

  @Autowired
  private ResilientLogService resilientLogService;

  @Autowired
  private MessageSource messageSource;

  public final String getLocalizedMessage(Throwable exception) {
    String localizedMessage = "";
    Locale locale = LocaleContextHolder.getLocale();

    if (exception instanceof ResilientException) {
      //This is a translatable Resilient message
      ResilientException resilientException = (ResilientException) exception;
      try {
        localizedMessage = messageSource.getMessage(resilientException.getMessageCode(),
            resilientException.getMessageArgs(), locale);
      } catch (NoSuchMessageException ex) {
        // Fall back to exception LocalizedMessage.
        localizedMessage = exception.getLocalizedMessage();
      }

      if (localizedMessage == null) {
        localizedMessage = resilientException.getMessageCode();
      }
    } else if (exception instanceof ResilientRuntimeException) {
      //This is a translatable Resilient message
      ResilientRuntimeException resilientException = (ResilientRuntimeException) exception;
      try {
        localizedMessage = messageSource.getMessage(resilientException.getMessageCode(),
            resilientException.getMessageArgs(), locale);
      } catch (NoSuchMessageException ex) {
        // Fall back to exception LocalizedMessage.
        localizedMessage = exception.getLocalizedMessage();
      }

      if (localizedMessage == null) {
        localizedMessage = resilientException.getMessageCode();
      }
    } else {
      localizedMessage = exception.getLocalizedMessage();
    }

    return localizedMessage;
  }

  protected void logError(AbstractResilientLongIdEntity<?> id, String username, Throwable error) {
    String localizedMessage = getLocalizedMessage(error);

    this.logError(id, username, localizedMessage);
  }

  protected void logError(AbstractResilientLongIdEntity<?> id, String username, String message) {
    this.log(ResilientLogLevel.ERROR, id, username, message);
  }

  protected void logInfo(AbstractResilientLongIdEntity<?> id, String username, String message) {
    this.log(ResilientLogLevel.INFO, id, username, message);
  }

  private void log(ResilientLogLevel level, AbstractResilientLongIdEntity<?> id, String username, String message) {
    ResilientLog log = new ResilientLog();

    //@formatter:off
    log
      .ownerId(id.getId())
      .level(level)
      .creationDate(Instant.now())
      .creationUsername(username)
      .logMessage(message);
    //@formatter:on

    this.resilientLogService.saveWithNewTransaction(log);
  }
}
