
package de.ivu.elect.wus.exchange.gebietsbaum;

import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;

import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nullable;
import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.EmlImportMapper;
import de.ivu.elect.EmlImportMapperHelper;
import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.exchange.control.FileKonfiguration;
import de.ivu.elect.business.exchange.control.MappingException;
import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietDAO;
import de.ivu.elect.business.gebietsbaum.boundary.GebietDAO;
import de.ivu.elect.business.gebietsbaum.boundary.GebietsartTemplate;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietFactory;
import de.ivu.elect.business.gebietsbaum.control.Gebietsbaum;
import de.ivu.elect.business.gebietsbaum.control.StimmbezirkGebietsarten;
import de.ivu.elect.business.gebietsbaum.control.StimmbezirkGebietsbaumExchangeContext;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.gebietsbaum.entity.NlGebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.wahl.entity.NlWahlart;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.jee.exchange.Violations;
import de.ivu.elect.nl.eml.EmlWrapper;
import de.ivu.elect.nl.eml.readable.Eml110bWrapper;
import de.ivu.elect.nl.eml.readable.PollingPlaceWrapper;
import de.ivu.elect.was.exchange.wahl.Eml110Mapper;
import de.ivu.wahl.wus.oasis.eml.VotingChannelType;

public class Eml110bStimmbezirkImportMapper implements GebietsbaumStimmbezirkImportMapper, Serializable, EmlImportMapper {

  private static final long serialVersionUID = 1L;

  private static final Logger LOG = LoggerFactory.getLogger(Eml110bStimmbezirkImportMapper.class);

  public static final String EML_ID = "110b";

  public static final String FILE_FORMAT_NAME = "eml.110b.file.format";

  @Inject
  private transient Eml110Mapper eml110Mapper;

  @Inject
  private transient WahldurchfuehrungsgebietFactory wahldurchfuehrungsgebietFactory;

  @Inject
  private transient StimmbezirkGebietsarten stimmbezirkGebietsarten;

  @Inject
  private transient GebietDAO gebietDAO;

  @Inject
  private transient EmlImportMapperHelper emlImportMapperHelper;

  public Eml110bStimmbezirkImportMapper() {
    super();
  }

  @Override
  public boolean isAcceptContext(StimmbezirkGebietsbaumExchangeContext context) {
    return context.getWahl() != null && !isStimmbezirkeVorhanden(context.getWahl()) && !isBackupRestoreEnabled(context.getWahl());
  }

  @Override
  public FileFormat getFileFormat() {
    return new FileFormat(FILE_FORMAT_NAME, FileType.XML);
  }

  private boolean isStimmbezirkeVorhanden(Wahl wahl) {
    return isStimmbezirkeVorhanden(wahl, gebietDAO);
  }

  public static boolean isStimmbezirkeVorhanden(Wahl wahl, AbstractGebietDAO gebietDAO) {
    return gebietDAO.findByWahl(wahl).stream().anyMatch(gebiet -> gebiet.getGebietsart().getBezirksart().isBezirk());
  }

