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

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import javax.validation.constraints.Null;

import org.primefaces.model.UploadedFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.CurrentCustomer;
import de.ivu.elect.business.dokumente.control.DokumentKonverterLookup;
import de.ivu.elect.business.dokumente.control.DokumentVorlageCreate;
import de.ivu.elect.business.dokumente.entity.DokumentArchiveUploadResultEntry;
import de.ivu.elect.business.dokumente.entity.DokumentArchiveUploadResultStatus;
import de.ivu.elect.business.dokumente.entity.DokumentVorlage;
import de.ivu.elect.business.dokumente.entity.DokumentVorlageAnlageArt;
import de.ivu.elect.business.mandant.boundary.MandantDAO;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.wahl.boundary.WahlDAO;
import de.ivu.elect.business.wahl.boundary.WahlterminDAO;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlPK;
import de.ivu.elect.business.wahl.entity.WahlartDokumentvorlagen;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import de.ivu.elect.business.wahl.entity.WahlterminPK;

@Stateless
public class DefaultDokumentVorlagenInit implements Serializable {
  private static final long serialVersionUID = 1L;

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

  @Inject
  private transient DokumentVorlageCreate dokumentVorlageCreate;

  @Inject
  private transient DokumentKonverterLookup dokumentKonverterLookup;

  @Inject
  private transient CurrentCustomer currentCustomer;

  @Inject
  private transient WahlterminDAO wahlterminDAO;

  @Inject
  private transient WahlDAO wahlDAO;

  @Inject
  private transient MandantDAO mandantDAO;

  @Inject
  private transient DokumentVorlageDAO dokumentVorlageDAO;

  public List<DokumentArchiveUploadResultEntry> initDefaultDokumentVorlagen(Wahltermin wahlterminNotAttached, boolean zuruecksetzenAufStandard) {
    Wahltermin wahltermin = wahlterminDAO.attach(wahlterminNotAttached);
    if (wahltermin == null) {
      return Collections.emptyList();
    }
    LOG.info("Aktualisiere Dokumentvorlagen für: " + wahltermin + ", zuruecksetzenAufStandard=" + zuruecksetzenAufStandard);
    List<DokumentArchiveUploadResultEntry> dokumentArchiveUploadResultEntries = dokumentvorlagenUebernehmen(wahltermin, null, wahltermin.getDokumentvorlagen(), zuruecksetzenAufStandard);
    LOG.info("Aktualisiere Dokumentvorlagen für: " + wahltermin + ", dokumentArchiveUploadResultEntries=" + dokumentArchiveUploadResultEntries.size());
    return dokumentArchiveUploadResultEntries;
  }

  public List<DokumentArchiveUploadResultEntry> initDefaultDokumentVorlagen(Wahl wahlNotAttached, boolean zuruecksetzenAufStandard) {
    Wahl wahl = wahlDAO.attach(wahlNotAttached);
    if (wahl == null) {
      return Collections.emptyList();
    }
    LOG.info("Aktualisiere Dokumentvorlagen für: " + wahl + ", zuruecksetzenAufStandard=" + zuruecksetzenAufStandard);
    List<DokumentArchiveUploadResultEntry> dokumentArchiveUploadResultEntries = dokumentvorlagenUebernehmen(wahl.getWahltermin(), wahl, wahl.getWahlart().getDokumentvorlagen(), zuruecksetzenAufStandard);
    LOG.info("Aktualisiere Dokumentvorlagen für: " + wahl + ", dokumentArchiveUploadResultEntries=" + dokumentArchiveUploadResultEntries.size());
    return dokumentArchiveUploadResultEntries;
  }

