
package de.ivu.elect.business.dokumente.boundary;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.annotation.Nonnull;
import javax.ejb.Stateless;
import javax.inject.Inject;

import org.apache.commons.io.IOUtils;

import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.dokumente.entity.DokumentVorlage;
import de.ivu.elect.business.dokumente.entity.DokumentVorlageArt;
import de.ivu.elect.business.dokumente.entity.QDokumentVorlage;
import de.ivu.elect.business.exchange.control.ImportExportStreamCreator;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.security.UseCaseSecure;
import de.ivu.elect.business.wahl.boundary.WahlterminExtendedDAO;
import de.ivu.elect.business.wahl.control.WahlExtendedAttacher;
import de.ivu.elect.jee.entity.EagerLoadingCollector;
import de.ivu.elect.jee.entity.QueryDsl;


import static org.apache.commons.lang3.StringUtils.isNotBlank;

@Stateless
@UseCaseSecure(UseCasePermission.DOKUMENT_VORLAGE_SEARCH)
public class DokumentVorlageSearch extends AbstractDokumentVorlageSearch {

  private static final int BUFFER_IN_BYTES = 1000 * 1000 * 10;

  @Inject
  private transient WahlExtendedAttacher wahlDao;

  @Inject
  private transient WahlterminExtendedDAO wahlterminDao;

  @Inject
  private transient ImportExportStreamCreator streamCreator;

  public DokumentVorlageSearch() {
    super();
  }

  @Override
  protected List<DokumentVorlage> querySearchResult(DokumentVorlageSearchParameters searchParameters) {
    if (searchParameters.getWahl() == null && searchParameters.getWahltermin() == null) {
      return Collections.emptyList();
    }

    QDokumentVorlage dokumentVorlage = QDokumentVorlage.dokumentVorlage;

    QueryDsl queryDsl = createQueryDslWahlWahltermin(searchParameters, dokumentVorlage);

    queryDsl.eqIfNotNull(dokumentVorlage.dokumentVorlageArt, searchParameters.getDokumentVorlageArt());
    queryDsl.eqIfNotNull(dokumentVorlage.anlageArt, searchParameters.getDokumentVorlageAnlageArt());
    queryDsl.likeIfNotBlank(dokumentVorlage.versionsnummer, searchParameters.getVersionsnummer());
    queryDsl.likeIfNotBlank(dokumentVorlage.name, searchParameters.getName());

    return executeQuery(dokumentVorlage, queryDsl);
  }

  @Nonnull
  private List<DokumentVorlage> executeQuery(QDokumentVorlage dokumentVorlage, QueryDsl queryDsl) {
    queryDsl.orderBy(dokumentVorlage.dokumentVorlageArt.asc(), dokumentVorlage.name.asc());

    List<DokumentVorlage> result = queryDsl.list(dokumentVorlage);

    // NOTE ELECTDD-327, dass es ohne dieses 'Vorladen' zu einer LazyInitializationException beim Navigieren in der GUI per Pagination kommt!
    for (DokumentVorlage entry : result) {
      entry.getDateiname();
    }

    return result;
  }

  @Nonnull
  private QueryDsl createQueryDslWahlWahltermin(DokumentVorlageSearchParameters searchParameters, QDokumentVorlage dokumentVorlage) {
    QueryDsl queryDsl = dao.getQueryDsl().from(dokumentVorlage);

    if (searchParameters.getWahl() == null) {
      queryDsl.eq(dokumentVorlage.wahltermin, wahlterminDao.attach(searchParameters.getWahltermin()));
    } else {
      queryDsl.eq(dokumentVorlage.wahl, wahlDao.attach(searchParameters.getWahl()));
    }
    return queryDsl;
  }

  public List<DokumentVorlage> getSearchResultLeerformulare(DokumentVorlageSearchParameters searchParameters,
                                                            EagerLoadingCollector<DokumentVorlage> eagerLoadingCollector) {
    QDokumentVorlage dokumentVorlage = QDokumentVorlage.dokumentVorlage;
    QueryDsl queryDsl = createQueryDslWahlWahltermin(searchParameters, dokumentVorlage);
    queryDsl.eq(dokumentVorlage.dokumentVorlageArt, DokumentVorlageArt.LEERFORMULAR);
    return executeQuery(dokumentVorlage, queryDsl);
  }

  public byte[] getDokumentVorlagenAlsZip(List<DokumentVorlage> unattchedVorlagen) {
    ByteArrayOutputStream bos = new ByteArrayOutputStream(BUFFER_IN_BYTES);
    try (ZipOutputStream zos = new ZipOutputStream(bos)) {
      Set<String> verwendetetNamen = new HashSet<>();
      AtomicLong counter = new AtomicLong(0L);
      for (DokumentVorlage dokumentVorlage : dao.attachAll(unattchedVorlagen)) {
        zos.putNextEntry(new ZipEntry(filterDoppelte(bestimmeDateiname(dokumentVorlage), verwendetetNamen, counter)));
        try (InputStream is = dokumentVorlage.getDatei().createInputStream(streamCreator)) {
          IOUtils.copy(is, zos);
        }
        zos.closeEntry();
      }
      zos.finish();
      zos.flush();
      zos.close();
    } catch (IOException e) {
      throw new RuntimeException(e.getMessage(), e);
    }

    return bos.toByteArray();
  }

  /**
   * ELECT-4557
   */
  private String filterDoppelte(String s, Set<String> verwendetetNamen, AtomicLong counter) {
    if (verwendetetNamen.add(s)) {
      return s;
    }

    return filterDoppelte(counter.addAndGet(1L) + "_" + s, verwendetetNamen, counter);
  }

  private String bestimmeDateiname(DokumentVorlage dokumentVorlage) {
    String originalDateiname = dokumentVorlage.getDateinameOriginal();

    if (isNotBlank(originalDateiname)) {
      return originalDateiname;
    }

    return DokumentVorlageArt.getFileName(dokumentVorlage, FileType.ODT);
  }
}
