package de.ivu.elect.business.statistik.boundary;

import java.util.Date;
import java.util.List;

import javax.ejb.Stateless;
import javax.inject.Inject;

import de.ivu.elect.business.benutzerverwaltung.boundary.BenutzerDAO;
import de.ivu.elect.business.benutzerverwaltung.boundary.BenutzerEinsatzortDAO;
import de.ivu.elect.business.datei.entity.DateiImportBerichtCustomData;
import de.ivu.elect.business.exchange.control.AbstractImporter;
import de.ivu.elect.business.exchange.control.ImportExchange;
import de.ivu.elect.business.gebietsbaum.boundary.GebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.QGebiet;
import de.ivu.elect.business.security.control.ElectCommonPrincipal;
import de.ivu.elect.business.statistik.entity.QStatistikLauf;
import de.ivu.elect.business.statistik.entity.StatistikKennzeichen;
import de.ivu.elect.business.statistik.entity.StatistikLauf;
import de.ivu.elect.business.wahl.boundary.WahlDAO;
import de.ivu.elect.business.wahl.boundary.WahlartDAO;
import de.ivu.elect.business.wahl.boundary.WahlterminDAO;
import de.ivu.elect.business.wahl.entity.QWahl;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import de.ivu.elect.jee.entity.QueryDsl;
import de.ivu.elect.jee.exchange.Violations;
import de.ivu.elect.business.exchange.entity.ImportUpdateReport;
import de.ivu.elect.business.statistik.control.StatistikLaufImportContext;
import de.ivu.elect.business.statistik.control.StatistikLaufImportExchange;
import de.ivu.elect.business.statistik.entity.StatistikLaufMeta;
import de.ivu.elect.business.statistik.entity.StatistikLaufMitKennzeichen;
import de.ivu.elect.jee.entity.Meta;
import de.ivu.elect.jee.exchange.ImportOptions;

@Stateless
public class StatistikLaufImporter extends AbstractImporter<List<StatistikLaufMitKennzeichen>, StatistikLaufImportContext, DateiImportBerichtCustomData> {

  @Inject
  StatistikLaufMeta statistikLaufMeta;

  @Inject
  StatistikLaufImportExchange importExchange;

  @Inject
  StatistikLaufDAO statistikLaufDAO;

  @Inject
  StatistikKennzeichenDAO statistikKennzeichenDAO;

  @Inject
  WahlterminDAO wahlterminDAO;

  @Inject
  WahlDAO wahlDAO;

  @Inject
  ElectCommonPrincipal principal;

  @Inject
  BenutzerDAO benutzerDAO;

  @Inject
  BenutzerEinsatzortDAO benutzerEinsatzortDAO;

  @Inject
  WahlartDAO wahlartDAO;

  @Inject
  GebietDAO gebietDAO;

  @Override
  protected void validiere(Violations violations, List<StatistikLaufMitKennzeichen> statistikLaufMitKennzeichens, StatistikLaufImportContext statistikLaufImportContext, ImportOptions importOptions) {
    for (StatistikLaufMitKennzeichen statistikLaufMitKennzeichen : statistikLaufMitKennzeichens) {
      StatistikLauf statistikLauf = statistikLaufMitKennzeichen.getStatistikLauf();
      List<StatistikKennzeichen> statistikKennzeichens = statistikLaufMitKennzeichen.getKennzeichenZumLauf();

      if (statistikLauf == null) {
        violations.addError("statistik.lauf.import.statistik.lauf.ist.null");
      }

      if (statistikLauf.getStatus() == null) {
        violations.addError("statistik.lauf.import.importer.status.nicht.gesetzt");
      }

      if (statistikLauf.getWahl() != null) {
        validateWahlSuche(violations, statistikLaufImportContext, statistikLauf.getWahl().getWahlart().getWahlartName());
      }

      for (StatistikKennzeichen statistikKennzeichen : statistikKennzeichens) {
        if (statistikKennzeichen.getKennzeichen() == null) {
          violations.addError("statistik.kennzeichen.import.importer.kennzeichen.nicht.gesetzt");
        }

        if (statistikKennzeichen.getWahl() != null) {
          validateWahlSuche(violations, statistikLaufImportContext, statistikKennzeichen.getWahl().getWahlart().getWahlartName());
        }

        if (statistikKennzeichen.getGebiet() != null) {
          validateGebieteSuche(violations, statistikKennzeichen, statistikLaufImportContext);
        }
      }

    }
  }

