package com.capgemini.reports.common;

import com.capgemini.framework.common.SettingsHelper;
import com.capgemini.framework.common.StringHelper;
import com.capgemini.framework.teamcenter.exceptions.KurtException;
import com.capgemini.reports.database.HibernateUtil;
import com.capgemini.reports.database.javabean.DbReport;
import com.capgemini.reports.database.manager.MngReport;
import com.capgemini.reports.exceptions.InsufficientParametersException;
import com.capgemini.reports.exceptions.NotFoundException;
import com.capgemini.reports.formatting.ErrorPageTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * JSP Servlet for downloading file content from web GUI.
 */
@Slf4j
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
  private static final long serialVersionUID = 4856158363304758345L;

  private static final String OUTPUT_FOLDER = SettingsHelper.getReportLocation();
  private static final String OUTPUT_FOLDER_SCHEDULING_HISTORY = SettingsHelper.getSchedulingHistoryLocation();

  private static final String UNABLE_TO_DOWNLOAD_FILE = "Unable to download the file";

  @Override
  protected void doGet(final HttpServletRequest aRequest, final HttpServletResponse aResponse) throws ServletException, IOException {
    SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session session = sessionFactory.openSession();
    try {
      // Check URL argument
      String theReportUid = getMandatoryArg(aRequest, "reportUid");

      MngReport theMngReport = new MngReport();
      DbReport theReport = theMngReport.searchByUid(theReportUid, session);
      if (theReport == null) {
        throw new NotFoundException("The report has not been found for uid: " + theReportUid);
      }

      // Build file path
      String theFilePathValue = makeFileName(theReport);

      // Secure file path
      theFilePathValue = theFilePathValue.replace("..", "").replace("//", "/");

      // Extract binary content
      downloadFile(aResponse, theFilePathValue, theReport.getFileName());
    }
    catch (KurtException e) {
      String theErrorTitle = UNABLE_TO_DOWNLOAD_FILE;
      log.error(theErrorTitle, e);
      ErrorPageTemplate theErrorTemplate = new ErrorPageTemplate(theErrorTitle, e.getMessage());
      aResponse.getWriter().write(theErrorTemplate.generateCode());
    }
    finally {
      session.close();
    }
  }

  private String makeFileName(final DbReport theReport) throws NotFoundException {
    String[] split = theReport.getFileName().split("\\.");
    if (split.length != 2) {
      throw new NotFoundException("Could not extract file name and extension from DB: " + theReport.getFileName());
    }
    String fileNameNoExtension = split[0];
    String extension = split[1];

    // Define which folder to findAll
    if (theReport.getSchedulingReportId() == null) {
      return StringHelper.concatToString(OUTPUT_FOLDER, "/", fileNameNoExtension, "_", theReport.getReportUid(), ".", extension);
    }
    else {
      return StringHelper.concatToString(OUTPUT_FOLDER_SCHEDULING_HISTORY, "/", fileNameNoExtension, ".", extension);
    }
  }

  /**
   * Try to download a file from the output directory.
   */
  private void downloadFile(final HttpServletResponse aResponse, final String aFilePathValue, final String fileName)
    throws IOException, InsufficientParametersException, NotFoundException {
    final File theSourceFile = new File(aFilePathValue);
    if (!theSourceFile.exists() || theSourceFile.isDirectory()) {
      throw new NotFoundException("The source file does not exist: " + aFilePathValue);
    }

    long theFileSize = theSourceFile.length();
    if (theFileSize == 0) {
      throw new NotFoundException("The source file is empty: " + aFilePathValue);
    }

    try (FileInputStream input = new FileInputStream(theSourceFile); ServletOutputStream out = aResponse.getOutputStream()) {
      aResponse.setContentType("application/download");
      aResponse.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
      aResponse.setContentLength((int) theFileSize);
      IOUtils.copy(input, out);
    }
    catch (IOException e) {
      log.error(UNABLE_TO_DOWNLOAD_FILE, e);
    }
  }

  /**
   * Try to findAll an argument value in the URL and return an error message in JSP when it failed.
   */
  private static String getMandatoryArg(final HttpServletRequest aRequest, final String anArgName) throws ServletException, IOException, InsufficientParametersException {
    String theArgValue = aRequest.getParameter(anArgName);
    if (theArgValue == null) {
      throw new InsufficientParametersException("Missing required argument in URL: " + anArgName);
    }
    return theArgValue;
  }
}
