
package de.ivu.elect.jee.faces;

import static org.apache.commons.lang3.StringUtils.isNotBlank;

import java.text.MessageFormat;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.context.FacesContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.util.ClassUtil;

/*
 * NOTE: Als "msgKey" keine Rohstrings (z.B. Fehlermeldungen o.ä.) übergeben, da die Keys stets noch formatiert werden; bei
 * geschweiften Klammern kann es dann zu Fehlern kommen! Cf. ELECT-2698...
 */
public class FacesMessageUtil {

  private static final String BUNDLE_NAME = "msgs";

  private static final String ENUM_MESSAGE_KEY_SEPARATOR = ".";

  public static final String GLOBAL_MESSAGES_ID = "ctnMessages";

  private static final Logger LOG = LoggerFactory.getLogger(FacesMessageUtil.class);

  private FacesMessageUtil() {
    super();
  }

  public static FacesMessage addInfoMessage(String msgKey, Object... params) {
    return addInfoMessage(msgKey, msgKey, params);
  }

  public static FacesMessage addInfoMessage(String summaryKey, String detailKey, Object... params) {
    return addInfoMessage(FacesContext.getCurrentInstance(), summaryKey, detailKey, params);
  }

  public static FacesMessage addInfoMessage(FacesContext facesContext, String msgKey, Object... params) {
    return addInfoMessage(facesContext, msgKey, msgKey, params);
  }

  public static FacesMessage addInfoMessage(FacesContext facesContext, String summaryKey, String detailKey, Object... params) {
    return addMessage(facesContext, createInfoMessage(facesContext, summaryKey, detailKey, params));
  }

  public static FacesMessage addWarnMessage(String msgKey, Object... params) {
    return addWarnMessage(msgKey, msgKey, params);
  }

  public static FacesMessage addWarnMessage(String summaryKey, String detailKey, Object... params) {
    return addWarnMessage(FacesContext.getCurrentInstance(), summaryKey, detailKey, params);
  }

  public static FacesMessage addWarnMessage(FacesContext facesContext, String msgKey, Object... params) {
    return addWarnMessage(facesContext, msgKey, msgKey, params);
  }

  public static FacesMessage addWarnMessage(FacesContext facesContext, String summaryKey, String detailKey, Object... params) {
    return addMessage(facesContext, createWarnMessage(facesContext, summaryKey, detailKey, params));
  }

  public static FacesMessage addErrorMessage(String msgKey, Object... params) {
    return addErrorMessage(msgKey, msgKey, params);
  }

  public static FacesMessage addErrorMessage(String summaryKey, String detailKey, Object... params) {
    return addErrorMessage(FacesContext.getCurrentInstance(), summaryKey, detailKey, params);
  }

  public static FacesMessage addErrorMessage(FacesContext facesContext, String msgKey, Object... params) {
    return addErrorMessage(facesContext, msgKey, msgKey, params);
  }

  public static FacesMessage addErrorMessage(FacesContext facesContext, String summaryKey, String detailKey, Object... params) {
    return addMessage(facesContext, createErrorMessage(facesContext, summaryKey, detailKey, params));
  }

  private static FacesMessage addMessage(FacesContext facesContext, FacesMessage facesMessage) {
    if (facesContext != null && !containsMessage(facesContext, facesMessage)) {
      facesContext.addMessage(null, facesMessage);
    }

    return facesMessage;
  }

  private static boolean containsMessage(FacesContext facesContext, FacesMessage expected) {
    for (FacesMessage actual : facesContext.getMessageList()) {
      if (new FacesMessageComparable(actual).equals(expected)) {
        return true;
      }
    }

    return false;
  }

  public static FacesMessage createInfoMessage(FacesContext facesContext, String summaryKey, String detailKey, Object... params) {
    return createMessage(facesContext, FacesMessage.SEVERITY_INFO, summaryKey, detailKey, params);
  }

