package de.ivu.elect.presentation.praesentation.table;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;

import de.ivu.elect.business.ergebnis.boundary.ErgebnisExtendedDAO;
import de.ivu.elect.business.internet.boundary.PraesentationUploadStatusStore;
import de.ivu.elect.business.internet.entity.PraesentationUploadStatusEntry;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.praesentation.entity.PraesentationBereitstellen;
import de.ivu.elect.business.praesentation.entity.PraesentationStatistikEntry;
import de.ivu.elect.business.praesentation.entity.PraesentationStatistikSearchResult;
import de.ivu.elect.business.praesentation.entity.PraesentationUploadServiceStatus;
import de.ivu.elect.business.praesentation.entity.PraesentationUploadServiceTyp;
import de.ivu.elect.business.wahl.entity.WahlParameterExtendedLoader;
import de.ivu.elect.jee.presentation.dynamictable.AbstractColumnBuilder;
import de.ivu.elect.jee.presentation.dynamictable.ColumnEntry;
import de.ivu.elect.jee.presentation.dynamictable.ColumnType;
import de.ivu.elect.presentation.mandant.MandantSessionSelect;
import de.ivu.elect.presentation.table.DefaultColumnEntry;


import static de.ivu.elect.business.wahl.entity.WahlParameterWerte.PREASENTATIION_STATUS;
import static de.ivu.elect.business.wahl.entity.WahlParameterWerte.PREASENTATIION_UPLOAD_SERVICE_STATUS;
import static de.ivu.elect.business.wahl.entity.WahlParameterWerte.PREASENTATIION_UPLOAD_SERVICE_TYP;

@Named(PraesentationStatistikColumnBuilder.COMPONENT_NAME)
public class PraesentationStatistikColumnBuilder extends AbstractColumnBuilder<PraesentationStatistikSearchResult> {

  private static final long serialVersionUID = 1L;

  public static final String COMPONENT_NAME = "praesentationStatistikColumnBuilder";

  @Inject
  private transient MandantSessionSelect mandantSessionSelect;

  @Inject
  private ErgebnisExtendedDAO ergebnisExtendedDAO;

  @Inject
  private transient WahlParameterExtendedLoader wahlParameterExtendedLoader;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> mandantNummerColumn;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> mandantNameColumn;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> wahlDatumColumn;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> wahlNameColumn;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> wahlArtColumn;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> ergebnisArtColumn;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> ergebnisZeitpunktColumn;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> generierungTyp;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> generierungZeitstempel;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> generierungDauer;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> uploadTyp;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> uploadStatus;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> uploadZeitstempel;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> uploadDauer;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> zeitOhneGenerierungNachBerechnungInSekunden;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> zeitOhneUploadNachGenerierungInSekunden;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> letzterErfolgreichZeitstempel;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> letzterFehlerhaftZeitstempel;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> letzterUploadStatus;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> letzterUploadDateienAnzahl;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> letzterUploadDateienSkippedAnzahl;

  private DefaultColumnEntry<PraesentationStatistikSearchResult> letzterUploadDateienGroesse;

  @Inject
  private transient PraesentationUploadStatusStore praesentationUploadStatusStore;

