
package de.ivu.elect.presentation.entschaedigung.form;

import static java.util.stream.Collectors.groupingBy;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.inject.Inject;

import de.ivu.elect.business.entschaedigung.boundary.EntschaedigungExtendedDAO;
import de.ivu.elect.business.entschaedigung.control.EntschaedigungDefaultComparator;
import de.ivu.elect.business.entschaedigung.entity.Auszahlung;
import de.ivu.elect.business.entschaedigung.entity.Entschaedigung;
import de.ivu.elect.business.entschaedigung.entity.EntschaedigungAnzahlBooleanProperty;
import de.ivu.elect.business.entschaedigung.entity.EntschaedigungAnzahlLongProperty;
import de.ivu.elect.business.entschaedigung.entity.EntschaedigungConstants;
import de.ivu.elect.business.entschaedigung.entity.EntschaedigungEinzelbetragMoneyProperty;
import de.ivu.elect.business.entschaedigung.entity.EntschaedigungStatus;
import de.ivu.elect.business.entschaedigung.entity.Entschaedigungsart;
import de.ivu.elect.business.entschaedigung.entity.EntschaedigungsartConstants;
import de.ivu.elect.business.person.entity.Personendaten;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.Property;
import de.ivu.elect.jee.presentation.dynamicform.ReadOnlyStringProperty;
import de.ivu.elect.presentation.ViewFormBuilder;

public class EntschaedigungViewFormBuilder extends ViewFormBuilder {

  private static final long serialVersionUID = 1L;

  private static final Comparator<Entschaedigung> DEFAULT_COMPARATOR = new EntschaedigungDefaultComparator();

  @Inject
  private transient EntschaedigungExtendedDAO entschaedigungDAO;

  @Inject
  private transient AuszahlungViewFormBuilder auszahlungViewFormBuilder;

  public EntschaedigungViewFormBuilder() {
    super();
  }

  public void addPropertiesSeparator(FormBuilder formBuilder) {
    formBuilder.addSeparator(EntschaedigungConstants.MESSAGE_KEY_ENTITY_MEHRZAHL);
  }

  public void addProperties(FormBuilder formBuilder, Personendaten personendaten, List<Entschaedigung> entschaedigungenZurManuellenBearbeitung, boolean readonly) {
    addProperties(formBuilder, personendaten, entschaedigungenZurManuellenBearbeitung, collectEntschaedigungenAusgezahlt(personendaten), collectEntschaedigungenPerAuszahlung(personendaten), readonly);
  }

  private void addProperties(FormBuilder formBuilder, Personendaten personendaten, List<Entschaedigung> entschaedigungenZurManuellenBearbeitung, List<Entschaedigung> entschaedigungenAusgezahlt,
      Map<Auszahlung, List<Entschaedigung>> entschaedigungenPerAuszahlung, boolean readonly) {
    boolean existsEntschaedigungZurManuellenBearbeitung = !entschaedigungenZurManuellenBearbeitung.isEmpty();
    boolean existsEntschaedigungAusgezahlt = !entschaedigungenAusgezahlt.isEmpty();
    boolean existsEntschaedigungInAuszahlung = !entschaedigungenPerAuszahlung.isEmpty();

    if (existsEntschaedigungZurManuellenBearbeitung) {
      Collections.sort(entschaedigungenZurManuellenBearbeitung, DEFAULT_COMPARATOR);

      for (Iterator<Entschaedigung> iter = entschaedigungenZurManuellenBearbeitung.iterator(); iter.hasNext();) {
        addEntschaedigungZurManuellenBearbeitungProperties(formBuilder, personendaten, iter.next(), readonly);

        if (iter.hasNext()) {
          formBuilder.addSeparator();
        }
      }
    } else {
      String msgKey = "personendaten.entschaedigung.edit.message.manuell.leer.weitere.leer";

      if (existsEntschaedigungAusgezahlt || existsEntschaedigungInAuszahlung) {
        msgKey = "personendaten.entschaedigung.edit.message.manuell.leer.weitere.vorhanden";
      }

      formBuilder.addProperty(new ReadOnlyStringProperty("", getMessage(msgKey)));
    }

    if (existsEntschaedigungAusgezahlt) {
      formBuilder.addSeparator("personendaten.entschaedigung.edit.separator.weitere.ausgezahlt");

      Collections.sort(entschaedigungenAusgezahlt, DEFAULT_COMPARATOR);

      for (Iterator<Entschaedigung> iter = entschaedigungenAusgezahlt.iterator(); iter.hasNext();) {
        addEntschaedigungAusgezahltProperties(formBuilder, personendaten, iter.next());

        if (iter.hasNext()) {
          formBuilder.addSeparator();
        }
      }
    }

    if (existsEntschaedigungInAuszahlung) {
      formBuilder.addSeparator("personendaten.entschaedigung.edit.separator.weitere.auszahlungen");

      SortedMap<Auszahlung, List<Entschaedigung>> entschaedigungenPerAuszahlungSorted = new TreeMap<>(AuszahlungViewFormBuilder.AUSZAHLUNG_DEFAULT_COMPARATOR);
      entschaedigungenPerAuszahlungSorted.putAll(entschaedigungenPerAuszahlung);

      for (Iterator<List<Entschaedigung>> iterEntschaedigungenPerAuszahlung = entschaedigungenPerAuszahlungSorted.values().iterator(); iterEntschaedigungenPerAuszahlung.hasNext();) {
        List<Entschaedigung> entschaedigungen = new ArrayList<>(iterEntschaedigungenPerAuszahlung.next());
        Collections.sort(entschaedigungen, DEFAULT_COMPARATOR);

        for (Iterator<Entschaedigung> iterEntschaedigungen = entschaedigungen.iterator(); iterEntschaedigungen.hasNext();) {
          Entschaedigung entschaedigungInAuszahlung = iterEntschaedigungen.next();

          addEntschaedigungInAuszahlungProperties(formBuilder, personendaten, entschaedigungInAuszahlung.getAuszahlung(), entschaedigungInAuszahlung);

          if (iterEntschaedigungen.hasNext()) {
            formBuilder.addSeparator();
          }
        }

        if (!entschaedigungen.isEmpty() && iterEntschaedigungenPerAuszahlung.hasNext()) {
          formBuilder.addSeparator();
        }
      }
    }
  }