  @Override
  public Gebietsbaum deserialize(InputStream is, StimmbezirkGebietsbaumExchangeContext context, FileKonfiguration fileKonfiguration) throws MappingException {
    EmlWrapper emlWrapper = emlImportMapperHelper.readEmlNoHash(is);
    Eml110bWrapper eml110bWrapper = new Eml110bWrapper(emlWrapper.getContent());

    Wahl wahl = eml110Mapper.mappeWahl(emlWrapper);

    Wahldurchfuehrungsgebiet uebergeordnetesGebiet = wahldurchfuehrungsgebietFactory.create();

    uebergeordnetesGebiet.setNummer(noLeadingZero(eml110bWrapper.getContest().getUebergeordnetesGebietNummer()));
    uebergeordnetesGebiet.setName(eml110bWrapper.getContest().getUebergeordnetesGebietName());

    List<Wahldurchfuehrungsgebiet> gebiete = new ArrayList<>();

    GebietsartTemplate gebietsartTemplateUrnenwahlbezirk = stimmbezirkGebietsarten.getGebietsartTemplateForStimmbezirke(context.getMandant());
    GebietsartTemplate gebietsartTemplateBriefwahlbezirk = stimmbezirkGebietsarten.getGebietsartTemplateForBriefwahlbezirke(context.getMandant());

    Gebietsart gebietsartUrnenwahlbezirk = Gebietsart.createGebietsartFromTemplate(gebietsartTemplateUrnenwahlbezirk);
    Gebietsart gebietsartBriefwahlbezirk = Gebietsart.createGebietsartFromTemplate(gebietsartTemplateBriefwahlbezirk);

    Gebiet wurzelgebietInDb = gebietDAO.findByWahlWurzelgebiet(context.getWahl());

    boolean briefwahlGemeindeUnterstuetzt = NlWahlart.isBriefwahlGemeindeUnterstuetzt(wurzelgebietInDb);
    boolean briefwahlAuslandUnterstuetzt = NlWahlart.isBriefwahlAuslandUnterstuetzt(wurzelgebietInDb);

    LOG.info("Importiere Erfassungsgebiete [wurzelgebiet = {}, briefwahlGemeindeUnterstuetzt = {}, briefwahlAuslandUnterstuetzt = {}]", wurzelgebietInDb, briefwahlGemeindeUnterstuetzt,
        briefwahlAuslandUnterstuetzt);

    long position = 1;

    for (PollingPlaceWrapper pollingPlaceWrapper : eml110bWrapper.getContest().getPollingPlace()) {
      String nummer = noLeadingZero(pollingPlaceWrapper.getId());
      String name = trimToEmpty(pollingPlaceWrapper.getName());

      boolean matchesGebietsartBriefwahlbezirk = VotingChannelType.POSTAL.equals(pollingPlaceWrapper.getChannel());

      if (!matchesGebietsartBriefwahlbezirk || briefwahlGemeindeUnterstuetzt || briefwahlAuslandUnterstuetzt) { // ELECT-13419
        Wahldurchfuehrungsgebiet gebiet = wahldurchfuehrungsgebietFactory.create();

        gebiete.add(gebiet);

        gebiet.setUebergeordnetesGebiet(uebergeordnetesGebiet);
        gebiet.setNummer(nummer);
        gebiet.setName(name);
        gebiet.setPosition(position++);

        Long anzahlWahlberechtigte = parseLong(pollingPlaceWrapper.getAnzahlWahlberechitige());

        if (matchesGebietsartBriefwahlbezirk) {
          gebiet.setGebietsart(gebietsartBriefwahlbezirk);
          // ELECTVAPP-772 gebiet.setAnzahlWahlberechtigteMitWahlschein(parseLong(pollingPlaceWrapper.getAnzahlWahlberechitige()));
          gebiet.setAnzahlWahlberechtigteOhneWahlschein(anzahlWahlberechtigte); // ELECTVAPP-772

          if (briefwahlGemeindeUnterstuetzt) { // ELECT-13419
            new NlGebiet(gebiet).setBriefwahlgebietGemeinde();
          } else if (briefwahlAuslandUnterstuetzt) {
            new NlGebiet(gebiet).setBriefwahlgebietAusland();
          }
        } else {
          gebiet.setGebietsart(gebietsartUrnenwahlbezirk);
          gebiet.setAnzahlWahlberechtigteOhneWahlschein(anzahlWahlberechtigte);
        }

        gebiet.getAnschrift().setPlz(pollingPlaceWrapper.getZipCode());
      } else {
        LOG.warn("Ignoriere Briefwahlbezirk [nummer = {}, name = {}]", nummer, name);
      }
    }

    Long anzahlWahlberechtigteOhneWahlschein = null;
    Long anzahlWahlberechtigteMitWahlschein = null;

    for (Wahldurchfuehrungsgebiet gebiet : gebiete) {
      if (gebiet.getAnzahlWahlberechtigteOhneWahlschein() != null) {
        if (anzahlWahlberechtigteOhneWahlschein == null) {
          anzahlWahlberechtigteOhneWahlschein = gebiet.getAnzahlWahlberechtigteOhneWahlschein();
        } else {
          anzahlWahlberechtigteOhneWahlschein += gebiet.getAnzahlWahlberechtigteOhneWahlschein();
        }
      }

      if (gebiet.getAnzahlWahlberechtigteMitWahlschein() != null) {
        if (anzahlWahlberechtigteMitWahlschein == null) {
          anzahlWahlberechtigteMitWahlschein = gebiet.getAnzahlWahlberechtigteMitWahlschein();
        } else {
          anzahlWahlberechtigteMitWahlschein += gebiet.getAnzahlWahlberechtigteMitWahlschein();
        }
      }
    }

    uebergeordnetesGebiet.setAnzahlWahlberechtigteOhneWahlschein(anzahlWahlberechtigteOhneWahlschein);
    uebergeordnetesGebiet.setAnzahlWahlberechtigteMitWahlschein(anzahlWahlberechtigteMitWahlschein);

    List gebieteOhneGenerics = gebiete;

    return new Gebietsbaum(wahl, gebieteOhneGenerics);
  }

