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

import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import de.ivu.elect.business.ergebniskonflikt.entity.AbstractErgebnisKonfliktLosoptionenCustomData;
import de.ivu.elect.business.ergebniskonflikt.entity.ErgebnisKonfliktCustomDataFormatter;
import de.ivu.elect.business.ergebniskonflikt.entity.ErgebnisKonfliktLosoption;
import de.ivu.elect.business.gebietsbaum.boundary.AbstractWahldurchfuehrungsgebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.GebietPK;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.boundary.AbstractPolitischeGruppeDAO;
import de.ivu.elect.business.gruppe.entity.GruppePK;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.kandidatur.boundary.AbstractKandidaturDAO;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.person.entity.PersonendatenPK;
import de.ivu.elect.business.wahlvorschlag.boundary.AbstractListenverbindungDAO;
import de.ivu.elect.business.wahlvorschlag.boundary.AbstractWahlvorschlagDAO;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.Listenverbindung;
import de.ivu.elect.business.wahlvorschlag.entity.ListenverbindungPK;
import de.ivu.elect.business.wahlvorschlag.entity.NlListe;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagPK;

public class OsvLegacyKonfliktCustomData extends AbstractErgebnisKonfliktLosoptionenCustomData {

  private static final long serialVersionUID = 1L;

  private final OsvLegacyErgebnisKonfliktArt osvLegacyErgebnisKonfliktArt;
  private final Long position;
  private final OsvLegacyArtikelnummer artikelnummer;

  private final List<GruppePK> gruppenZurAuswahl = new ArrayList<>();
  private final List<PersonendatenPK> listenkandidaturenZurAuswahl = new ArrayList<>();
  private final List<P2Liste> p2ListeZurAuswahl = new ArrayList<>(); //um die Reihenfolge der P2Listen einzuhalten.

  private OsvLegacyKonfliktLoesung konfliktLoesung = new OsvLegacyKonfliktLoesung();

  public OsvLegacyKonfliktCustomData(OsvLegacyErgebnisKonfliktArt osvLegacyErgebnisKonfliktArt, Long position, OsvLegacyArtikelnummer artikelnummer) {
    this.osvLegacyErgebnisKonfliktArt = osvLegacyErgebnisKonfliktArt;
    this.position = position;
    this.artikelnummer = artikelnummer;
    setAnzahlZuLosen(1);
  }

  public OsvLegacyErgebnisKonfliktArt getOsvLegacyErgebnisKonfliktArt() {
    return osvLegacyErgebnisKonfliktArt;
  }

  public OsvLegacyArtikelnummer getArtikelnummer() {
    return artikelnummer;
  }

  public Long getPosition() {
    return position;
  }

  public List<GruppePK> getGruppenZurAuswahl() {
    return gruppenZurAuswahl;
  }

  public List<WahlvorschlagPK> getListenZurAuswahl() {
    return p2ListeZurAuswahl.stream().filter(P2Liste::isListe).map(P2Liste::getListe).collect(Collectors.toList());
  }

  public void addGruppeZurAuswahl(GruppePK gruppePK) {
    gruppenZurAuswahl.add(gruppePK);
  }

  public List<PersonendatenPK> getListenkandidaturenZurAuswahl() {
    return listenkandidaturenZurAuswahl;
  }

  public List<OsvLegacyStel> getStelsZurAuswahl() {
    return p2ListeZurAuswahl.stream().filter(P2Liste::isStel).map(P2Liste::getStel).collect(Collectors.toList());
  }

  public OsvLegacyKonfliktLoesung getKonfliktLoesung() {
    return konfliktLoesung;
  }

  public void setKonfliktLoesung(OsvLegacyKonfliktLoesung konfliktLoesung) {
    this.konfliktLoesung = konfliktLoesung;
  }

  public List<P2Liste> getP2ListeZurAuswahl() {
    return p2ListeZurAuswahl;
  }

  @Override
  public String toString() {
    return "OsvLegacyKonfliktCustomData{" + "osvLegacyErgebnisKonfliktArt=" + osvLegacyErgebnisKonfliktArt + ", position=" + position + ", gruppenZurAuswahl=" + gruppenZurAuswahl
        + ", listenZurAuswahl=" + getListenZurAuswahl() + ", stelsZurAuswahl=" + getStelsZurAuswahl() + ", listenkandidaturenZurAuswahl=" + listenkandidaturenZurAuswahl + ", konfliktLoesung="
        + konfliktLoesung + '}';
  }

