
package de.ivu.elect.exchange.gebietsbaum;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.zip.ZipOutputStream;

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

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.EmlExportMapper;
import de.ivu.elect.EmlExportMapperHelper;
import de.ivu.elect.business.datei.entity.Datei;
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.GebietDAO;
import de.ivu.elect.business.gebietsbaum.boundary.NlGebietDarstellungNummerFormatter;
import de.ivu.elect.business.gebietsbaum.control.GebietByBreadthFirstComparator;
import de.ivu.elect.business.gebietsbaum.control.Gebietsbaum;
import de.ivu.elect.business.gebietsbaum.control.GebietsbaumExchangeContext;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.security.boundary.EmlSignKeyService;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.nl.eml.EmlWrapper;
import de.ivu.elect.nl.eml.readable.Eml110bContestWrapper;
import de.ivu.elect.nl.eml.readable.Eml110bWrapper;
import de.ivu.elect.nl.eml.readable.PollingPlaceWrapper;
import de.ivu.elect.util.FileUtil;
import de.ivu.elect.was.exchange.wahl.Eml110Mapper;
import de.ivu.elect.wus.exchange.gebietsbaum.Eml110bStimmbezirkImportMapper;
import de.ivu.wahl.wus.oasis.eml.VotingChannelType;

public class Eml110bGebietsbaumExportMapper implements GebietsbaumExportMapper, EmlExportMapper {

  public static final String EML_ID = Eml110bStimmbezirkImportMapper.EML_ID;

  public static final String EML_FILE_NAME_PREFIX = "Stembureaus";

  public static final String FILE_FORMAT_NAME = Eml110bStimmbezirkImportMapper.FILE_FORMAT_NAME;

  public static final FileFormat FILE_FORMAT = new FileFormat(FILE_FORMAT_NAME, FileType.ZIP);

  public static final String EML110B_CONTEST_ID = "geen";

  @Inject
  private transient GebietDAO gebietDAO;

  @Inject
  private transient Eml110Mapper eml110Mapper;

  @Inject
  private transient EmlExportMapperHelper emlExportMapperHelper;

  @Inject
  private transient EmlSignKeyService emlSignKeyService;

  @Inject
  private transient Messages messages;

  @Inject
  private transient NlGebietDarstellungNummerFormatter nlGebietDarstellungNummerFormatter;

  public Eml110bGebietsbaumExportMapper() {
    super();
  }

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

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

  @Override
  public FileFormat getFileFormat() {
    return FILE_FORMAT;
  }

  @Override
  public boolean isSignEml(Wahl wahl) {
    return false; // nicht notwendig siehe https://support.elect-it.com/wiki/display/VAPP/EML-Import+validation
  }

  @Override
  public void addProperties(FormBuilder formBuilder, GebietsbaumExchangeContext context, @Nullable Datei datei) {
    addSignProperties(context.getWahl(), formBuilder, context, messages);
  }

  @Override
  public void serialize(Gebietsbaum gebietsbaum, OutputStream os, GebietsbaumExchangeContext context, FileKonfiguration fileKonfiguration) throws MappingException {
    Wahl wahl = gebietsbaum.getWahl();

    if (wahl == null) {
      return;
    }

    List<Gebiet> gebiete = gebietsbaum.getGebiete();
    gebiete.sort(new GebietByBreadthFirstComparator());

    Gebiet oberstesGebiet = gebiete.stream().filter(g -> !g.hasUebergeordnetesGebiet()).findFirst().orElse(null);

    if (oberstesGebiet == null) {
      return;
    }

    EmlWrapper emlWrapper = EmlWrapper.createEml(EML_ID);

    Eml110bWrapper eml110bWrapper = new Eml110bWrapper(emlWrapper.getContent());
    eml110bWrapper.getManagingAuthority().setAuthorityId(nlGebietDarstellungNummerFormatter.formatiereNummer(oberstesGebiet));
    eml110bWrapper.getManagingAuthority().setAuthorityName(oberstesGebiet.getName());

    eml110Mapper.mappeWahldaten(wahl, oberstesGebiet, gebietsbaum::getGebiete, eml110bWrapper.getElectionIdentifier110a(), true /* ELECTVAPP-1277 */);

    Eml110bContestWrapper contest = eml110bWrapper.getContest();
    contest.getContent().getContestIdentifier().setId(EML110B_CONTEST_ID);
    contest.setUebergeordnetesGebietName(oberstesGebiet.getName());
    contest.setUebergeordnetesGebietNummer(StringUtils.leftPad(oberstesGebiet.getNummer(), 4, '0')); // ELECTVAPP-1280

    for (Gebiet gebiet : gebiete) {
      if (gebiet.getGebietsart().getBezirksart().isBezirk()) {
        PollingPlaceWrapper pollingPlaceWrapper = contest.addPollingPlace();
        pollingPlaceWrapper.setId(gebiet.getNummer());
        pollingPlaceWrapper.setName(gebiet.getName());

        if (gebiet.isBriefwahlgebiet()) {
          pollingPlaceWrapper.setChannel(VotingChannelType.POSTAL);
        } else {
          pollingPlaceWrapper.setChannel(VotingChannelType.POLLING);
        }

        if (gebiet.isInstanceOf(Wahldurchfuehrungsgebiet.class)) {
          Long anzahlWahlberechtigteGesamt = gebiet.castTo(Wahldurchfuehrungsgebiet.class).getEntity().getAnzahlWahlberechtigterGesamtA1A2A3();

          if (anzahlWahlberechtigteGesamt != null) {
            pollingPlaceWrapper.setAnzahlWahlberechitige(Long.toString(anzahlWahlberechtigteGesamt));
          }
        }

        pollingPlaceWrapper.setZipCode(gebiet.getAnschrift().getPlz());
      }
    }

    try (ZipOutputStream zos = new ZipOutputStream(os)) {
      emlExportMapperHelper.writeEml(emlWrapper, zos, createEmlXmlFileName(wahl, oberstesGebiet), oberstesGebiet, context, retrievePrivateKey(wahl, context, emlSignKeyService),
          retrievePublicKey(wahl, context, emlSignKeyService), retrieveNlX509Metadata(wahl, context, emlSignKeyService));

      zos.finish();
    } catch (IOException ex) {
      throw new MappingException(ex.getMessage(), ex);
    }
  }

  protected String createEmlXmlFileName(Wahl wahl, Gebiet oberstesGebiet) {
    return createSimpleFileName(wahl, oberstesGebiet) + EML_FILE_EXTENSION;
  }

  private String createSimpleFileName(Wahl wahl, Gebiet oberstesGebiet) {
    return FileUtil.normalizeZipEntryNamePreserveCase(EML_FILE_NAME_PREFIX + "_" + wahl.getKennzeichen() + "_" + oberstesGebiet.getName());
  }
}