  @Override
  protected void initColumns() {
    if (isNurSystemMandant()) {
      mandantNummerColumn = new DefaultColumnEntry<>("mandantNummerColumn", messages, "praesentation.statistik.search.mandant.nummer", ColumnType.LONG,
          s -> ergebnisExtendedDAO.findByPrimaryKey(s.getErgebnisPK()).getMandant().getNummer());
      mandantNameColumn = new DefaultColumnEntry<>("mandantNameColumn", messages, "praesentation.statistik.search.mandant.name", ColumnType.STRING,
          s -> ergebnisExtendedDAO.findByPrimaryKey(s.getErgebnisPK()).getMandant().getName());
    }
    wahlDatumColumn = new DefaultColumnEntry<>("wahldatumColumn", messages, "praesentation.statistik.search.wahldatum", ColumnType.DATE,
        s -> ergebnisExtendedDAO.findByPrimaryKey(s.getErgebnisPK()).getWahl().getWahldatum());
    wahlArtColumn = new DefaultColumnEntry<>("wahlArtColumn", messages, "praesentation.statistik.search.wahlart", ColumnType.STRING,
        s -> ergebnisExtendedDAO.findByPrimaryKey(s.getErgebnisPK()).getWahl().getWahlart().getName());
    wahlNameColumn = new DefaultColumnEntry<>("wahlNameColumn", messages, "praesentation.statistik.search.wahlname", ColumnType.STRING,
        s -> ergebnisExtendedDAO.findByPrimaryKey(s.getErgebnisPK()).getWahl().getName());
    ergebnisArtColumn = new DefaultColumnEntry<>("ergebnisArtColumn", messages, "praesentation.statistik.search.ergebnisart", ColumnType.STRING,
        s -> messages.lokalisiere(ergebnisExtendedDAO.findByPrimaryKey(s.getErgebnisPK()).getErgebnisart()));

    ergebnisZeitpunktColumn = new DefaultColumnEntry<>("ergebnisZeitpunktColumn", messages, "praesentation.statistik.search.ergebniszeitpunkt",
        ColumnType.TIMESTAMP,
        s -> ergebnisExtendedDAO.findByPrimaryKey(s.getErgebnisPK()).getZeitstempel());

    generierungTyp = new DefaultColumnEntry<>("generierungTyp", messages, "praesentation.statistik.search.generierungTyp", ColumnType.STRING,
        s -> messages.lokalisiere(wahlParameterExtendedLoader.resolveEnumValue(ergebnisExtendedDAO.findByPrimaryKey(s.getErgebnisPK()).getWahl(),
            PREASENTATIION_STATUS,
            PraesentationBereitstellen.class)));

    generierungZeitstempel = new PraesentationStatistikGenerierungZeitstempelColumn("generierungZeitstempel", messages,
        "praesentation.statistik.search.generierungZeitstempel",
        ColumnType.TIMESTAMP,
        s -> findeLetztenEintrag(s.getStatistikEntries()).getZeitpunkt());

    generierungZeitstempel.setTooltipFct(s -> {
      if (findeLetztenEintrag(s.getStatistikEntries()).isError()) {
        return getMessage("praesentation.statistik.search.generierungError");
      }
      return "";
    });

    generierungDauer = new DefaultColumnEntry<>("generierungDauer", messages, "praesentation.statistik.search.generierungDauer",
        ColumnType.LONG,
        s -> findeLetztenEintrag(s.getStatistikEntries()).getDauerInMs());

    uploadTyp = new DefaultColumnEntry<>("uploadTyp", messages, "praesentation.statistik.search.uploadTyp", ColumnType.STRING,
        s -> messages.lokalisiere(wahlParameterExtendedLoader.resolveEnumValue(ergebnisExtendedDAO.findByPrimaryKey(s.getErgebnisPK()).getWahl(),
            PREASENTATIION_UPLOAD_SERVICE_TYP,
            PraesentationUploadServiceTyp.class)));

    uploadStatus = new DefaultColumnEntry<>("uploadStatus", messages, "praesentation.statistik.search.uploadStatus", ColumnType.STRING,
        s -> messages.lokalisiere(wahlParameterExtendedLoader.resolveEnumValue(ergebnisExtendedDAO.findByPrimaryKey(s.getErgebnisPK()).getWahl(),
            PREASENTATIION_UPLOAD_SERVICE_STATUS,
            PraesentationUploadServiceStatus.class)));

    zeitOhneUploadNachGenerierungInSekunden = new DefaultColumnEntry<>("zeitOhneUploadNachGenerierungInSekunden", messages,
        "praesentation.statistik.search.zeitOhneUploadNachGenerierungInSekunden",
        ColumnType.LONG,
        s -> {
          if (s == null) {
            return null;
          }

          var praesentationStatistikEntry = findeLetztenEintrag(s.getStatistikEntries());

          var generierung = praesentationStatistikEntry.getZeitpunkt();
          if (generierung == null) {
            return null;
          }

          var upload = bestimmeLetzteErfolgreich(s);
          if (upload == null) {
            return Long.MAX_VALUE;
          }

          if (upload.after(generierung)) {
            return 0L;
          }

          return (System.currentTimeMillis() - upload.getTime()) / 1000L;
        }
    );
    zeitOhneGenerierungNachBerechnungInSekunden = new DefaultColumnEntry<>("zeitOhneGenerierungNachBerechnungInSekunden", messages,
        "praesentation.statistik.search.zeitOhneGenerierungNachBerechnungInSekunden",
        ColumnType.LONG,
        s -> {
          if (s == null) {
            return null;
          }

          var zeitstempelErgebnis = ergebnisExtendedDAO.findByPrimaryKey(s.getErgebnisPK()).getZeitstempel();
          if (zeitstempelErgebnis == null) {
            return null;
          }

          var praesentationStatistikEntry = findeLetztenEintrag(s.getStatistikEntries());

          var generierung = praesentationStatistikEntry.getZeitpunkt();
          if (generierung == null) {
            return Long.MAX_VALUE;
          }

          if (generierung.after(zeitstempelErgebnis)) {
            return 0L;
          }

          return (System.currentTimeMillis() - generierung.getTime()) / 1000L;
        }
    );

    uploadZeitstempel = new PraesentationStatistikUploadZeitstempelColumn("uploadZeitstempel", messages,
        "praesentation.statistik.search.uploadZeitstempel", ColumnType.TIMESTAMP,
        s -> bestimmeLetztenEintrag(s).getStart(), this::bestimmeLetztenEintrag);

    uploadZeitstempel.setTooltipFct(s -> {
      if (bestimmeLetztenEintrag(s).isError()) {
        return getMessage("praesentation.statistik.search.uploadError");
      }
      return "";
    });

    uploadDauer = new DefaultColumnEntry<>("uploadDauer", messages, "praesentation.statistik.search.uploadDauer", ColumnType.LONG,
        s -> bestimmeLetztenEintrag(s).getDauerInMs());

    letzterErfolgreichZeitstempel = new DefaultColumnEntry<>("letzterErfolgreichZeitstempel", messages, "praesentation.statistik.search.letzter.erfolgreich",
        ColumnType.TIMESTAMP, this::bestimmeLetzteErfolgreich);
    letzterFehlerhaftZeitstempel = new DefaultColumnEntry<>("letzterFehlerhaftZeitstempel", messages, "praesentation.statistik.search.letzter.fehlerhaft",
        ColumnType.TIMESTAMP, this::bestimmeLetzteFehlerhaft);

    letzterUploadDateienAnzahl = new DefaultColumnEntry<>("letzterUploadDateienAnzahl", messages, "praesentation.statistik.search.letzterUploadDateienAnzahl",
        ColumnType.LONG, s -> bestimmeLetztenEintrag(s).getAnzahlDateien());
    letzterUploadDateienGroesse = new DefaultColumnEntry<>("letzterUploadDateienGroesse", messages,
        "praesentation.statistik.search.letzterUploadDateienGroesse",
        ColumnType.LONG, s -> bestimmeLetztenEintrag(s).getBytes());

    letzterUploadDateienSkippedAnzahl = new DefaultColumnEntry<>("letzterUploadDateienSkippedAnzahl", messages,
        "praesentation.statistik.search.letzterUploadDateienSkippedAnzahl",
        ColumnType.LONG, s -> bestimmeLetztenEintrag(s).getAnzahlDateienSkipped());

    letzterUploadStatus = new DefaultColumnEntry<>("letzterUploadStatus", messages,
        "praesentation.statistik.search.letzterUploadStatus",
        ColumnType.STRING, s -> messages.getMessageForMessageKey("praesentation.statistik.search.letzterUploadStatus." + bestimmeLetztenEintrag(s).isError()));

  }

