
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.NlWahlart;
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;
import de.ivu.wahl.wus.oasis.eml.kiesraad.ElectionSubcategoryType;


import static de.ivu.elect.jee.entity.EntityUtil.castTo;

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, context.getNow());

    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();

    NlWahlart nlWahlartFuerContestIdentifier = NlWahlart.valueOf(wahl);
    if (ElectionSubcategoryType.PS_1.equals(eml110bWrapper.getElectionIdentifier110a().getElectionSubcategory())) {
      nlWahlartFuerContestIdentifier = NlWahlart.PROVINCIALE_STATEN1; // Spezialbehandlung PS1 ELECT-13568
    } else if (ElectionSubcategoryType.PS_2.equals(eml110bWrapper.getElectionIdentifier110a().getElectionSubcategory())) {
      nlWahlartFuerContestIdentifier = NlWahlart.PROVINCIALE_STATEN2;
    }

    String eml110ContestIdentifierLautWahlart = nlWahlartFuerContestIdentifier.getEml110ContestIdentifier();

    // ELECTVAPP-1834 und ELECT-14772
    if (StringUtils.isNotBlank(eml110ContestIdentifierLautWahlart)) {
      contest.getContent().getContestIdentifier().setId(eml110ContestIdentifierLautWahlart);
    } else {
      contest.getContent().getContestIdentifier().setId(EML110B_CONTEST_ID);
    }

    contest.setUebergeordnetesGebietName(oberstesGebiet.getName());
    contest.setUebergeordnetesGebietNummer(StringUtils.leftPad(oberstesGebiet.getNummer(), 4, '0')); // ELECTVAPP-1280

    long anzahlWahlberechtigteImWurzelgebiet = sumAnzahlWahlberechtigteImGebiet(oberstesGebiet);
    contest.getContent().setMaxVotes(Long.toString(anzahlWahlberechtigteImWurzelgebiet)); // ELECTVAPP-1797

    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);
        }

        long anzahlWahlberechtigteImErfassungsgebiet = sumAnzahlWahlberechtigteImGebiet(gebiet);
        pollingPlaceWrapper.setAnzahlWahlberechitige(Long.toString(anzahlWahlberechtigteImErfassungsgebiet));

        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);
    }
  }

  private long sumAnzahlWahlberechtigteImGebiet(Gebiet gebiet) {
    if (gebiet != null && gebiet.isInstanceOf(Wahldurchfuehrungsgebiet.class)) {
      Long result = castTo(gebiet, Wahldurchfuehrungsgebiet.class).getAnzahlWahlberechtigterGesamtA1A2A3();

      if (result != null) {
        return result;
      }
    }

    return 0L; // ELECTVAPP-1892
  }

  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());
  }
}
