
package de.ivu.elect.business.system.boundary;

import java.io.Serializable;
import java.util.Date;

import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.mandant.boundary.MandantDAO;
import de.ivu.elect.business.mandant.entity.AbstractMandant;
import de.ivu.elect.business.mandant.entity.EntityMitMandant;
import de.ivu.elect.business.mandant.entity.EntityPKMitMandant;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.system.entity.LogNachrichtArt;
import de.ivu.elect.business.system.entity.SystemLog;
import de.ivu.elect.business.system.entity.SystemLogQuelle;
import de.ivu.elect.jee.entity.SimpleDAO;
import de.ivu.elect.util.ThrowableUtil;


import static de.ivu.elect.business.system.entity.LogNachrichtArt.FEHLER;
import static de.ivu.elect.business.system.entity.LogNachrichtArt.INFO;
import static de.ivu.elect.business.system.entity.LogNachrichtArt.WARNUNG;

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class SystemLogWriter implements Serializable {

  private static final long serialVersionUID = 1L;

  static Logger LOG = LoggerFactory.getLogger(SystemLogWriter.class);

  @Inject
  transient SystemLogDAO systemLogDAO;

  @Inject
  transient SimpleDAO simpleDAO;

  @Inject
  transient MandantDAO mandantDAO;

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeInfoLogMessageFuerMandant(EntityPKMitMandant entityPKMitMandant, String nachricht, SystemLogQuelle quelle) {
    loggeUndPersistiere(bestimmeMandant(entityPKMitMandant), nachricht, null, quelle, INFO);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeInfoLogMessageFuerMandant(EntityMitMandant entityMitMandant, String nachricht, SystemLogQuelle quelle) {
    loggeUndPersistiere(bestimmeMandant(entityMitMandant), nachricht, null, quelle, INFO);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeInfoLogMessageFuerSystemMandant(String nachricht, SystemLogQuelle quelle) {
    loggeUndPersistiere(null, nachricht, null, quelle, INFO);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeWarnLogMessageFuerMandant(EntityMitMandant entityMitMandant, String nachricht, SystemLogQuelle quelle) {
    loggeUndPersistiere(bestimmeMandant(entityMitMandant), nachricht, null, quelle, WARNUNG);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeWarnLogMessageFuerMandant(EntityMitMandant entityMitMandant, String nachricht, Throwable ursache, SystemLogQuelle quelle) {
    loggeUndPersistiere(bestimmeMandant(entityMitMandant), nachricht, ursache, quelle, WARNUNG);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeWarnLogMessageFuerSystemMandant(String nachricht, SystemLogQuelle quelle) {
    loggeUndPersistiere(null, nachricht, null, quelle, WARNUNG);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeErrorLogMessageFuerMandant(EntityMitMandant entityMitMandant, String nachricht, SystemLogQuelle quelle) {
    loggeUndPersistiere(bestimmeMandant(entityMitMandant), nachricht, null, quelle, FEHLER);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeErrorLogMessageFuerMandant(EntityPKMitMandant entityPKMitMandant, String nachricht, SystemLogQuelle quelle) {
    loggeUndPersistiere(bestimmeMandant(entityPKMitMandant), nachricht, null, quelle, FEHLER);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeErrorLogMessageFuerMandant(EntityMitMandant entityMitMandant, String nachricht, Throwable ursache, SystemLogQuelle quelle) {
    loggeUndPersistiere(bestimmeMandant(entityMitMandant), nachricht, ursache, quelle, FEHLER);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeErrorLogMessageFuerMandantUndSystemMandant(EntityMitMandant entityMitMandant, String nachricht, Throwable ursache, SystemLogQuelle quelle) {
    loggeUndPersistiere(bestimmeMandant(entityMitMandant), nachricht, ursache, quelle, FEHLER, true);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeErrorLogMessageFuerMandant(EntityPKMitMandant entityPKMitMandant, String nachricht, Throwable ursache, SystemLogQuelle quelle) {
    loggeUndPersistiere(bestimmeMandant(entityPKMitMandant), nachricht, ursache, quelle, FEHLER);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeErrorLogMessageFuerSystemMandant(String nachricht, SystemLogQuelle quelle) {
    loggeUndPersistiere(null, nachricht, null, quelle, FEHLER);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeErrorLogMessageFuerSystemMandant(String nachricht, Throwable ursache, SystemLogQuelle quelle) {
    loggeUndPersistiere(null, nachricht, ursache, quelle, FEHLER);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeWarnLogMessageFuerSystemMandant(String nachricht, Throwable ursache, SystemLogQuelle quelle) {
    loggeUndPersistiere(null, nachricht, ursache, quelle, WARNUNG);
  }

  @Deprecated // Mandant fehlt
  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeWarnLogMessageNewTransaction(String nachricht, SystemLogQuelle quelle) {
    loggeUndPersistiere(null, nachricht, null, quelle, WARNUNG);
  }

  @Deprecated // Mandant fehlt
  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeErrorLogMessageNewTransaction(String nachricht, Throwable ursache, SystemLogQuelle quelle) {
    loggeUndPersistiere(null, nachricht, ursache, quelle, FEHLER);
  }

  @Deprecated // Mandant fehlt
  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void writeErrorLogMessageNewTransaction(String nachricht, SystemLogQuelle quelle) {
    loggeUndPersistiere(null, nachricht, null, quelle, FEHLER);
  }

  private void persistSystemLog(Mandant mandant, String nachricht, SystemLogQuelle quelle, LogNachrichtArt art, boolean systemMandantImmer) {
    try {
      Mandant mandantFinal = mandantFallbackSystemMandant(mandant);
      if (mandantFinal == null) {
        LOG.error("Kein Mandant für Systemlog-Eintrag: " + nachricht + ", quelle:" + quelle + ", art: " + art);
        return;
      }

      SystemLog systemLog = new SystemLog();
      systemLog.setMandant(mandantFinal);
      if (StringUtils.isBlank(nachricht)) {
        systemLog.setLogNachricht("-");
      } else {
        systemLog.setLogNachricht(nachricht);
      }
      systemLog.setLogNachrichtArt(art);
      systemLog.setQuelle(quelle);
      systemLog.setZeitpunkt(new Date());
      systemLogDAO.persist(systemLog);

      if (!mandantFinal.getArt().isSystemMandant() && systemMandantImmer) {
        persistSystemLog(null, nachricht, quelle, art, false);
      }
    } catch (Exception e) {
      LOG.error("Fehler beim Erstellen des Systemlog-Eintrags Mandant:" + mandant + ", Nachricht: " + nachricht + ", quelle:" + quelle + ", art: " + art, e);
    }
  }

  private Mandant mandantFallbackSystemMandant(Mandant mandant) {
    if (mandant == null) {
      return AbstractMandant.getSystemMandant(mandantDAO);
    }
    return mandant;
  }

  private String appendThrowableToNachricht(String nachricht, Throwable throwable) {
    if (throwable == null) {
      return nachricht;
    }
    StringBuilder builder = new StringBuilder();

    if (StringUtils.isNotBlank(nachricht)) {
      builder.append(nachricht);
      builder.append(" (");
    }

    builder.append(ThrowableUtil.getMessagesAllerGruendeAlsString(throwable, " --> "));

    if (StringUtils.isNotBlank(nachricht)) {
      builder.append(")");
    }

    return builder.toString();
  }

  private Mandant bestimmeMandant(EntityPKMitMandant entityPKMitMandant) {
    try {
      return entityPKMitMandant.ladeMandant(simpleDAO.getEntityManager());
    } catch (Exception e) {
      LOG.warn("Fehler beim Bestimmen des Mandanten für: " + entityPKMitMandant + ", " + e.getMessage(), e);
    }
    return null;

  }

  private Mandant bestimmeMandant(EntityMitMandant entityMitMandant) {
    try {
      return entityMitMandant.ladeMandant(simpleDAO.getEntityManager());
    } catch (Exception e) {
      LOG.warn("Fehler beim Bestimmen des Mandanten für: " + entityMitMandant + ", " + e.getMessage(), e);
    }
    return null;
  }

  private String logge(Mandant mandant) {
    if (mandant == null) {
      return "(null)";
    }
    return "" + mandant.getNummer();
  }

  private void loggeUndPersistiere(Mandant mandant, String nachricht, Throwable ursache, SystemLogQuelle quelle, LogNachrichtArt art) {
    loggeUndPersistiere(mandant, nachricht, ursache, quelle, art, false);
  }

  private void loggeUndPersistiere(Mandant mandant, String nachricht, Throwable ursache, SystemLogQuelle quelle, LogNachrichtArt art, boolean systemMandantImmer) {
    String nachrichtMitThrowable = appendThrowableToNachricht(nachricht, ursache);
    if (art.isInfo()) {
      LOG.info("Systemlog - Art='" + art + "', Mandant='" + logge(mandant) + "', quelle='" + quelle + "', nachricht='" + nachrichtMitThrowable + "'", ursache);
    } else {
      LOG.warn("Systemlog - Art='" + art + "', Mandant='" + logge(mandant) + "', quelle='" + quelle + "', nachricht='" + nachrichtMitThrowable + "'", ursache);
    }
    persistSystemLog(mandant, nachrichtMitThrowable, quelle, art, systemMandantImmer);
  }

}