  private void validateWahlSuche(Violations violations, StatistikLaufImportContext statistikLaufImportContext, String wahlartNameExtern) {
    Wahltermin wahltermin = statistikLaufImportContext.getWahltermin();
    List<Wahl> wahlenInDB = findeWahlenInDB(wahltermin, wahlartNameExtern);

    if (wahlenInDB.size() > 1) {
      violations.addError("statistik.lauf.import.importer.mehrere.wahlen.gefunden", wahlartNameExtern, wahltermin.getId());
    } else if (wahlenInDB.isEmpty()) {
      violations.addError("statistik.lauf.import.importer.keine.wahlen.gefunden", wahlartNameExtern, wahltermin.getId());
    }
  }

  private void validateGebieteSuche(Violations violations, StatistikKennzeichen statistikKennzeichen, StatistikLaufImportContext statistikLaufImportContext) {
    Wahltermin wahltermin = statistikLaufImportContext.getWahltermin();
    List<Gebiet> gebieteInDB = findeGebieteInDB(statistikKennzeichen, wahltermin);

    if (gebieteInDB.isEmpty()) {
      violations.addError("statistik.lauf.import.importer.kein.gebiet.gefunden", statistikKennzeichen.getGebiet().getNummer(),
          statistikKennzeichen.getGebiet().getGebietsart().getName());
    } else if (gebieteInDB.size() > 1) {
      violations.addError("statistik.lauf.import.importer.mehrere.gebiete.gefunden", statistikKennzeichen.getGebiet().getNummer(),
          statistikKennzeichen.getGebiet().getGebietsart().getName());
    }
  }

  @Override
  protected void erstelleBericht(List<StatistikLaufMitKennzeichen> statistikLaufMitKennzeichens, StatistikLaufImportContext statistikLaufImportContext, ImportOptions importOptions, StringBuilder bericht, boolean vorab) {

  }

  @Override
  protected ImportUpdateReport createImportUpdateReport(List<StatistikLaufMitKennzeichen> statistikLaufMitKennzeichens, Violations violations, StatistikLaufImportContext statistikLaufImportContext, ImportOptions importOptions, StringBuilder bericht) {
    QStatistikLauf qStatistikLauf = QStatistikLauf.statistikLauf;
    QueryDsl queryDsl = statistikLaufDAO.getQueryDsl();

    int statistikLaufeNurInDatei = statistikLaufMitKennzeichens.size();
    int statistikLaufeNurInDB = queryDsl.from(qStatistikLauf).list().size();

    return new ImportUpdateReport(violations, statistikLaufeNurInDatei, statistikLaufeNurInDB, 0, importOptions, bericht);
  }

  @Override
  protected void anlegenWennNichtVorhanden(List<StatistikLaufMitKennzeichen> statistikLaufMitKennzeichens, StatistikLaufImportContext statistikLaufImportContext, DateiImportBerichtCustomData customDateiImportBericht, StringBuilder bericht) {
    Wahltermin wahlterminAttached = wahlterminDAO.attach(statistikLaufImportContext.getWahltermin());

    for (StatistikLaufMitKennzeichen statistikLaufMitKennzeichen : statistikLaufMitKennzeichens) {
      persistiere(statistikLaufMitKennzeichen, wahlterminAttached);
    }
  }

  private void persistiere(StatistikLaufMitKennzeichen statistikLaufMitKennzeichen, Wahltermin wahlterminAttached) {
    StatistikLauf statistikLauf = statistikLaufMitKennzeichen.getStatistikLauf();
    List<StatistikKennzeichen> statistikKennzeichensZumLauf = statistikLaufMitKennzeichen.getKennzeichenZumLauf();

    StatistikLauf statistikLaufPersisted = persistiereStatistikLaufAndGetIt(wahlterminAttached, statistikLauf);

    for (StatistikKennzeichen statistikKennzeichen : statistikKennzeichensZumLauf) {
      persistiereStatistikKennzeichen(statistikKennzeichen, statistikLaufPersisted, wahlterminAttached);
    }
  }