  private String noLeadingZero(String string) {
    if (isBlank(string) || string.length() == 1) {
      return string;
    }

    if (string.startsWith("0")) {
      return noLeadingZero(string.substring(1));
    }

    return string;
  }

  @Nullable
  private Long parseLong(String string) {
    if (isBlank(string)) {
      return null;
    }

    try {
      return Long.parseLong(string);
    } catch (Exception ex) {
      return null;
    }
  }

  @Override
  public Violations validateInputFormat(InputStream is, StimmbezirkGebietsbaumExchangeContext context, FileKonfiguration fileKonfiguration) {
    Violations violations = new Violations();

    EmlWrapper emlWrapper = emlImportMapperHelper.readEmlNoHash(is);

    if (!EML_ID.equalsIgnoreCase(emlWrapper.getContent().getId())) {
      violations.addError("eml.110b.id.falsch", emlWrapper.getContent().getId());

      return violations;
    }

    List<PollingPlaceWrapper> pollingPlaceWrappersBriefwahl = new Eml110bWrapper(emlWrapper.getContent()).getContest().getPollingPlace().stream() //
        .filter(pollingPlaceWrapper -> VotingChannelType.POSTAL.equals(pollingPlaceWrapper.getChannel())) //
        .collect(toList());

    if (!pollingPlaceWrappersBriefwahl.isEmpty()) {
      Gebiet wurzelgebietInDb = gebietDAO.findByWahlWurzelgebiet(context.getWahl());

      boolean briefwahlGemeindeUnterstuetzt = NlWahlart.isBriefwahlGemeindeUnterstuetzt(wurzelgebietInDb);
      boolean briefwahlAuslandUnterstuetzt = NlWahlart.isBriefwahlAuslandUnterstuetzt(wurzelgebietInDb);

      if (briefwahlGemeindeUnterstuetzt || briefwahlAuslandUnterstuetzt) {
        if (briefwahlGemeindeUnterstuetzt && briefwahlAuslandUnterstuetzt) {
          violations.addInfo("eml.110b.briefwahlbezirk.default.gemeinde");
        }
      } else {
        for (PollingPlaceWrapper pollingPlaceWrapper : pollingPlaceWrappersBriefwahl) {
          String nummer = noLeadingZero(pollingPlaceWrapper.getId());
          String name = trimToEmpty(pollingPlaceWrapper.getName());

          violations.addWarning("eml.110b.briefwahlbezirk.ignoriert", nummer, name); // ELECT-13419
        }
      }
    }

    return violations;
  }
}
