
package de.ivu.elect.core.exchange.wahl;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.lowerCase;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.UnaryOperator;

import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.NlExportFileNames;
import de.ivu.elect.business.datei.entity.AbstractDateiFileNames;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.NlGebietsart;
import de.ivu.elect.business.wahl.control.AbstractNlExportArtefaktNachSystemExportstatusExportContext;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import de.ivu.elect.presentation.exchange.FileFormatByMessageAndTypeComparator;

public class NlExportArtefaktNachSystemExportstatusFileNames extends AbstractDateiFileNames {

  private static final UnaryOperator<String> FILE_NAME_PART_NORMALIZER = part -> lowerCase(RegExUtils.replaceAll(trimToEmpty(part), "\\s+", "-"));

  private final List<FileFormat> fileFormatsZipped;

  private final String wurzelgebietName;

  private final String wurzelgebietNameNormalized;

  private final NlGebietsart wurzelgebietGebietsart;

  private final String wurzelgebietGebietsartName;

  private final String wurzelgebietGebietsartNameNormalized;

  public NlExportArtefaktNachSystemExportstatusFileNames(AbstractNlExportArtefaktNachSystemExportstatusExportContext context, AbstractGebietDAO gebietDAO, Collection<FileFormat> fileFormatsZipped) {
    super(context.getMessages());

    Set<FileFormat> fileFormatsZippedUnique = new HashSet<>(fileFormatsZipped);

    this.fileFormatsZipped = new ArrayList<>(fileFormatsZippedUnique);
    this.fileFormatsZipped.sort(new FileFormatByMessageAndTypeComparator(context.getLocale(), context.getMessages()));

    Gebiet wurzelgebiet = context.getWurzelgebiet(gebietDAO);
    this.wurzelgebietName = wurzelgebiet.getName();
    this.wurzelgebietNameNormalized = FILE_NAME_PART_NORMALIZER.apply(this.wurzelgebietName);

    this.wurzelgebietGebietsart = NlExportFileNames.getRegionType(wurzelgebiet);
    this.wurzelgebietGebietsartName = NlGebietsart.UNBEKANNT.equals(this.wurzelgebietGebietsart) ? wurzelgebiet.getGebietsart().getName() : this.wurzelgebietGebietsart.name();
    this.wurzelgebietGebietsartNameNormalized = FILE_NAME_PART_NORMALIZER.apply(this.wurzelgebietGebietsartName);
  }

  @Override
  public String getOriginalDateiname(FileFormat fileFormat, Wahl wahl, Wahltermin wahltermin) {
    String simpleFileName = getFileNamePrefix(fileFormat, wahl);

    simpleFileName += DELIMITER;
    simpleFileName += getCurrentDateFormatted(DATE_PATTERN_EXTERN);

    return normalize(simpleFileName, false, fileFormat.getFileType());
  }

  @Override
  public String getDateiname(FileFormat fileFormat, Wahl wahl, Wahltermin wahltermin) {
    return generateDateiname(fileFormat, getFileNamePrefix(fileFormat, wahl), true);
  }

  // ELECTVAPP-225, ELECTVAPP-1039
  private String getFileNamePrefix(FileFormat fileFormat, Wahl wahl) {
    String result = "";

    String fileFormatName = FILE_NAME_PART_NORMALIZER.apply(getMessage(fileFormat.getName()));

    if (isFileFormatNameIgnored()) {
      int fileFormatsZippedCount = fileFormatsZipped.size();

      if (fileFormatsZippedCount > 0) {
        FileFormat first = fileFormatsZipped.get(0);

        result += defaultIfBlank(formatFileFormatsNames(singletonList(first)), fileFormatName);

        if (fileFormatsZippedCount > 1) {
          List<FileFormat> more = fileFormatsZipped.subList(1, fileFormatsZippedCount);

          result += " ";
          result += formatFileFormatsShortNames(more);
        }
      }
    } else {
      if (isFileFormatsZippedShortNamesIgnored()) { // ELECTVAPP-1533
        result += fileFormatName;
      } else {
        String fileFormatsZippedShortNames = formatFileFormatsShortNames(fileFormatsZipped);

        if (!equalsIgnoreCase(fileFormatName, fileFormatsZippedShortNames)) { // ELECT-10681
          result += fileFormatName;
          result += " ";
        }

        result += fileFormatsZippedShortNames;
      }
    }

    result += " ";
    result += FILE_NAME_PART_NORMALIZER.apply(wahl.getKennzeichen());

    if (!NlGebietsart.STAAT.equals(wurzelgebietGebietsart)) {
      result += " ";
      result += wurzelgebietGebietsartNameNormalized;

      result += " ";
      result += wurzelgebietNameNormalized;
    }

    return result;
  }

  private boolean isFileFormatNameIgnored() {
    return fileFormatsZipped.size() < 2 //
        || containsOnly(fileFormatsZipped, "file.format.ergebnisdokument.export.modelo3.main", "file.format.ergebnisdokument.export.modelo3.bijlage1") // ELECTVAPP-1297
        || containsOnly(fileFormatsZipped, "file.format.ergebnisdokument.export.model2.main", "file.format.ergebnisdokument.export.model2.bijlage1") // ELECTVAPP-1287
    ;
  }

  private boolean isFileFormatsZippedShortNamesIgnored() {
    return true; // ELECTVAPP-1533
  }

  private boolean containsOnly(List<FileFormat> fileFormatsZipped, String one, String... more) {
    Set<String> actual = fileFormatsZipped.stream().map(FileFormat::getName).collect(toSet());

    Set<String> expected = new HashSet<>();
    expected.add(one);
    expected.addAll(asList(more));

    return actual.size() == expected.size() && actual.containsAll(expected);
  }

  private String formatFileFormatsNames(List<FileFormat> fileFormats) {
    return collectFileFormatNames(fileFormats, FileFormat::getName).stream().collect(joining(" "));
  }

  private String formatFileFormatsShortNames(List<FileFormat> fileFormats) {
    return collectFileFormatNames(fileFormats, fileFormat -> existsMessageKey(fileFormat.getName() + ".short") ? fileFormat.getName() + ".short" : fileFormat.getName()).stream().collect(joining(" "));
  }

  private List<String> collectFileFormatNames(List<FileFormat> fileFormats, Function<FileFormat, String> fileFormatNameFct) {
    return fileFormats.stream() //
        .map(fileFormatNameFct) //
        .map(this::getMessage) //
        .map(FILE_NAME_PART_NORMALIZER::apply) //
        .filter(StringUtils::isNotBlank) //
        .distinct().collect(toList());
  }
}