  public void addStelZurAuswahl(OsvLegacyStel stel) {
    p2ListeZurAuswahl.add(new P2Liste(stel));
  }

  public void addListeZurAuswahl(WahlvorschlagPK pk) {
    p2ListeZurAuswahl.add(new P2Liste(pk));
  }

  public void addListenkandidaturZurAuswahl(PersonendatenPK pk) {
    listenkandidaturenZurAuswahl.add(pk);
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (!(o instanceof OsvLegacyKonfliktCustomData)) {
      return false;
    }

    OsvLegacyKonfliktCustomData that = (OsvLegacyKonfliktCustomData) o;

    if (getOsvLegacyErgebnisKonfliktArt() != that.getOsvLegacyErgebnisKonfliktArt()) {
      return false;
    }

    // ELECT-10627
    if (getPosition() != null ? !getPosition().equals(that.getPosition()) : that.getPosition() != null) {
      return false;
    }

    if (getGruppenZurAuswahl() != null ? !getGruppenZurAuswahl().equals(that.getGruppenZurAuswahl()) : that.getGruppenZurAuswahl() != null) {
      return false;
    }
    if (getListenkandidaturenZurAuswahl() != null ? !getListenkandidaturenZurAuswahl().equals(that.getListenkandidaturenZurAuswahl()) : that.getListenkandidaturenZurAuswahl() != null) {
      return false;
    }
    return getP2ListeZurAuswahl() != null ? getP2ListeZurAuswahl().equals(that.getP2ListeZurAuswahl()) : that.getP2ListeZurAuswahl() != null;
  }

  @Override
  public int hashCode() {
    int result = getOsvLegacyErgebnisKonfliktArt() != null ? getOsvLegacyErgebnisKonfliktArt().hashCode() : 0;
    // ELECT-10627
    result = 31 * result + (getPosition() != null ? getPosition().hashCode() : 0);
    result = 31 * result + (getGruppenZurAuswahl() != null ? getGruppenZurAuswahl().hashCode() : 0);
    result = 31 * result + (getListenkandidaturenZurAuswahl() != null ? getListenkandidaturenZurAuswahl().hashCode() : 0);
    result = 31 * result + (getP2ListeZurAuswahl() != null ? getP2ListeZurAuswahl().hashCode() : 0);
    return result;
  }

  @Override
  protected Collection<PersonendatenPK> getBeteiligteKandidaturenPKs() {
    return listenkandidaturenZurAuswahl;
  }

  @Override
  protected Collection<PersonendatenPK> getGelosteKandidaturenPKs() {
    if (konfliktLoesung.isListenKandidaturEntscheidung()) {
      return singleton(konfliktLoesung.getListenkandidatur());
    }
    return emptyList();
  }

  @Override
  protected Collection<WahlvorschlagPK> getBeteiligteListenPKs() {
    return p2ListeZurAuswahl.stream().flatMap(p2 -> p2.isStel() ? p2.getStel().getListenDesStels().stream() : Stream.of(p2.getListe())).collect(Collectors.toList());
  }

  @Override
  protected Collection<WahlvorschlagPK> getGelosteListenPKs() {
    return getKonfliktLoesung().isListeEntscheidung()
        ? singleton(getKonfliktLoesung().getListe())
        : getKonfliktLoesung().isStelEntscheidung() ? getKonfliktLoesung().getStel().getListenDesStels() : emptyList();
  }

  @Override
  protected Collection<ListenverbindungPK> getBeteiligteListenverbindungenPKs() {
    return emptyList();
  }

  @Override
  protected Collection<ListenverbindungPK> getGelosteListenverbindungenPKs() {
    return emptyList();
  }

  @Override
  protected Collection<GruppePK> getBeteiligtePolitischeGruppenPKs() {
    return gruppenZurAuswahl;
  }

  @Override
  protected Collection<GruppePK> getGelostePolitischeGruppenPKs() {
    return getKonfliktLoesung().isGruppeEntscheidung() ? singleton(getKonfliktLoesung().getGruppe()) : emptyList();
  }

  @Override
  protected Collection<GebietPK> getBeteiligteGebietPKs() {
    return emptyList();
  }

  @Override
  protected Collection<GebietPK> getGelosteGebietPKs() {
    return emptyList();
  }

