package de.ivu.elect;

import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.datei.entity.Datei;
import de.ivu.elect.business.exchange.control.AbstractExchangeContext;
import de.ivu.elect.business.exchange.control.ImportExportStreamCreator;
import de.ivu.elect.business.konfiguration.entity.ElectKonfigurationWerte;
import de.ivu.elect.business.konfiguration.entity.KonfigurationLoader;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.LambdaEnumProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaStringProperty;
import de.ivu.elect.jee.presentation.dynamicform.ReadOnlyStringProperty;
import de.ivu.elect.nl.eml.EmlReader;
import de.ivu.elect.nl.eml.EmlWrapper;

public class EmlImportMapperHelper implements Serializable {

  private static final long serialVersionUID = 1L;

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

  public static final String EMLHASHCODE = "EMLHASHCODE";

  public static final String EML_VALIDATION = "EML_VALIDATION";

  public static final String EMLHASHCODE_CONFIRMED_AUSWAHL = "EMLHASHCODE_CONFIRMED_AUSWAHL";

  public static final String EMLHASHCODE_PARTS = "EMLHASHCODE_PARTS";

  public static final String EMLHASHCODE_PART = "EMLHASHCODE_PART";


  @Inject
  private transient Messages messages;

  @Inject
  private transient ImportExportStreamCreator streamCreator;

  @Inject
  private transient KonfigurationLoader konfigurationLoader;

  public String getMessage(String msgKey, Object... params) {
    return messages.getMessageForMessageKey(msgKey, params);
  }

  public BiConsumer<String, EmlWrapper> getHashCodeProperty(FormBuilder formBuilder, AbstractExchangeContext context,
                                                            Function<EmlWrapper, EmlValidation> emlValidationFct) {
    return (hash, emlWrapper) -> {
      context.put(EMLHASHCODE, hash);

      EmlValidation emlValidation = emlValidationFct.apply(emlWrapper);
      context.put(EML_VALIDATION, emlValidation);
      if (emlValidation.isAbfragen()) {
        List<EmlHashCodePart> hashCodeParts = bestimmeHashCodeParts(context, hash);
        formBuilder.addProperty(new ReadOnlyStringProperty(getMessage("eml110.hash.label"), bestimmeLesbareTeile(hashCodeParts, hash)));
        formBuilder.withStyleClass("editBoldText");

        for (EmlHashCodePart hashCodePart : hashCodeParts) {
          formBuilder
              .addProperty(new LambdaStringProperty(hashCodePart.getReplacement(), true, s -> context.put(EMLHASHCODE_PART + hashCodePart.getPosition(), s),
                  () -> context.get(EMLHASHCODE_PART + hashCodePart.getPosition(), String.class), 1, hashCodePart.getText().length()));

          formBuilder.withValidator(new EmlHashValidator(hashCodePart.getText()));
          formBuilder.withStyleClass("editHash");
        }
      } else {
        formBuilder.addProperty(new ReadOnlyStringProperty(getMessage("eml110.hash.label"), hash));
        formBuilder.withStyleClass("editBoldText");
        if (emlValidation.isBestaetigen()) {

          formBuilder.addProperty(new LambdaEnumProperty<>(getMessage("eml.hashcode.confirmed"), true/*eigener validator mit eigener Meldung fuinktioniert
        nicht*/,
              b -> context.put(EMLHASHCODE_CONFIRMED_AUSWAHL, b),
              () -> context.get(EMLHASHCODE_CONFIRMED_AUSWAHL, EmlBestaetigenAuswahl.class), EmlBestaetigenAuswahl.class,
              Collections.singletonList(EmlBestaetigenAuswahl.HASH_CODE_BESTAETIGT), true));
          formBuilder.withValidator(new EmlBestaetigenAuswahlValidator());

        }
      }
    };

  }

  private String bestimmeLesbareTeile(List<EmlHashCodePart> hashCodeParts, String hash) {
    String[] splitted = hash.split(" ");
    return IntStream.range(0, splitted.length).mapToObj(pos -> bestimmeHashCodeTeilText(pos, splitted, hashCodeParts)).collect(Collectors.joining(" "));
  }

  private String bestimmeHashCodeTeilText(int pos, String[] splitted, List<EmlHashCodePart> hashCodeParts) {
    for (EmlHashCodePart hashCodePart : hashCodeParts) {
      if (pos == hashCodePart.getPosition()) {
        return hashCodePart.getReplacement();
      }
    }
    return splitted[pos];
  }

  private List<EmlHashCodePart> bestimmeHashCodeParts(AbstractExchangeContext context, String hash) {

    Object list = context.get(EMLHASHCODE_PARTS, List.class);
    List<EmlHashCodePart> result;
    if (list == null) {

      String[] splitted = hash.split(" ");
      if (splitted.length < 2) {
        throw new IllegalStateException("Hashcode nicht im passenden Format");
      }

      String salt = getSystemId(); // ELECTVAPP-963
      Random random = new Random((hash + salt).hashCode());
      int position1 = random.nextInt(splitted.length);
      int position2 = random.nextInt(splitted.length);

      while (position2 == position1) {
        position2 = random.nextInt(splitted.length);
      }

      result = new ArrayList<>();
      if (position1 < position2) {
        result.add(new EmlHashCodePart(position1, splitted[position1], StringUtils.repeat(getMessage("eml.replace.character.1"), splitted[position1].length())));
        result.add(new EmlHashCodePart(position2, splitted[position2], StringUtils.repeat(getMessage("eml.replace.character.2"), splitted[position1].length())));
      } else {
        result.add(new EmlHashCodePart(position2, splitted[position2], StringUtils.repeat(getMessage("eml.replace.character.1"), splitted[position1].length())));
        result.add(new EmlHashCodePart(position1, splitted[position1], StringUtils.repeat(getMessage("eml.replace.character.2"), splitted[position1].length())));
      }

    } else {
      result = (List<EmlHashCodePart>) list;
    }

    return result;
  }

  private String getSystemId() {
    return konfigurationLoader.resolveStringValue(ElectKonfigurationWerte.SYSTEM_ID);
  }

  public EmlWrapper readEmlNoHash(InputStream is) {
    return new EmlReader().readEMlNoHash(is);

  }
  public EmlWrapper readEmlHash(InputStream is, BiConsumer<String, EmlWrapper> hashCodeConsumer) {
    List<String> hashCodeContainer = new ArrayList<>();
    EmlWrapper emlWrapper = new EmlReader().readEMlHash(is, hashCodeContainer::add);


    if (!hashCodeContainer.isEmpty()) {
      String hashCode = hashCodeContainer.get(0);
      if (emlWrapper!=null && emlWrapper.getContent()!=null) {
        // ELECTVAPP-276
        LOG.info("EML: " + emlWrapper.getContent().getId() + ", hashCode: " + hashCode);
      }
      hashCodeConsumer.accept(hashCode, emlWrapper);
    }

    return emlWrapper;
  }

  public InputStream getInputStream(Datei datei) {
    return datei.createInputStream(streamCreator);
  }

}
