package de.ivu.elect.presentation.wahllokalsystem;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

import org.primefaces.model.timeline.TimelineEvent;
import org.primefaces.model.timeline.TimelineGroup;
import org.primefaces.model.timeline.TimelineModel;

import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.stimmeingabe.boundary.LetzterEingangLoader;
import de.ivu.elect.business.wahl.boundary.WahlExtendedDAO;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahllokalsystem.boundary.WahllokalsystemStatusExtendedDAO;
import de.ivu.elect.business.wahllokalsystem.entity.WahllokalsystemStatus;
import de.ivu.elect.business.wahllokalsystem.entity.WahllokalsystemStatusConstants;
import de.ivu.elect.business.wahllokalsystem.entity.WahllokalsystemStatusDokumentEventType;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.ReadOnlyStringProperty;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.gebietsbaum.GebietNummerFormatter;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;
import de.ivu.elect.presentation.wahllokalsystem.form.WahllokalsystemStatusViewFormBuilder;
import de.ivu.elect.presentation.wahllokalsystem.table.WahllokalsystemStatusDokumenteColumn;

import static de.ivu.elect.business.DateUtil.asDate;
import static de.ivu.elect.business.DateUtil.asLocalDateTime;

@RequestScoped
@Named(WahllokalsystemStatusAnzeigenView.COMPONENT_NAME)
@View(viewId = WahllokalsystemStatusAnzeigenView.VIEW_ID, useCase = UseCasePermission.WAHLLOKALSYSTEM_STATUS_SEARCH)
@OnlineHilfeId(value = "761", mehrfachVergebenOk = true)
public class WahllokalsystemStatusAnzeigenView extends AbstractWahllokalsystemStatusEditView {

  public static final String COMPONENT_NAME = "wahllokalsystemStatusAnzeigenView";

  public static final String VIEW_ID = "/wahllokalsystem/wahllokalsystem-status-anzeigen.xhtml";

  private static final long serialVersionUID = 1L;

  public static final int ABSTAND_IN_MINUTEN = 30;

  @Inject
  private transient GebietNummerFormatter gebietNummerFormatter;

  @Inject
  private transient WahllokalsystemStatusExtendedDAO wahllokalsystemStatusExtendedDAO;

  @Inject
  private transient GebietExtendedDAO gebietExtendedDAO;

  @Inject
  protected transient WahlExtendedDAO wahlExtendedDAO;

  @Inject
  private transient WahllokalsystemStatusViewFormBuilder viewFormBuilder;

  @Inject
  private transient LetzterEingangLoader letzterEingangLoader;

  private transient TimelineModel<String, String> model;

  @Override
  protected void addProperties(FormBuilder formBuilder, WahllokalsystemStatus entity) {
    viewFormBuilder.addAusstattungProperties(formBuilder, entity, true);
    viewFormBuilder.addMeldungProperties(formBuilder, entity, true);
    viewFormBuilder.addWaehlerProperties(formBuilder, entity, true);

    formBuilder.addSeparator("wahllokalsystem.status.section.dokumente");
    Wahl wahl = getSelectedWahl();
    addWahlDokumenteProperty(formBuilder, wahl);

    for (Wahl weitereWahl : getEntity().getWahl().getWeitereWahlenAmWahltermin()) {
      addWahlDokumenteProperty(formBuilder, weitereWahl);
    }

  }

  private void addWahlDokumenteProperty(FormBuilder formBuilder, Wahl wahl) {
    WahllokalsystemStatusDokumenteColumn wahllokalsystemStatusDokumenteColumn = createStatusDokumenteColumn(wahl);
    formBuilder.addProperty(
        new ReadOnlyStringProperty(wahllokalsystemStatusDokumenteColumn.getHeader(), asString(wahllokalsystemStatusDokumenteColumn.getValue(getEntity()))));
    formBuilder.withTooltip(wahllokalsystemStatusDokumenteColumn.getTooltip(getEntity()));
  }

  private String asString(Object value) {
    if (value == null) {
      return null;
    }
    return value.toString();
  }

  private WahllokalsystemStatusDokumenteColumn createStatusDokumenteColumn(Wahl wahl) {
    return new WahllokalsystemStatusDokumenteColumn(messages, userLocale, wahl, wahllokalsystemStatusExtendedDAO, gebietExtendedDAO, wahlExtendedDAO, letzterEingangLoader);
  }

  @Override
  protected boolean isGebietBearbeitenRechtPruefen() {
    return true;
  }

  public String getGebietName() {
    return gebietNummerFormatter.getNameMitNummer(getGebiet());
  }

  protected Gebiet getGebiet() {
    return getEntity().getGebiet();
  }

  @Override
  public boolean isBtnSaveRendered() {
    return false;
  }

  @Override
  public boolean isBtnResetRendered() {
    return false;
  }

  @Override
  public String getViewId() {
    return VIEW_ID;
  }

  public TimelineModel<String, String> getTimeLineModel() {
    if (model == null) {
      model = createTimeLineModel();
    }
    return model;
  }