  @Override
  public void loeseKonfliktAutomatisch() {
    if (!gruppenZurAuswahl.isEmpty()) {
      konfliktLoesung.setGruppe(gruppenZurAuswahl.get(0));
    } else if (!p2ListeZurAuswahl.isEmpty()) {
      P2Liste p2Liste = p2ListeZurAuswahl.get(0);
      if (p2Liste.isListe()) {
        konfliktLoesung.setListe(p2Liste.getListe());
      } else if (p2Liste.isStel()) {
        konfliktLoesung.setStel(p2Liste.getStel());
      }
    } else if (!listenkandidaturenZurAuswahl.isEmpty()) {
      konfliktLoesung.setListenkandidatur(listenkandidaturenZurAuswahl.get(0));
    }

    notifyEntity();
  }

  @Override
  public void loeseKonfliktAutomatisch(int indexDerLoesung) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void loeseKonfliktAutomatischZyklisch(int indexDerLoesung) {
    throw new UnsupportedOperationException();
  }

  @Override
  public BigInteger getAnzahlLoesungen() {
    return new BigInteger("1");
  }

  @Override
  public int getAnzahlZyklischerLoesungen() {
    throw new UnsupportedOperationException();
  }

  @Override
  public String createLoesungBeschreibung(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, Collection<Wahlvorschlag> wahlvorschlaege, Collection<Kandidatur> kandidaturen,
      Collection<Listenverbindung> listenverbindungen, Collection<PolitischeGruppe> politischeGruppen, Collection<Wahldurchfuehrungsgebiet> gebiete) {
    return "";
  }

  @Override
  public List<ErgebnisKonfliktLosoption> getLosoptionenVerfuegbar(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, AbstractWahlvorschlagDAO wahlvorschlagDAO,
      AbstractKandidaturDAO kandidaturDAO, AbstractListenverbindungDAO listenverbindungDAO, AbstractPolitischeGruppeDAO politischeGruppeDAO,
      AbstractWahldurchfuehrungsgebietDAO wahldurchfuehrungsgebietDAO) {
    List<ErgebnisKonfliktLosoption> result = emptyList();

    if (!gruppenZurAuswahl.isEmpty()) {
      result = createLosoptionen(ergebnisKonfliktCustomDataFormatter, politischeGruppeDAO, gruppenZurAuswahl);
    } else if (!p2ListeZurAuswahl.isEmpty()) {
      result = createLosoptionen(ergebnisKonfliktCustomDataFormatter, wahlvorschlagDAO, p2ListeZurAuswahl);
    } else if (!listenkandidaturenZurAuswahl.isEmpty()) {
      result = createLosoptionen(ergebnisKonfliktCustomDataFormatter, kandidaturDAO, listenkandidaturenZurAuswahl);
    }

    return result;
  }

  @Override
  public List<ErgebnisKonfliktLosoption> getLosoptionenGelost(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, AbstractWahlvorschlagDAO wahlvorschlagDAO,
      AbstractKandidaturDAO kandidaturDAO, AbstractListenverbindungDAO listenverbindungDAO, AbstractPolitischeGruppeDAO politischeGruppeDAO,
      AbstractWahldurchfuehrungsgebietDAO wahldurchfuehrungsgebietDAO) {
    List<ErgebnisKonfliktLosoption> losoption = emptyList();

    if (konfliktLoesung.isGruppeEntscheidung()) {
      losoption = createLosoptionen(ergebnisKonfliktCustomDataFormatter, politischeGruppeDAO, singleton(konfliktLoesung.getGruppe()));
    } else if (konfliktLoesung.isP2ListeEntscheidung()) {
      losoption = createLosoptionen(ergebnisKonfliktCustomDataFormatter, wahlvorschlagDAO, singletonList(konfliktLoesung.getP2Liste()));
    } else if (konfliktLoesung.isListenKandidaturEntscheidung()) {
      losoption = createLosoptionen(ergebnisKonfliktCustomDataFormatter, kandidaturDAO, singleton(konfliktLoesung.getListenkandidatur()));
    }

    return losoption;
  }

  @Override
  public void setLosoptionenGelost(List<ErgebnisKonfliktLosoption> values) {
    if (values.size() > 1) {
      throw new IllegalStateException("Es muss genau 1 Losoption geben: " + values);
    }

    ErgebnisKonfliktLosoption losoption = values.get(0);
    if (losoption.isInstanceOf(PolitischeGruppe.class)) {
      konfliktLoesung.setGruppe((GruppePK) losoption.getPk());
    } else if (losoption.isInstanceOf(Liste.class)) {
      setGelosteP2Liste((WahlvorschlagPK) losoption.getPk());
    } else if (losoption.isInstanceOf(Listenkandidatur.class)) {
      konfliktLoesung.setListenkandidatur((PersonendatenPK) losoption.getPk());
    }

    notifyEntity();
  }