  private StatistikLauf persistiereStatistikLaufAndGetIt(Wahltermin wahlterminAttached, StatistikLauf statistikLauf) {
    statistikLauf.setWahltermin(wahlterminAttached);

    if (statistikLauf.getWahl() != null) { //Impliziert, dass WahlartName gesetzt ist
      String wahlartNameExtern = statistikLauf.getWahl().getWahlart().getWahlartName();

      List<Wahl> wahlenInDB = findeWahlenInDB(wahlterminAttached, wahlartNameExtern);
      if (wahlenInDB.size() == 1) {
        statistikLauf.setWahl(wahlenInDB.get(0));
      }
    }

    if (statistikLauf.getAusloeser() == null) {
      statistikLauf.setAusloeser(principal.getFormattedName(benutzerDAO, benutzerEinsatzortDAO));
    }

    if (statistikLauf.getZeitpunktDesLaufs() == null) {
      statistikLauf.setZeitpunktDesLaufs(new Date());
    }


    statistikLaufDAO.persist(statistikLauf);

    return statistikLauf;
  }

  private void persistiereStatistikKennzeichen(StatistikKennzeichen statistikKennzeichen, StatistikLauf statistikLaufPersisted, Wahltermin wahlterminAttached) {
    statistikKennzeichen.setStatistikLauf(statistikLaufPersisted);

    if (statistikKennzeichen.getGebiet() != null) { //Impliziert, dass GebietsNummer und GebietsArtName gesetzt sind
      List<Gebiet> gebieteInDB = findeGebieteInDB(statistikKennzeichen, wahlterminAttached);

      if (gebieteInDB.size() == 1) {
        statistikKennzeichen.setGebiet(gebieteInDB.get(0));
      }
    }

    if (statistikKennzeichen.getWahl() != null) { //Impliziert, dass WahlartName gesetzt ist
      String wahlartNameExtern = statistikKennzeichen.getWahl().getWahlart().getWahlartName();

      List<Wahl> wahlenInDB = findeWahlenInDB(wahlterminAttached, wahlartNameExtern);
      if (wahlenInDB.size() == 1) {
        statistikKennzeichen.setWahl(wahlenInDB.get(0));
      }
    }

    statistikKennzeichenDAO.persist(statistikKennzeichen);
  }

  private List<Gebiet> findeGebieteInDB(StatistikKennzeichen statistikKennzeichen, Wahltermin wahltermin) {
    String gebietsArtNameExtern = statistikKennzeichen.getGebiet().getGebietsart().getName();
    String gebietsNummerExtern = statistikKennzeichen.getGebiet().getNummer();

    QGebiet qGebiet = QGebiet.gebiet;
    QueryDsl queryDsl = gebietDAO.getQueryDsl();

    queryDsl.from(qGebiet);
    queryDsl.eq(qGebiet.wahltermin, wahltermin);
    queryDsl.eq(qGebiet.nummer, gebietsNummerExtern);
    queryDsl.eq(qGebiet.gebietsart.name, gebietsArtNameExtern);

    return (List<Gebiet>) queryDsl.list();
  }

  private List<Wahl> findeWahlenInDB(Wahltermin wahlterminAttached, String wahlartNameExtern) {
    QWahl qWahl = QWahl.wahl;
    QueryDsl queryDsl = wahlDAO.getQueryDsl();

    queryDsl.from(qWahl);
    queryDsl.eq(qWahl.wahltermin, wahlterminAttached);
    queryDsl.eq(qWahl.wahlart.name, wahlartNameExtern);

    return (List<Wahl>) queryDsl.list();
  }

  @Override
  protected void aktualisiereBestehendenDaten(List<StatistikLaufMitKennzeichen> statistikLaufMitKennzeichens, StatistikLaufImportContext statistikLaufImportContext, DateiImportBerichtCustomData customDateiImportBericht, StringBuilder bericht) {
  }

  @Override
  protected void loescheBestehendenDatenWennNichtInImportDatenVorhanden(List<StatistikLaufMitKennzeichen> statistikLaufMitKennzeichens, StatistikLaufImportContext statistikLaufImportContext, DateiImportBerichtCustomData customDateiImportBericht, StringBuilder bericht) {
  }

  @Override
  protected ImportExchange<List<StatistikLaufMitKennzeichen>, StatistikLaufImportContext> getExchange() {
    return importExchange;
  }

  @Override
  public Meta getMetaForEntityInternal() {
    return statistikLaufMeta;
  }
}