  private List<DokumentArchiveUploadResultEntry> dokumentvorlagenUebernehmen(Wahltermin wahltermin, @Nullable Wahl wahl, WahlartDokumentvorlagen dokumentvorlagen,
                                                                             boolean zuruecksetzenAufStandard) {
    if (dokumentvorlagen.isVorlagenVorhanden()) {
      String resourceName = dokumentvorlagen.getResourceName();
      try (InputStream is = Thread.currentThread().getContextClassLoader()
          .getResourceAsStream(resourceName)) {

        if (is == null) {
          LOG.warn("An Wahlart hinterlegte Ressourcen nicht gefunden: '" + resourceName + "'");
        } else {
          UploadedFile archive = new UploadedFile() {

            @Override
            public String getFileName() {
              return resourceName;
            }

            @Override
            public List<String> getFileNames() {
              throw new UnsupportedOperationException();
            }

            @Override
            public InputStream getInputstream() throws IOException {
              return is;
            }

            @Override
            public long getSize() {
              throw new UnsupportedOperationException();
            }

            @Override
            public byte[] getContents() {
              throw new UnsupportedOperationException();
            }

            @Override
            public String getContentType() {
              throw new UnsupportedOperationException();
            }

            @Override
            public void write(String s) {
              throw new UnsupportedOperationException();
            }
          };

          // ELECT-9074
          Function<DokumentVorlageAnlageArt, DokumentArchiveUploadResultStatus> updateStrategie = art -> {
            if (art.isAusAnwendung()) {
              return DokumentArchiveUploadResultStatus.AKTUALISIERT;
            }
            if (zuruecksetzenAufStandard) {
              return DokumentArchiveUploadResultStatus.BEREITS_MANUELL_ANGELEGT_VORHANDEN_AKTUALISIERT;
            }
            return DokumentArchiveUploadResultStatus.BEREITS_MANUELL_ANGELEGT_VORHANDEN;

          };

          Set<DokumentVorlage> ueberzaehligeDokumentvorlagen;
          if (wahl != null) {
            ueberzaehligeDokumentvorlagen = ladeUeberzaehligeDokumentvorlagen(wahl);
          } else {
            ueberzaehligeDokumentvorlagen = ladeUeberzaehligeDokumentvorlagen(wahltermin);
          }

          List<DokumentArchiveUploadResultEntry> dokumentArchiveUploadResultEntries = dokumentVorlageCreate
              .saveArchive(archive, wahl, wahltermin, dokumentKonverterLookup.collectDokumentVorlageArten(), updateStrategie,
                  DokumentVorlageAnlageArt.AUS_ANWENDUNG, ueberzaehligeDokumentvorlagen
              );

          for (DokumentArchiveUploadResultEntry dokumentArchiveUploadResultEntry : dokumentArchiveUploadResultEntries) {
            LOG.info("Dokumentvorlage Result: " + dokumentArchiveUploadResultEntry);
          }

          for (DokumentVorlage dokumentVorlage : ueberzaehligeDokumentvorlagen) {
            LOG.info("Entferne überzählige Dokumentvorlage: " + dokumentVorlage + " für Wahltermin: " + wahltermin + ", Wahl: " + wahl);
            dokumentVorlageCreate.loescheDokumentvorlage(dokumentVorlage);
          }
          return dokumentArchiveUploadResultEntries;

        }

      } catch (Exception e) {
        LOG.warn("Fehler beim Anlegen der hinterlegten Dokumentvorlagen: " + e.getMessage(), e);
      }

    } else {
      LOG.info("Keine Dokumentvorlagen übernommen");
    }
    return Collections.emptyList();
  }

  private Set<DokumentVorlage> ladeUeberzaehligeDokumentvorlagen(Wahl wahl) {
    return dokumentVorlageDAO.findByWahlOrderByArtAndName(wahl).stream()
        .filter(d -> d.getAnlageArt().isAusAnwendung()).collect(Collectors.toSet());
  }

  private Set<DokumentVorlage> ladeUeberzaehligeDokumentvorlagen(Wahltermin wahltermin) {
    return dokumentVorlageDAO.findByWahlterminOrderByArtAndName(wahltermin).stream()
        .filter(d -> d.getAnlageArt().isAusAnwendung()).collect(Collectors.toSet());
  }

  public void initDefaultDokumentVorlagenPostStartup() {
    LOG.info("initDefaultDokumentVorlagenPostStartup()");
    for (Mandant mandant : mandantDAO.findByAll()) {
      LOG.info("initDefaultDokumentVorlagenPostStartup - Mandant: " + mandant);

      for (Wahltermin wahltermin : wahlterminDAO.findByAll(mandant)) {
        LOG.info("initDefaultDokumentVorlagenPostStartup - Mandant: " + mandant + ", Wahltermin: " + wahltermin);
        initDefaultDokumentVorlagen(wahltermin, false);
      }

      for (Wahl wahl : wahlDAO.findByAll(mandant)) {
        LOG.info("initDefaultDokumentVorlagenPostStartup - Mandant: " + mandant + ", Wahl: " + wahl);
        initDefaultDokumentVorlagen(wahl, false);
      }

    }
    LOG.info("initDefaultDokumentVorlagenPostStartup() - done");

  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void initDefaultDokumentVorlagen(WahlPK wahlPK) {
    if (wahlPK != null) {
      initDefaultDokumentVorlagen(wahlDAO.findByPrimaryKey(wahlPK), false);
    }
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void initDefaultDokumentVorlagen(WahlterminPK wahlterminPK) {
    if (wahlterminPK != null) {
      initDefaultDokumentVorlagen(wahlterminDAO.findByPrimaryKey(wahlterminPK), false);
    }
  }

  public List<DokumentArchiveUploadResultEntry> setzeDefaultDokumentVorlagenZurueck(@Nullable Wahltermin selectedWahlterminNotAtached,
                                                                                    @Nullable Wahl selectedWahlNotAtached) {
    List<DokumentArchiveUploadResultEntry> result = new ArrayList<>();
    if (selectedWahlterminNotAtached != null) {
      result.addAll(initDefaultDokumentVorlagen(selectedWahlterminNotAtached, true));
    }
    if (selectedWahlNotAtached != null) {
      result.addAll(initDefaultDokumentVorlagen(selectedWahlNotAtached, true));
    }

    return result;
  }
}