  @Override
  public String getClassName() {
    return OsvLegacyKonfliktCustomData.class.getName();
  }

  private List<ErgebnisKonfliktLosoption> createLosoptionen(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, AbstractWahlvorschlagDAO wahlvorschlagDAO,
      List<P2Liste> p2Listen) {
    List<ErgebnisKonfliktLosoption> result = new ArrayList<>();
    boolean listenInnerhalbEinerGruppe = p2Listen.stream().map(p2 -> p2.getGruppe(wahlvorschlagDAO)).distinct().count() == 1;
    for (P2Liste p2Liste : p2Listen) {
      if (p2Liste.isListe()) {
        result.add(createLosoptionFuerListe(ergebnisKonfliktCustomDataFormatter, wahlvorschlagDAO, p2Liste.getListe(), listenInnerhalbEinerGruppe));
      } else if (p2Liste.isStel()) {
        result.add(createStelLosoption(ergebnisKonfliktCustomDataFormatter, wahlvorschlagDAO, p2Liste.getStel()));
      }
    }
    return result;
  }

  private ErgebnisKonfliktLosoption createLosoptionFuerListe(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, AbstractWahlvorschlagDAO wahlvorschlagDAO, WahlvorschlagPK pk,
      boolean innerhalbEinerGruppe) {
    Wahlvorschlag wahlvorschlag = wahlvorschlagDAO.findByPrimaryKey(pk);
    String wahlvorschlagDisplayName = createWahlvorschlagDisplayName(ergebnisKonfliktCustomDataFormatter, wahlvorschlag);
    if (innerhalbEinerGruppe) {
      wahlvorschlagDisplayName += " (" + wahlvorschlag.getGebiet().getName() + ")";
    }
    return new ErgebnisKonfliktLosoption(wahlvorschlag, wahlvorschlagDisplayName, wahlvorschlag.getGebiet());
  }

  private ErgebnisKonfliktLosoption createStelLosoption(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, AbstractWahlvorschlagDAO wahlvorschlagDAO, OsvLegacyStel stel) {
    Liste eineListeDesStels = getEineListeDesStels(stel, wahlvorschlagDAO);

    String stelDisplayName = createStelDisplayName(wahlvorschlagDAO, stel, NlListe.getStelNummerWas(eineListeDesStels).orElse(null),
        createWahlvorschlagDisplayName(ergebnisKonfliktCustomDataFormatter, eineListeDesStels));

    return new ErgebnisKonfliktLosoption(eineListeDesStels, stelDisplayName, eineListeDesStels.getGebiet());
  }

  private String createWahlvorschlagDisplayName(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, Wahlvorschlag wahlvorschlag) {
    return ergebnisKonfliktCustomDataFormatter.formatWahlvorschlagDisplayName(wahlvorschlag, w -> "");
  }

  private String createStelDisplayName(AbstractWahlvorschlagDAO wahlvorschlagDAO, OsvLegacyStel stel, Integer stelNummer, String wahlvorschlagDisplayName) {

    String gebietString = stel.getListenDesStels().stream().map(liste -> wahlvorschlagDAO.findByPrimaryKey(liste).getGebiet().getName()).collect(Collectors.joining(", "));

    return wahlvorschlagDisplayName + ", Stel: " + stelNummer + " (" + gebietString + ")";
  }

  private Liste getEineListeDesStels(OsvLegacyStel stel, AbstractWahlvorschlagDAO wahlvorschlagDAO) {
    return wahlvorschlagDAO.findByPrimaryKey(stel.getListenDesStels().iterator().next()).castTo(Liste.class).getEntity();
  }

  private void setGelosteP2Liste(WahlvorschlagPK pk) {
    Set<WahlvorschlagPK> listenDesStels = p2ListeZurAuswahl.stream().filter(p2Liste -> p2Liste.isStel() && p2Liste.getStel().getListenDesStels().contains(pk)).findAny()
        .map(p2Liste -> p2Liste.getStel().getListenDesStels()).orElse(null);

    if (listenDesStels == null) {
      konfliktLoesung.setListe(pk);
    } else {
      konfliktLoesung.setStel(new OsvLegacyStel(listenDesStels));
    }
  }
}
