
package de.ivu.elect.core.exchange.stimmen.csv.dynamic;

import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietFactory;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.gebietsbaum.entity.GebietsartBezirksart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.boundary.GruppeFactory;
import de.ivu.elect.business.gruppe.boundary.PolitischeGruppeFactory;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.gruppe.entity.SystemGruppe;
import de.ivu.elect.business.gruppe.entity.SystemGruppeEigenschaften;
import de.ivu.elect.business.gruppeimgebiet.boundary.GruppeImGebietFactory;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.kandidatur.boundary.ListenkandidaturFactory;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.ReferendumOption;
import de.ivu.elect.was.exchange.DTOHavingErfassungsgebiet;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

public class DynamicStimmeingabeImportCache {

  private final Gebietsart briefwahlbezirksart = createGebietsart(GebietsartBezirksart.BRIEFWAHLBEZIRK);

  private final Gebietsart urnenwahlbezirksart = createGebietsart(GebietsartBezirksart.STIMMBEZIRK);

  private final Map<Gebietsart, Map<String, Wahldurchfuehrungsgebiet>> gebiete = new HashMap<>();

  private final Map<SystemGruppe, Gruppe> systemgruppen = new HashMap<>();

  private final Map<Long, PolitischeGruppe> politischeGruppeMap = new HashMap<>();

  private final Map<Gruppe, Map<Gebiet, GruppeImGebiet>> gruppenImGebiet = new HashMap<>();

  private final Map<PolitischeGruppe, Liste> listen = new HashMap<>();

  private final Map<PolitischeGruppe, Map<Long, ReferendumOption>> referendumOptionen = new HashMap<>();

  private final Map<GruppeImGebiet, Liste> listenMap = new HashMap<>();

  private Wahldurchfuehrungsgebiet oberstesGebiet;

  public DynamicStimmeingabeImportCache() {
    super();
  }

  public Wahldurchfuehrungsgebiet getGebiet(DTOHavingErfassungsgebiet dto, WahldurchfuehrungsgebietFactory wahldurchfuehrungsgebietFactory) {
    Gebietsart gebietsart = getGebietsartAusDto(dto, this::getGebietsart);

    return gebiete.computeIfAbsent(gebietsart, x -> new HashMap<>()).computeIfAbsent(dto.getErfassungsgebietNummer(), x -> {
      Wahldurchfuehrungsgebiet result = wahldurchfuehrungsgebietFactory.create();
      result.setGebietsart(gebietsart);
      result.setNummer(dto.getErfassungsgebietNummer());

      result.setName(dto.getErfassungsgebietName());

      result.setSonderstimmbezirk(Boolean.TRUE.equals(dto.getSonderwahlbezirk()));
      result.setAuswahlbezirk(Boolean.TRUE.equals(dto.getAuswahlbezirk()));
      result.setBeweglicherWahlvorstand(Boolean.TRUE.equals(dto.getBeweglicherwahlvorstand()));

      return result;
    });
  }

  public Listenkandidatur getOrCreateListenkandidatur(Wahldurchfuehrungsgebiet gebiet, PolitischeGruppe politischeGruppe, int listenkandidaturPos, GruppeImGebietFactory gruppeImGebietFactory,
      ListenkandidaturFactory listenkandidaturFactory) {
    GruppeImGebiet gruppeImGebiet = getGruppeImGebiet(politischeGruppe, gebiet, gruppeImGebietFactory);

    Liste liste = listenMap.computeIfAbsent(gruppeImGebiet, x -> {
      Liste result = new Liste();
      result.setGruppeImGebiet(gruppeImGebiet);

      return result;
    });

    for (Listenkandidatur listenkandidatur : liste.getListenkandidaturen()) {
      if (listenkandidatur.getRelativePosition() == listenkandidaturPos) {
        return listenkandidatur;
      }
    }

    Listenkandidatur listenkandidatur = listenkandidaturFactory.create();
    listenkandidatur.setRelativePosition((long) listenkandidaturPos);
    listenkandidatur.setEingereichtePosition((long) listenkandidaturPos);
    listenkandidatur.setVorgegebenePosition((long) listenkandidaturPos);

    listenkandidatur.setListe(liste);
    return listenkandidatur;

  }

  public static Gebietsart getGebietsartAusDto(DTOHavingErfassungsgebiet dto, Function<Boolean, Gebietsart> gebietsartSupplier) {
    return gebietsartSupplier.apply(dto.isBriefwahlgebiet());
  }

  public Gebietsart getGebietsart(boolean briefwahl) {
    if (briefwahl) {
      return briefwahlbezirksart;
    }

    return urnenwahlbezirksart;
  }

