package de.ivu.elect.sitzberechnung.konflikt;

import com.google.gson.Gson;
import de.ivu.elect.sitzberechnung.Kandidat;
import de.ivu.elect.sitzberechnung.Kandidatur;
import de.ivu.elect.sitzberechnung.SitzberechnungEntity;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class StimmengleichheitErgebnisKonflikt extends AbstractErgebnisKonflikt {

  private final Set<String> kandidaturenZurAuswahl;

  private final long anzahlZuLosen;

  private final Set<String> kandidaturenGelost;

  public StimmengleichheitErgebnisKonflikt(StimmengleichheitErgebnisKonfliktDaten daten) {
    this(daten.getErgebnisKonfliktStatus(), daten.getKandidaturKeysZurAuswahl(), daten.getAnzahlZuLosen(), daten.getGelosteKandidaturKeys());
  }

  public static StimmengleichheitErgebnisKonflikt createUngeloestenKonflikt(Collection<Kandidatur> kandidaturenZurAuswahl, long anzahlZuLosen) {
    return new StimmengleichheitErgebnisKonflikt(ErgebnisKonfliktStatus.UNGELOEST, kandidaturenZurAuswahl.stream()
            .map(SitzberechnungEntity::getKey)
            .collect(Collectors.toSet()), anzahlZuLosen, Collections.emptySet());
  }

  protected StimmengleichheitErgebnisKonflikt(ErgebnisKonfliktStatus ergebnisKonfliktStatus, Set<String> kandidaturenZurAuswahl, long anzahlZuLosen, Set<String> kandidaturenGelost) {
    super(ergebnisKonfliktStatus, 0);
    this.kandidaturenZurAuswahl = kandidaturenZurAuswahl;
    this.anzahlZuLosen = anzahlZuLosen;
    this.kandidaturenGelost = kandidaturenGelost;
  }


  @Override
  public ErgebnisKonfliktArt getErgebnisKonfliktArt() {
    return ErgebnisKonfliktArt.STIMMENGLEICHHEIT_DER_ZWEITPLATZIERTEN;
  }

  public long getAnzahlZuLosen() {
    return anzahlZuLosen;
  }

  private Set<String> getKandidaturenZurAuswahl() {
    return kandidaturenZurAuswahl;
  }

  private Set<String> getKandidaturenGelost() {
    return kandidaturenGelost;
  }

  public List<Kandidatur> getKandidaturenZurAuswahl(Collection<Kandidatur> alleKandidaturen) {
    return alleKandidaturen.stream().filter(k -> getKandidaturenZurAuswahl().contains(k.getKey())).collect(Collectors.toList());
  }

  public List<Kandidatur> getKandidaturenGelost(Collection<Kandidatur> alleKandidaturen) {
    return alleKandidaturen.stream().filter(k -> getKandidaturenGelost().contains(k.getKey())).collect(Collectors.toList());
  }

  @Override
  public boolean isFachlichGleich(ErgebnisKonflikt other) {
    return isGleicheKandidaten(other);
  }

  @Override
  public String toJson() {
    StimmengleichheitErgebnisKonfliktDaten result = new StimmengleichheitErgebnisKonfliktDaten();
    result.setErgebnisKonfliktStatus(getErgebnisKonfliktStatus());
    result.setAnzahlZuLosen(getAnzahlZuLosen());
    result.setKandidaturKeysZurAuswahl(kandidaturenZurAuswahl);
    result.setGelosteKandidaturKeys(kandidaturenGelost);

    return new Gson().toJson(result);
  }

  public static StimmengleichheitErgebnisKonflikt fromJson(String json) {
    StimmengleichheitErgebnisKonfliktDaten daten = new Gson().fromJson(json, StimmengleichheitErgebnisKonfliktDaten.class);

    return daten.isEmpty() ? null : new StimmengleichheitErgebnisKonflikt(daten);
  }


  private boolean isGleicheKandidaten(ErgebnisKonflikt other) {
    if (!(other instanceof StimmengleichheitErgebnisKonflikt)) {
      return false;
    }
    StimmengleichheitErgebnisKonflikt otherKonflikt = (StimmengleichheitErgebnisKonflikt) other;

    return otherKonflikt.getAnzahlZuLosen() == getAnzahlZuLosen() &&
            otherKonflikt.getKandidaturenZurAuswahl().size() == this.getKandidaturenZurAuswahl().size() &&
            otherKonflikt.getKandidaturenZurAuswahl().containsAll(this.getKandidaturenZurAuswahl());

  }

  public StimmengleichheitErgebnisKonflikt loeseKonfliktManuell(Collection<Kandidat> gelosteKandidate) {
    return new StimmengleichheitErgebnisKonflikt(ErgebnisKonfliktStatus.MANUELL_ENTSCHIEDEN, getKandidaturenZurAuswahl(), getAnzahlZuLosen(), gelosteKandidate.stream().map(SitzberechnungEntity::getKey).collect(Collectors.toSet()));
  }
}