  private TimelineModel<String, String> createTimeLineModel() {
    TimelineModel<String, String> result = new TimelineModel();

    result.addGroup(new TimelineGroup("meldungen", getMessage("wahllokalsystem.status.section.meldung")));
    result.addGroup(new TimelineGroup("waehler", getMessage("wahllokalsystem.status.section.waehler")));

    Wahl wahl = getSelectedWahl();
    addTimeLineGroupWahl(result, wahl);
    for (Wahl weitereWahl : wahl.getWeitereWahlenAmWahltermin()) {
      addTimeLineGroupWahl(result, weitereWahl);
    }
    Collections.reverse(result.getGroups());

    WahllokalsystemStatus wahllokalsystemStatus = getEntity();
    LocalDateTime letzteMeldung = asLocalDateTime(wahllokalsystemStatus.getLetzteMeldung());
    if (letzteMeldung != null) {
      TimelineEvent<String> timelineEvent = new TimelineEvent<>();
      timelineEvent.setData(getMessage(WahllokalsystemStatusConstants.MESSAGE_KEY_LETZTE_MELDUNG_EINZAHL));
      timelineEvent.setStartDate(letzteMeldung);
      timelineEvent.setEditable(false);
      timelineEvent.setGroup("meldungen");
      result.add(timelineEvent);
    }
    LocalDateTime letzteAbmeldung = asLocalDateTime(wahllokalsystemStatus.getLetzteAbmeldung());
    if (letzteAbmeldung != null) {
      TimelineEvent<String> timelineEvent = new TimelineEvent<>();
      timelineEvent.setData(getMessage(WahllokalsystemStatusConstants.MESSAGE_KEY_LETZTE_ABMELDUNG_EINZAHL));
      timelineEvent.setStartDate(letzteAbmeldung);
      timelineEvent.setEditable(false);
      timelineEvent.setGroup("meldungen");
      result.add(timelineEvent);
    }

    LocalDateTime letzteWaehler = asLocalDateTime(wahllokalsystemStatus.getAnzahlWaehlerZeitpunkt());
    if (letzteWaehler != null) {
      TimelineEvent<String> timelineEvent = new TimelineEvent<>();
      timelineEvent.setData(getMessage(WahllokalsystemStatusConstants.MESSAGE_KEY_ANZAHL_WAEHLER_EINZAHL));
      timelineEvent.setStartDate(letzteWaehler);
      timelineEvent.setEditable(false);
      timelineEvent.setGroup("waehler");
      result.add(timelineEvent);
    }

    addTimelineEventsWahl(result, wahllokalsystemStatus, wahl);

    for (Wahl weitereWahl : wahl.getWeitereWahlenAmWahltermin()) {
      addTimelineEventsWahl(result, wahllokalsystemStatus, weitereWahl);
    }

    return result;
  }

  private void addTimeLineGroupWahl(TimelineModel<String, String> result, Wahl wahl) {
    WahllokalsystemStatusDokumenteColumn wahllokalsystemStatusDokumenteColumn = createStatusDokumenteColumn(wahl);
    result.addGroup(new TimelineGroup(getGroupIdWahl(wahl), wahllokalsystemStatusDokumenteColumn.getHeader()));
  }

  private String getGroupIdWahl(Wahl wahl) {
    return "wahl" + wahl.getId();
  }

  private void addTimelineEventsWahl(TimelineModel<String, String> result, WahllokalsystemStatus wahllokalsystemStatus, Wahl wahl) {
    WahllokalsystemStatus status = wahllokalsystemStatus.getWahllokalsystemStatusAndereWahl(wahl, wahllokalsystemStatusExtendedDAO, gebietExtendedDAO);
    if (status!=null) {
      Map<Supplier<Date>, WahllokalsystemStatusDokumentEventType> zeitpunktDokumente = status.getZeitpunktDokumente(letzterEingangLoader);
      for (Map.Entry<Supplier<Date>, WahllokalsystemStatusDokumentEventType> entry : zeitpunktDokumente.entrySet()) {
        Date date = entry.getKey().get();
        if (date != null) {
          TimelineEvent<String> timelineEvent = new TimelineEvent<>();
          timelineEvent.setData(getMessage(entry.getValue()));
          timelineEvent.setStartDate(asLocalDateTime(date));
          timelineEvent.setEditable(false);
          timelineEvent.setGroup(getGroupIdWahl(wahl));
          result.add(timelineEvent);
        }
      }
    }
  }

  public LocalDateTime getTimeLineStart() {
    Date start = getTimeLineStartOhneAnpassung();
    Date end = getTimeLineEndOhneAnpassung();
    long diff = end.getTime() - start.getTime();
    return asLocalDateTime(new Date(start.getTime() - diff / 16));
  }

  public LocalDateTime getTimeLineEnd() {
    Date start = getTimeLineStartOhneAnpassung();
    Date end = getTimeLineEndOhneAnpassung();
    long diff = end.getTime() - start.getTime();
    return asLocalDateTime(new Date(end.getTime() + diff / 16));
  }

  private Date getTimeLineStartOhneAnpassung() {
    LocalDateTime start = LocalDateTime.now();
    for (TimelineEvent<String> event : getTimeLineModel().getEvents()) {
      if (event.getStartDate().isBefore(start)) {
        start = event.getStartDate();
      }
    }

    return new Date(asDate(start).getTime() - TimeUnit.MINUTES.toMillis(ABSTAND_IN_MINUTEN));
  }

  private Date getTimeLineEndOhneAnpassung() {
    LocalDateTime end = LocalDateTime.now();
    for (TimelineEvent<String> event : getTimeLineModel().getEvents()) {
      if (event.getStartDate().isAfter(end)) {
        end = event.getStartDate();
      }
    }

    return new Date(asDate(end).getTime() + TimeUnit.MINUTES.toMillis(ABSTAND_IN_MINUTEN));
  }

  public long getZoomMax() {
    return (asDate(getTimeLineEnd()).getTime() - asDate(getTimeLineStart()).getTime()) * 2;
  }

}
