package de.ivu.elect.business.wahllokalsystem.entity;

import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Supplier;

import javax.annotation.Nullable;
import javax.persistence.NoResultException;
import javax.persistence.Transient;

import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.EntityMitGebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietTK;
import de.ivu.elect.business.gebietsbaum.entity.GebietUK;
import de.ivu.elect.business.gebietsbaum.entity.GebietsartUK;
import de.ivu.elect.business.stimmeingabe.boundary.LetzterEingangLoader;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsart;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlPK;
import de.ivu.elect.business.wahllokalsystem.boundary.AbstractWahllokalsystemStatusDAO;

public abstract class AbstractWahllokalsystemStatus implements WahllokalsystemStatusBase, EntityMitGebiet {

  private static final long serialVersionUID = 1L;

  public void reset() {
    setLetzteMeldung(null);
    setLetzteAbmeldung(null);
    setAnzahlWaehler(null);
    setAnzahlWaehlerZeitpunkt(null);
    setNiederschriftDrucken(null);
    setNiederschriftSenden(null);
    setSchnellmeldungDrucken(null);
    setSchnellmeldungSenden(null);
    setNiederschriftEingang(null);
  }

  public Map<Supplier<Date>, WahllokalsystemStatusDokumentEventType> getZeitpunktDokumente(LetzterEingangLoader letzterEingangLoader) {

    Map<Supplier<Date>, WahllokalsystemStatusDokumentEventType> result = new LinkedHashMap<>();

    result.put(this::getSchnellmeldungSenden, WahllokalsystemStatusDokumentEventType.SCHNELLMELDUNG_GESENDET);
    result.put(()->getSchnellmeldungEmpfangen(letzterEingangLoader),  WahllokalsystemStatusDokumentEventType.SCHNELLMELDUNG_EMPFANGEN);
    result.put(this::getSchnellmeldungDrucken,  WahllokalsystemStatusDokumentEventType.SCHNELLMELDUNG_GEDRUCKT);

    result.put(this::getNiederschriftSenden, WahllokalsystemStatusDokumentEventType.NIEDERSCHRIFT_GESENDET);
    result.put(()->getNiederschriftEmpfangen(letzterEingangLoader), WahllokalsystemStatusDokumentEventType.NIEDERSCHRIFT_EMPFANGEN);
    result.put(this::getNiederschriftDrucken, WahllokalsystemStatusDokumentEventType.NIEDERSCHRIFT_GEDRUCKT);
    result.put(this::getNiederschriftEingang, WahllokalsystemStatusDokumentEventType.NIEDERSCHRIFT_EINGEGANGEN);

    return result;
  }

  private Date getSchnellmeldungEmpfangen(LetzterEingangLoader letzterEingangLoader){
    // ELECT-12342 ELECT-12338
    return letzterEingangLoader.bestimmeLetztenEingang(getGebiet().getWahl().getPk(), getGebiet().getPk(), Ergebniseingangsart.VORLAEUFIG).getZeitstempel();
//    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getGebiet().castTo(Wahldurchfuehrungsgebiet.class).getEntity();
//    Ergebniseingang letzterEingangVorlaeufig = wahldurchfuehrungsgebiet.getLetzterEingangVorlaeufig();
//    if (letzterEingangVorlaeufig==null) {
//      return null;
//    }
//    return letzterEingangVorlaeufig.getZeitstempel();
  }

  private Date getNiederschriftEmpfangen(LetzterEingangLoader letzterEingangLoader){
    // ELECT-12342 ELECT-12338
    return letzterEingangLoader.bestimmeLetztenEingang(getGebiet().getWahl().getPk(), getGebiet().getPk(), Ergebniseingangsart.ENDGUELTIG).getZeitstempel();
//    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getGebiet().castTo(Wahldurchfuehrungsgebiet.class).getEntity();
//    Ergebniseingang letzterEingangEndgueltig = wahldurchfuehrungsgebiet.getLetzterEingangEndgueltig();
//    if (letzterEingangEndgueltig==null) {
//      return null;
//    }
//    return letzterEingangEndgueltig.getZeitstempel();
  }

  @Transient
  private final Map<WahlPK, WahllokalsystemStatus> statusAndereWahl = new HashMap<>();

  public WahllokalsystemStatus getWahllokalsystemStatusAndereWahl(Wahl andereWahl, AbstractWahllokalsystemStatusDAO wahllokalsystemStatusDAO, AbstractGebietDAO abstractGebietDAO) {
    if (andereWahl.equals(getWahl())) {
      return (WahllokalsystemStatus) this;
    }
    return statusAndereWahl.computeIfAbsent(andereWahl.getPk(), x -> {
      Gebiet anderesGebiet = getAnderesGebiet(andereWahl, abstractGebietDAO);
      if (anderesGebiet == null) {
        return null;
      }
      try {
        return wahllokalsystemStatusDAO.findByGebiet(anderesGebiet);
      } catch (NoResultException nre) {
        // ignorieren
        return null;
      }

    });
  }

  @Nullable
  public Gebiet getAnderesGebiet(Wahl andereWahl, AbstractGebietDAO abstractGebietDAO) {
    if (andereWahl.equals(getWahl())) {
      return getGebiet();
    }

    GebietTK gebietTK = getGebiet().getGebietTK();
    GebietUK gebietUk = new GebietUK(gebietTK.getNummer(), new GebietsartUK(gebietTK.getGebietsartTK().getName(), andereWahl.getMandant().getMandantUK()), andereWahl.getWahlUK(),
            andereWahl.getWahltermin().getWahlterminUK());

    try {
      return abstractGebietDAO.findByUK(gebietUk);
    } catch (NoResultException nre) {
      return null;
    }

  }

  public WahllokalsystemStatusOnline getWahllokalsystemStatusOnline(Date now, Long timeoutInSeconds) {
    return WahllokalsystemStatusOnline.from(now, getLetzteMeldung(), getLetzteAbmeldung(), timeoutInSeconds);
  }

}
