
package de.ivu.elect.business.praesentation.control;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Optional;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;

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

import de.ivu.elect.business.datei.entity.BinaryUnit;
import de.ivu.elect.business.praesentation.entity.PraesentationPathInfo;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.jee.servlet.ByteArrayStaticResource;
import de.ivu.elect.jee.servlet.FileStaticResource;
import de.ivu.elect.jee.servlet.StaticResource;
import de.ivu.elect.jee.servlet.StaticResourceServlet;

@WebServlet(PraesentationStaticResourceServlet.PATTERN)
public class PraesentationStaticResourceServlet extends StaticResourceServlet {

  private static final long serialVersionUID = 1L;

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

  private static final int BUFFER_SIZE_IN_BYTES = BinaryUnit.bytesOf(8, BinaryUnit.KIBIBYTE);

  public static final String PATH = "/static/praesentation/";

  public static final String PATTERN = PATH + "*";

  @Inject
  private transient PraesentationServletPathFinder praesentationServletPathFinder;

  @Inject
  private transient Messages messages;

  @Inject
  private transient PraesentationStaticResourceServletSecurity praesentationStaticResourceServletSecurity;

  public PraesentationStaticResourceServlet() {
    super();
  }

  @Nullable
  @Override
  protected StaticResource getStaticResource(HttpServletRequest request) throws IllegalArgumentException {
    String pathInfoVollstaendig = request.getPathInfo();

    if (pathInfoVollstaendig == null || pathInfoVollstaendig.isEmpty() || "/".equals(pathInfoVollstaendig)) {
      throw new IllegalArgumentException();
    }

    PraesentationPathInfo praesentationPathInfo = new PraesentationPathInfo(pathInfoVollstaendig);
    if (StringUtils.isBlank(praesentationPathInfo.getResource())) {
      return null;
    }


    if (PraesentationStaticResourceServletSecurity.PRESENTATION_SERVLET_STATUS.isDeaktiviert()) {
      return null;
    }

    if (PraesentationStaticResourceServletSecurity.PRESENTATION_SERVLET_STATUS.isAnmeldungNotwendig()) {

      if (!praesentationStaticResourceServletSecurity.checkStatus(PraesentationStaticResourceServletSecurity.PRESENTATION_SERVLET_STATUS)){
        return null;
      }
    }

    String name;
    try {
      name = URLDecoder.decode(praesentationPathInfo.getResource().substring(1), StandardCharsets.UTF_8.name());
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e.getMessage(), e);
    }

    StaticResource result;
//    if (praesentationServletPathFinder.isAblageverzeichnisKonfiguriert(praesentationPathInfo)) {
//      // Legacy verhalten
//      String folderName = praesentationServletPathFinder.getPfadFuerWahl(praesentationPathInfo);
//      if (StringUtils.isBlank(folderName)) {
//        return null;
//      }
//
//      File preasentationFolder = new File(folderName);
//
//      boolean praesentationAlsZip = praesentationServletPathFinder.isPraesentationAlsZipKonfiguriert(praesentationPathInfo);
//
//      File file = new File(preasentationFolder, name);
//
//      if (praesentationAlsZip) {
//        result = zipFirst(name, preasentationFolder, file);
//      } else {
//        result = fileFirst(name, preasentationFolder, file);
//      }
//
//    } else {
      File zipFile = praesentationServletPathFinder.getLatestExportFile(praesentationPathInfo);
      if (zipFile == null) {
        result = null;
      } else {
        result = getStaticResourceFromZip(name, zipFile);
      }
//    }

    if (result == null) {
      if (name.toLowerCase().endsWith("html")) {
        result = new ByteArrayStaticResource(name, System.currentTimeMillis(),
            messages.getMessageForMessageKey("presentation.html.not.found", StringEscapeUtils.escapeHtml4(name)).getBytes(StandardCharsets.UTF_8));
      }
    }

    return result;
  }

  private StaticResource fileFirst(String name, File preasentationFolder, File file) {
    StaticResource result = getFromFile(file);
    if (result == null) {
      return getFromZip(name, preasentationFolder);
    } else {
      return result;
    }
  }

  private StaticResource zipFirst(String name, File preasentationFolder, File file) {
    StaticResource result = getFromZip(name, preasentationFolder);
    if (result == null) {
      return getFromFile(file);
    } else {
      return result;
    }
  }

  private StaticResource getFromFile(File file) {
    if (file.exists()) {
      return new FileStaticResource(file);
    }
    return null;
  }

  @Nullable
  private StaticResource getFromZip(String name, File preasentationFolder) {
    StaticResource result = null;
    // ELECT-6403 Zip-File?
    File[] files = preasentationFolder.listFiles();
    if (files != null) {
      Optional<File> latestZip = Arrays.stream(files).filter(File::isFile).filter(f -> f.getName().toLowerCase().endsWith(".zip")).min((v, u) -> u.getName().compareTo(v.getName()));

      if (latestZip.isPresent()) {
        result = getStaticResourceFromZip(name, latestZip.get());
      }
    }
    return result;
  }

  private StaticResource getStaticResourceFromZip(String name, File zip) {
    StaticResource result;
    Path zipfile = Paths.get(zip.getAbsolutePath());
    try (FileSystem fileSystem = FileSystems.newFileSystem(zipfile, (ClassLoader) null)) {
      Path fileToExtract = fileSystem.getPath(name);
      ByteArrayOutputStream bos = new ByteArrayOutputStream(BUFFER_SIZE_IN_BYTES);
      Files.copy(fileToExtract, bos);
      result = new ByteArrayStaticResource(name, zip.lastModified(), bos.toByteArray());
    } catch (IOException e) {
      LOG.warn("Ressource '" + name + "' nicht in ZIP-Datei '" + zip + "' gefunden: " + e.getMessage());
      result = null;
    }
    return result;
  }

}