  public static FacesMessage createWarnMessage(FacesContext facesContext, String summaryKey, String detailKey, Object... params) {
    return createMessage(facesContext, FacesMessage.SEVERITY_WARN, summaryKey, detailKey, params);
  }

  public static FacesMessage createErrorMessage(FacesContext facesContext, String summaryKey, String detailKey, Object... params) {
    return createMessage(facesContext, FacesMessage.SEVERITY_ERROR, summaryKey, detailKey, params);
  }

  private static FacesMessage createMessage(FacesContext facesContext, Severity severity, String summaryKey, String detailKey, Object... params) {
    FacesMessage facesMessage = new FacesMessage();
    facesMessage.setSeverity(severity);
    facesMessage.setSummary(getFormattedMessage(facesContext, summaryKey, params));
    facesMessage.setDetail(getFormattedMessage(facesContext, detailKey, params));

    return facesMessage;
  }

  protected static String getFormattedMessage(FacesContext facesContext, String msgKey, Object... params) {
    // NOTE: getResourceText sorgt (seltsamerweise) schon für das Formatieren.
    return getResourceText(facesContext, msgKey, params);
  }

  public static String getResourceText(String msgKey, Object... params) {
    return getResourceText(FacesContext.getCurrentInstance(), msgKey, params);
  }

  public static String getResourceText(ResourceBundle resourceBundle, String msgKey, Object... params) {
    String text;

    try {
      text = resourceBundle.getString(msgKey);

    } catch (MissingResourceException e) {
      return "???" + msgKey + "???";
    }

    if (params != null) {
      try {
        text = MessageFormat.format(text, params);
      } catch (IllegalArgumentException e) {
        //  wenn das nicht weggefangen wird, schlägt es dubioser Weise beim Text: "Die importierte XML-Datei entspricht nicht den Vorgaben der XML-Schema-Datei \"elect-interfaces-wus-was.xsd\". Zeile: 2, Spalte 58, Meldung: unexpected element (uri:\"http://www.ivu.de/ELECT\", local:\"erfassteStimmen\"). Expected elements are <{http://www.ivu.de/ELECT}wahl>" fehl...
        // ---- FNE: lag daran, dass die Nachrichten hier doppelt bzw. dreifach formatiert wurden, die geschweiften Klammern aus den Parametern so in den Meldungstext kamen und dort nicht verstanden werden konnten... Cf. ELECT-2698
        LOG.warn("Fehler beim Formatieren: '" + text + "', args: " + params + ", meldung: " + e.getMessage());
      }
    }

    return text;
  }

  public static String getResourceText(FacesContext facesContext, String msgKey, Object... params) {
    if (facesContext == null) {
      // HINT Bei Tests steht kein Kontext zur Verfügung
      return "???" + msgKey + "???";
    }

    return getResourceText(getResourceBundle(facesContext), msgKey, params);
  }

  private static ResourceBundle getResourceBundle(FacesContext facesContext) {
    return facesContext.getApplication().getResourceBundle(facesContext, BUNDLE_NAME);
  }

  public static String getEnumMessageKey(Enum< ? > enumValue) {
    if (enumValue == null) {
      return null;
    }

    return getEnumMessageKeyPrefix(enumValue) + enumValue.name();
  }

  public static String getEnumMessageKey(Enum< ? > enumValueMain, Enum< ? > enumValueDetail) {
    String enumValueMainMsgKey = getEnumMessageKey(enumValueMain);

    if (isNotBlank(enumValueMainMsgKey) && enumValueDetail != null) {
      return enumValueMainMsgKey + ENUM_MESSAGE_KEY_SEPARATOR + enumValueDetail.name();
    }

    return null;
  }

  public static String getEnumMessageKeyPrefix(Enum< ? > enumValue) {
    return getEnumMessageKeyPrefix(enumValue.getClass());
  }

  public static String getEnumMessageKeyPrefix(Class< ? > enumClass) {
    return ClassUtil.getClassSimpleName(enumClass) + ENUM_MESSAGE_KEY_SEPARATOR;
  }
}