  private PraesentationUploadStatusEntry bestimmeLetztenEintrag(PraesentationStatistikSearchResult statistikSearchResult) {
    if (statistikSearchResult == null) {
      return PraesentationUploadStatusEntry.EMPTY;
    }

    var entries = praesentationUploadStatusStore.getEntries(statistikSearchResult.getErgebnisPK());
    if (entries.isEmpty()) {
      return PraesentationUploadStatusEntry.EMPTY;
    }

    return entries.get(entries.size() - 1);
  }

  @Nullable
  private Date bestimmeLetzteFehlerhaft(PraesentationStatistikSearchResult s) {

    if (s != null && s.getErgebnisPK() != null) {
      return praesentationUploadStatusStore.getEntries(s.getErgebnisPK()).stream()
          .filter(PraesentationUploadStatusEntry::isError)
          .map(PraesentationUploadStatusEntry::getEnd)
          .filter(Objects::nonNull)
          .max(Date::compareTo)
          .orElse(null);
    }

    return null;
  }

  @Nullable
  private Date bestimmeLetzteErfolgreich(PraesentationStatistikSearchResult s) {
    if (s != null && s.getErgebnisPK() != null) {
      return praesentationUploadStatusStore.getEntries(s.getErgebnisPK()).stream()
          .filter(p -> !p.isError())
          .map(PraesentationUploadStatusEntry::getEnd)
          .filter(Objects::nonNull)
          .max(Date::compareTo)
          .orElse(null);
    }

    return null;
  }

  protected static PraesentationStatistikEntry findeLetztenEintrag(List<PraesentationStatistikEntry> statistikEntries) {
    return statistikEntries.stream().max(Comparator.comparing(PraesentationStatistikEntry::getZeitpunkt))
        .orElse(new PraesentationStatistikEntry(null, 0, 0, 0, 0, 0, 0, 0, false));
  }

  @Override
  protected List<ColumnEntry<? super PraesentationStatistikSearchResult>> getColumnEntriesSorted() {
    List<ColumnEntry<? super PraesentationStatistikSearchResult>> result = new ArrayList<>();
    if (isNurSystemMandant()) {
      result.add(mandantNummerColumn);
      result.add(mandantNameColumn);
    }
    result.add(wahlDatumColumn);
    result.add(wahlArtColumn);
    result.add(wahlNameColumn);
    result.add(ergebnisArtColumn);
    result.add(ergebnisZeitpunktColumn);
    result.add(generierungTyp);
    result.add(zeitOhneGenerierungNachBerechnungInSekunden);
    result.add(generierungZeitstempel);
    result.add(generierungDauer);
    result.add(uploadTyp);
    result.add(uploadStatus);
    result.add(zeitOhneUploadNachGenerierungInSekunden);
    result.add(letzterUploadStatus);
    result.add(uploadZeitstempel);
    result.add(uploadDauer);
    result.add(letzterUploadDateienAnzahl);
    result.add(letzterUploadDateienGroesse);
    result.add(letzterUploadDateienSkippedAnzahl);
    result.add(letzterErfolgreichZeitstempel);
    result.add(letzterFehlerhaftZeitstempel);

    return result;
  }

  private boolean isNurSystemMandant() {
    return getMandant().getMandant().getArt().isSystemMandant() && !getMandant().getMandant().getArt().isCustomerMandant();
  }

  private Mandant getMandant() {
    return mandantSessionSelect.getMandant();
  }

}
