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

import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.stream.Collectors.toMap;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import de.ivu.elect.business.exchange.control.AbstractMandantExchangeContext;
import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietsartDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.system.entity.SystemGebietsebene;
import de.ivu.elect.business.wahl.boundary.AbstractWahlartDAO;

public class WahlImportContext extends AbstractMandantExchangeContext {

  private static final long serialVersionUID = 1L;

  private WahlPK wahlPK;

  private Date neuesDatum;

  private String neueNummer;

  private final SystemGebietsebene systemGebietsebene;

  private final List<Wahlart> systemWahlarten;

  private final Map<Wahlart, List<Gebietsart>> systemGebietsartenPerWahlart;

  private final Map<Wahlart, List<Gebietsart>> systemGebietsartenWahlgebietPerWahlart;

  private final Map<Wahlart, List<Gebietsart>> systemGebietsartenListenwahlgebietPerWahlart;

  private final Map<Wahlart, List<Gebietsart>> systemGebietsartenDirektwahlgebietPerWahlart;

  public WahlImportContext(Mandant mandant) {
    this(mandant, SystemGebietsebene.UNBEKANNT, emptyList(), emptyMap(), emptyMap(), emptyMap(), emptyMap());
  }

  public WahlImportContext(Mandant mandant, SystemGebietsebene systemGebietsebene, AbstractWahlartDAO wahlartDAO, AbstractGebietsartDAO gebietsartDAO) {
    this(mandant, systemGebietsebene, collectSystemWahlarten(wahlartDAO, mandant, systemGebietsebene), gebietsartDAO);
  }

  private WahlImportContext(Mandant mandant, SystemGebietsebene systemGebietsebene, List<Wahlart> systemWahlarten, AbstractGebietsartDAO gebietsartDAO) {
    this(mandant, systemGebietsebene, systemWahlarten, collectSystemGebietsartenPerWahlart(gebietsartDAO, systemWahlarten),
        collectSystemGebietsartenWahlgebietPerWahlart(gebietsartDAO, systemWahlarten), collectSystemGebietsartenListenwahlgebietPerWahlart(gebietsartDAO, systemWahlarten),
        collectSystemGebietsartenDirektwahlgebietPerWahlart(gebietsartDAO, systemWahlarten));
  }

  private WahlImportContext(Mandant mandant, SystemGebietsebene systemGebietsebene, List<Wahlart> systemWahlarten, Map<Wahlart, List<Gebietsart>> systemGebietsartenPerWahlart,
      Map<Wahlart, List<Gebietsart>> systemGebietsartenWahlgebietPerWahlart, Map<Wahlart, List<Gebietsart>> systemGebietsartenListenwahlgebietPerWahlart,
      Map<Wahlart, List<Gebietsart>> systemGebietsartenDirektwahlgebietPerWahlart) {
    super(mandant);

    this.systemGebietsebene = systemGebietsebene;

    this.systemWahlarten = systemWahlarten;

    this.systemGebietsartenPerWahlart = systemGebietsartenPerWahlart;
    this.systemGebietsartenWahlgebietPerWahlart = systemGebietsartenWahlgebietPerWahlart;
    this.systemGebietsartenListenwahlgebietPerWahlart = systemGebietsartenListenwahlgebietPerWahlart;
    this.systemGebietsartenDirektwahlgebietPerWahlart = systemGebietsartenDirektwahlgebietPerWahlart;
  }

  public WahlPK getWahlPK() {
    return wahlPK;
  }

  public void setWahlPK(WahlPK wahlPK) {
    this.wahlPK = wahlPK;
  }

  public Date getNeuesDatum() {
    return neuesDatum;
  }

  public void setNeuesDatum(Date neuesDatum) {
    this.neuesDatum = neuesDatum;
  }

  public String getNeueNummer() {
    return neueNummer;
  }

  public void setNeueNummer(String neueNummer) {
    this.neueNummer = neueNummer;
  }

  public SystemGebietsebene getSystemGebietsebene() {
    return systemGebietsebene;
  }

  public List<Wahlart> getSystemWahlarten() {
    return systemWahlarten;
  }

  public List<Gebietsart> collectGebietsarten(Wahlart wahlart) {
    return getGebietsartenOrDefault(wahlart, systemGebietsartenPerWahlart);
  }

  public List<Gebietsart> collectGebietsartenWahlgebiet(Wahlart wahlart) {
    return getGebietsartenOrDefault(wahlart, systemGebietsartenWahlgebietPerWahlart);
  }

  public List<Gebietsart> collectGebietsartenListenwahlgebiet(Wahlart wahlart) {
    return getGebietsartenOrDefault(wahlart, systemGebietsartenListenwahlgebietPerWahlart);
  }

  public List<Gebietsart> collectGebietsartenDirektwahlgebiet(Wahlart wahlart) {
    return getGebietsartenOrDefault(wahlart, systemGebietsartenDirektwahlgebietPerWahlart);
  }

  private static List<Gebietsart> getGebietsartenOrDefault(Wahlart wahlart, Map<Wahlart, List<Gebietsart>> source) {
    if (wahlart != null) {
      return source.getOrDefault(wahlart, new ArrayList<>());
    }

    return new ArrayList<>();
  }

  private static List<Wahlart> collectSystemWahlarten(AbstractWahlartDAO wahlartDAO, Mandant mandant, SystemGebietsebene systemGebietsebene) {
    return wahlartDAO.findByAllOrderByName(mandant).stream() //
        .filter(wahlart -> WahlartSystemGebietsebene.akzeptiert(wahlart.getSystemGebietsebenen(), systemGebietsebene)) //
        .collect(Collectors.toList());
  }

  private static Map<Wahlart, List<Gebietsart>> collectSystemGebietsartenPerWahlart(AbstractGebietsartDAO gebietsartDAO, List<Wahlart> wahlarten) {
    return wahlarten.stream() //
        .collect(toMap(Function.identity(), gebietsartDAO::findByWahlart));
  }

  private static Map<Wahlart, List<Gebietsart>> collectSystemGebietsartenWahlgebietPerWahlart(AbstractGebietsartDAO gebietsartDAO, List<Wahlart> wahlarten) {
    return wahlarten.stream() //
        .collect(toMap(Function.identity(), wahlart -> gebietsartDAO.findByWahlartAndWahlgebiet(wahlart, true)));
  }

  private static Map<Wahlart, List<Gebietsart>> collectSystemGebietsartenListenwahlgebietPerWahlart(AbstractGebietsartDAO gebietsartDAO, List<Wahlart> wahlarten) {
    return wahlarten.stream() //
        .collect(toMap(Function.identity(), wahlart -> gebietsartDAO.findByWahlartAndListenwahlgebiet(wahlart, true)));
  }

  private static Map<Wahlart, List<Gebietsart>> collectSystemGebietsartenDirektwahlgebietPerWahlart(AbstractGebietsartDAO gebietsartDAO, List<Wahlart> wahlarten) {
    return wahlarten.stream() //
        .collect(toMap(Function.identity(), wahlart -> gebietsartDAO.findByWahlartAndDirektwahlgebiet(wahlart, true)));
  }
}