  public Gruppe getSystemGruppe(SystemGruppe systemGruppe, GruppeFactory gruppeFactory, SystemGruppeEigenschaften systemGruppeEigenschaften) {
    return systemgruppen.computeIfAbsent(systemGruppe, x -> {
      Gruppe gruppe = gruppeFactory.create();
      gruppe.setName(systemGruppeEigenschaften.getName(systemGruppe));
      gruppe.setKurzname(systemGruppeEigenschaften.getKurzname(systemGruppe));
      gruppe.setSchluessel(systemGruppeEigenschaften.getSchluessel(systemGruppe));
      gruppe.setRelativePosition(systemGruppeEigenschaften.getPosition(systemGruppe));
      gruppe.setVorgegebenePosition(systemGruppeEigenschaften.getPosition(systemGruppe));
      return gruppe;
    });
  }

  public GruppeImGebiet getGruppeImGebiet(Gruppe gruppe, Wahldurchfuehrungsgebiet gebiet, GruppeImGebietFactory gruppeImGebietFactory) {
    return gruppenImGebiet.computeIfAbsent(gruppe, x -> new HashMap<>()).computeIfAbsent(gebiet, x -> {
      GruppeImGebiet gruppeImGebiet = gruppeImGebietFactory.create();
      gruppeImGebiet.setGruppe(gruppe);
      gruppeImGebiet.setGebiet(gebiet);
      if (gruppeImGebiet.isPolitischeGruppe()) {
        gruppeImGebiet.setVorgegebenePosition(gruppe.getVorgegebenePosition());
      }
      return gruppeImGebiet;
    });
  }

  public PolitischeGruppe getPolitischeGruppe(Long position, PolitischeGruppeFactory politischeGruppeFactory) {
    return politischeGruppeMap.computeIfAbsent(position, x -> {
      PolitischeGruppe politischeGruppe = politischeGruppeFactory.create();
      politischeGruppe.setVorgegebenePosition(position);
      return politischeGruppe;
    });
  }

  public Liste getListe(PolitischeGruppe politischeGruppe, WahldurchfuehrungsgebietFactory wahldurchfuehrungsgebietFactory, GruppeImGebietFactory gruppeImGebietFactory) {
    return listen.computeIfAbsent(politischeGruppe, x -> {
      Liste liste = new Liste();
      liste.setGruppeImGebiet(getGruppeImGebiet(politischeGruppe, getOberstesGebiet(wahldurchfuehrungsgebietFactory), gruppeImGebietFactory));
      liste.getGruppeImGebiet().setVorgegebenePosition(politischeGruppe.getVorgegebenePosition());
      return liste;
    });
  }

  public ReferendumOption getReferendumOption(PolitischeGruppe politischeGruppe, Long referendumOptionPosition, WahldurchfuehrungsgebietFactory wahldurchfuehrungsgebietFactory,
      GruppeImGebietFactory gruppeImGebietFactory) {
    Map<Long, ReferendumOption> referendumOptionenPolitischeGruppe = referendumOptionen.computeIfAbsent(politischeGruppe, x -> new HashMap<>());
    return referendumOptionenPolitischeGruppe.computeIfAbsent(referendumOptionPosition, x -> {
      ReferendumOption referendumOption = new ReferendumOption();
      referendumOption.setPosition(referendumOptionPosition);
      referendumOption.setGruppeImGebiet(getGruppeImGebiet(politischeGruppe, getOberstesGebiet(wahldurchfuehrungsgebietFactory), gruppeImGebietFactory));
      referendumOption.getGruppeImGebiet().setVorgegebenePosition(politischeGruppe.getVorgegebenePosition());
      return referendumOption;
    });
  }

  public ReferendumOption getReferendumOption(PolitischeGruppe politischeGruppe, Long referendumOptionPosition, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
      GruppeImGebietFactory gruppeImGebietFactory) {
    Map<Long, ReferendumOption> referendumOptionenPolitischeGruppe = referendumOptionen.computeIfAbsent(politischeGruppe, x -> new HashMap<>());
    return referendumOptionenPolitischeGruppe.computeIfAbsent(referendumOptionPosition, x -> {
      ReferendumOption referendumOption = new ReferendumOption();
      referendumOption.setPosition(referendumOptionPosition);
      referendumOption.setGruppeImGebiet(getGruppeImGebiet(politischeGruppe, wahldurchfuehrungsgebiet, gruppeImGebietFactory));
      referendumOption.getGruppeImGebiet().setVorgegebenePosition(politischeGruppe.getVorgegebenePosition());
      return referendumOption;
    });
  }

  private Wahldurchfuehrungsgebiet getOberstesGebiet(WahldurchfuehrungsgebietFactory wahldurchfuehrungsgebietFactory) {
    if (oberstesGebiet == null) {
      oberstesGebiet = wahldurchfuehrungsgebietFactory.create();
      oberstesGebiet.setGebietsart(new Gebietsart());
    }
    return oberstesGebiet;
  }

  public static Gebietsart createGebietsart(GebietsartBezirksart bezirksart) {
    Gebietsart result = new Gebietsart();
    result.setBezirksart(bezirksart);
    result.setName(bezirksart.name());
    return result;
  }

  public Map<Gebietsart, Map<String, Wahldurchfuehrungsgebiet>> getGebiete() {
    return gebiete;
  }
}