  private void addEntschaedigungZurManuellenBearbeitungProperties(FormBuilder formBuilder, Personendaten personendaten, Entschaedigung entschaedigung, boolean readonly) {
    addDefaultEntschaedigungProperties(formBuilder, personendaten, entschaedigung, readonly || !entschaedigung.isBearbeitbar());
  }

  private void addEntschaedigungAusgezahltProperties(FormBuilder formBuilder, Personendaten personendaten, Entschaedigung entschaedigung) {
    addDefaultEntschaedigungProperties(formBuilder, personendaten, entschaedigung, true);
  }

  private void addEntschaedigungInAuszahlungProperties(FormBuilder formBuilder, Personendaten personendaten, Auszahlung auszahlung, Entschaedigung entschaedigung) {
    addDefaultEntschaedigungProperties(formBuilder, personendaten, entschaedigung, true);

    auszahlungViewFormBuilder.addAuszahlungProperties(formBuilder, auszahlung, entschaedigung);
  }

  private void addDefaultEntschaedigungProperties(FormBuilder formBuilder, Personendaten personendaten, Entschaedigung entschaedigung, boolean readonly) {
    Entschaedigungsart entschaedigungsart = entschaedigung.getEntschaedigungsart();

    formBuilder.addProperty(new ReadOnlyStringProperty(getMessage(EntschaedigungsartConstants.MESSAGE_KEY_ENTITY_EINZAHL), entschaedigungsart.getErklaerung()));
    formBuilder.addProperty(new EntschaedigungEinzelbetragMoneyProperty(entschaedigung)).withReadonly(!entschaedigungsart.isEinzelbetragVariabel() || readonly);

    Property anzahlProperty = new EntschaedigungAnzahlLongProperty(entschaedigung);

    if (entschaedigungsart.usesAnzahlMaximal(1L)) {
      anzahlProperty = new EntschaedigungAnzahlBooleanProperty(entschaedigung);
    }

    formBuilder.addProperty(anzahlProperty).withReadonly(!entschaedigungsart.isAnzahlVariabel() || readonly);

    // ELECT-6871, ELECT-8865, initialer Status und Status in einer Auszahlung irrelevant.
    if (!entschaedigung.hasAuszahlung() && !EntschaedigungStatus.VORGESEHEN.equals(entschaedigung.getStatus())) {
      formBuilder.addProperty(entschaedigung.getProperties().getStatusProperty()).readonly();
    }
  }

  private List<Entschaedigung> collectEntschaedigungenAusgezahlt(Personendaten personendaten) {
    return entschaedigungDAO.findByErstelltFuerKonkretePersondendatenAndEntschaedigungStatus(personendaten, EntschaedigungStatus.valuesAusgezahlt());
  }

  private Map<Auszahlung, List<Entschaedigung>> collectEntschaedigungenPerAuszahlung(Personendaten personendaten) {
    return entschaedigungDAO.findByErstelltFuerBeliebigeAuszahlungAndPersondendaten(personendaten).stream() //
        .collect(groupingBy(Entschaedigung::getAuszahlung));
  }
}
