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

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;

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

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

import de.ivu.elect.business.gebietsbaum.control.GebietsartCreate;
import de.ivu.elect.business.gebietsbaum.control.GebietsartenInitialerImport;
import de.ivu.elect.business.gebietsbaum.entity.AbstractGebietsart;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.mandant.boundary.MandantDAO;
import de.ivu.elect.business.mandant.entity.AbstractMandant;
import de.ivu.elect.business.mandant.entity.Mandant;

@Stateless
public class GebietsartCreator {

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

  @Inject
  private transient MandantDAO mandantDAO;

  @Inject
  private transient GebietsartDAO gebietsartDAO;

  @Inject
  private transient GebietsartCreate gebietsartCreate;

  @Inject
  private transient DefaultAnzulegendeGebietsartCreator defaultAnzulegendeGebietsartCreator;

  @Inject
  private transient GebietsartenInitialerImport gebietsartenInitialerImport;

  public GebietsartCreator() {
    super();
  }

  @Deprecated // nur für Tests verwenden!
  protected GebietsartCreator(DefaultAnzulegendeGebietsartCreator defaultAnzulegendeGebietsartCreator) {
    super();

    this.defaultAnzulegendeGebietsartCreator = defaultAnzulegendeGebietsartCreator;
  }

  @TransactionAttribute(TransactionAttributeType.SUPPORTS)
  public Collection< ? extends GebietsartTemplate> getGebietsartTemplates(Mandant mandantDetached) {
    return getAllGebietsarten(mandantDetached);
  }

  public Collection< ? extends GebietsartTemplate> getAnzulegendeGebietsartTemplates() {
    Collection<GebietsartTemplate> result = new ArrayList<>(getAnzulegendeCustomGebietsartTemplates());

    // ELECT-7624
    for (GebietsartTemplate defaultGebietsartTemplate : getAnzulegendeDefaultGebietsartTemplates()) {
      if (result.stream().noneMatch(equalsCustomGebietsart(defaultGebietsartTemplate))) {
        result.add(defaultGebietsartTemplate);
      }
    }

    return result;
  }

  private Predicate< ? super GebietsartTemplate> equalsCustomGebietsart(GebietsartTemplate defaultGebietsartTemplate) {
    return customGebietsartTemplate -> {
      boolean equalsCustomGebietsartName = customGebietsartTemplate.equalsGebietsart(defaultGebietsartTemplate.getGebietsartName());
      boolean equalsCustomGebietsartBezirksart = !defaultGebietsartTemplate.getGebietsartBezirksart().isKeine()
          && customGebietsartTemplate.getGebietsartBezirksart().equals(defaultGebietsartTemplate.getGebietsartBezirksart());

      return equalsCustomGebietsartName || equalsCustomGebietsartBezirksart;
    };
  }

  protected Collection< ? extends GebietsartTemplate> getAnzulegendeDefaultGebietsartTemplates() {
    return defaultAnzulegendeGebietsartCreator.getAnzulegendeGebietsartTemplates();
  }

  protected Collection< ? extends GebietsartTemplate> getAnzulegendeCustomGebietsartTemplates() {
    return defaultAnzulegendeGebietsartCreator.getAnzulegendeGebietsartTemplates();
  }

  public void initGebietsartenAlleMandanten() {
    for (Mandant mandant : mandantDAO.findByAll()) {
      if (mandant.getArt().isCustomerMandant()) {
        initGebietsarten(mandant);
      }
    }
  }

  public void initGebietsarten(Mandant mandantDetached) {
    Mandant mandant = attachOrFallback(mandantDetached);

    if (mandant.getArt().isCustomerMandant()) {
      LOG.info("Initialisiere Gebietsarten für Mandant: " + mandant.getNummer());

      if (mandant.getArt().isCustomerMandant()) {
        List<Gebietsart> vorhandeneGebietsarten = getAllGebietsarten(mandant);

        LOG.info("Vorhandene Gebietsarten: " + vorhandeneGebietsarten.size());

        boolean angelegt = false;

        for (GebietsartTemplate gebietsartTemplate : getAnzulegendeGebietsartTemplates()) {
          Gebietsart vorhandeneGebietsart = vorhanden(gebietsartTemplate, vorhandeneGebietsarten);

          if (vorhandeneGebietsart == null) {
            Gebietsart gebietsart = Gebietsart.createGebietsartFromTemplate(gebietsartTemplate);
            gebietsart.setMandant(mandant);

            gebietsartCreate.save(gebietsart);

            LOG.info("Gebietsart '" + gebietsart.getName() + "' angelegt");

            angelegt = true;
          } else {
            aktualisiereGebietsart(vorhandeneGebietsart, gebietsartTemplate);
          }
        }

        if (angelegt) {
          gebietsartDAO.flush();
        }

        gebietsartenInitialerImport.importiereGebietsartenWennVorhanden(mandant);
      } else {
        LOG.info("Gebietsarten für Mandant: " + mandant.getNummer() + " mit Art: " + mandant.getArt() + " werden nicht angelegt");
      }
    }
  }

  private void aktualisiereGebietsart(Gebietsart vorhandeneGebietsart, GebietsartTemplate gebietsartTemplate) {
    // ELECT-8350
    AbstractGebietsart.fillGebietsartFromTemplate(vorhandeneGebietsart, gebietsartTemplate);

    if (vorhandeneGebietsart.isAttributeChanged()) {
      LOG.info("Bestehende Gebietsart aktualisiert: " + vorhandeneGebietsart);
    } else {
      LOG.info("Bestehende Gebietsart unverändert: " + vorhandeneGebietsart);
    }
  }

  protected List<Gebietsart> getAllGebietsarten(Mandant mandantDetached) {
    return gebietsartDAO.findByAll(attachOrFallback(mandantDetached));
  }

  protected Gebietsart vorhanden(GebietsartTemplate gebietsartTemplate, List<Gebietsart> vorhandeneGebietsarten) {
    return vorhandeneGebietsarten.stream().filter(gebietsartTemplate::equalsGebietsart).findFirst().orElse(null);
  }

  public Gebietsart getGebietsart(Mandant mandantDetached, GebietsartTemplate gebietsartTemplate) {
    for (Gebietsart gebietsart : getAllGebietsarten(mandantDetached)) {
      if (gebietsartTemplate.equalsGebietsart(gebietsart)) {
        return gebietsart;
      }
    }

    throw new IllegalStateException("Keine Gebietsart gefunden: '" + gebietsartTemplate + "'");
  }

  public GebietsartTemplate findByName(Mandant mandantDetached, String gebietsartName) {
    Collection< ? extends GebietsartTemplate> vorhandeneTemplates = getGebietsartTemplates(mandantDetached);

    for (GebietsartTemplate gebietsartTemplate : vorhandeneTemplates) {
      if (gebietsartTemplate.equalsGebietsart(gebietsartName)) {
        return gebietsartTemplate;
      }
    }

    throw new IllegalArgumentException("Kein Gebietsart-Template mit dem Namen '" + gebietsartName + "' gefunden. Vorhandene Templates: " + vorhandeneTemplates);
  }

  private Mandant attachOrFallback(Mandant template) {
    try {
      Mandant mandant = mandantDAO.attach(template);

      if (mandant != null) {
        return mandant;
      }
    } catch (Exception expectedIfNotPersistent) {
      // ELECTVAPP-1476, Check auf Mandant#isPersistent führt zu LazyInitializationException...
    }

    // ELECTKOM-142
    LOG.error("ENTFERNEN ELECTKOM-142");

    return AbstractMandant.getCustomerMandant(mandantDAO);
  }
}
