package com.capgemini.reports.auto;

import com.capgemini.framework.common.SettingsHelper;
import com.capgemini.framework.common.TeamcenterDateFormatter;
import com.capgemini.framework.common.activedirectory.ActiveDirectory;
import com.capgemini.framework.teamcenter.exceptions.KurtException;
import com.capgemini.reports.database.DataBaseReportHelper;
import com.capgemini.reports.database.HibernateUtil;
import com.capgemini.reports.database.javabean.DbReport;
import com.capgemini.reports.database.javabean.DbUser;
import com.capgemini.reports.database.javabean.DbUser.UserData;
import com.capgemini.reports.database.manager.MngCriteriaVal.CriteriaValue;
import com.capgemini.reports.database.manager.MngUser;
import com.capgemini.reports.exceptions.InsufficientParametersException;
import com.capgemini.reports.exceptions.InvalidActiveDirectorySearchException;
import com.capgemini.reports.exceptions.InvalidInputException;
import com.capgemini.reports.exceptions.NoConnectionToActiveDirectoryException;
import com.capgemini.reports.execution.AbstractExecutionReport;
import com.capgemini.reports.logging.ReportLoggerHelper;
import com.capgemini.reports.queuing.EmbeddedServer;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import javax.jms.JMSException;
import javax.naming.NamingException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * An abstract {@link HttpServlet} for classes that launch a report from an automatic Workflow request.<br>
 * This servlet expects the Tc workflow owner to be passed as parameter: <code>http:\\[URL]?[parameters]&userID=toto[&...]</code>
 *
 * @param <T> the exact {@link AbstractExecutionReport} report type
 */
@Slf4j
public abstract class AbstractAutoLaunchServlet<T extends AbstractExecutionReport<T, ?>> extends HttpServlet {

  private static final long serialVersionUID = -8586399099581830304L;

  private static final String URL_PARAMETER_OWNER_LOGIN = "userID";

  private static final boolean DEV_MODE = SettingsHelper.isDevMode();

  @Override
  protected void doGet(final HttpServletRequest aRequest, final HttpServletResponse aResponse) throws IOException {
    String relaunchURL = makeRelaunchURL(aRequest);
    SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session session = sessionFactory.openSession();
    try {
      // Register owner
      final DbUser dbUser = retrieveUserInAdAndDb(session, aRequest);

      // Generate the report definition
      T report = processFunctionalParams(aRequest);

      // Prepare execution roadmap
      setupSchedulingStrategy(relaunchURL, report);

      // Prepare the report execution parameters
      List<CriteriaValue> launchData = report.getLaunchData();
      String reportCode = report.getReportIdentifier().getReportCode();
      log.info("Server received a request to perform a {} report from WKF, owned by user {} with parameters: {}", reportCode, dbUser.getLogin(),
        launchData);

      // Register current time as the posting time
      report.setPostingTime(TeamcenterDateFormatter.getCurrentTime());

      // Post the report into the queues
      EmbeddedServer.produce(report);

      // Record the report launch in the database, and log this
      DbReport theReportEntry = DataBaseReportHelper.registerLaunch(report, dbUser);

      // Log the report request (AFTER the email is sent, so that the report knows its startTime)
      ReportLoggerHelper.logRequest(dbUser, theReportEntry, report);
      // Normal processing termination, return success code
      aResponse.setStatus(HttpServletResponse.SC_NO_CONTENT); // Successful, but no content to return
    }
    catch (KurtException | JMSException | NamingException e) {
      aResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); // 400 Bad Request
      aResponse.getWriter().write(e.getMessage());
      log.warn(e.getMessage(), e);
    }
    finally {
      try {
        session.close();
      }
      catch (HibernateException e) {
        log.error(e.getMessage(), e);
      }
    }
  }

  protected abstract void setupSchedulingStrategy(String relaunchURL, T report);

  private String makeRelaunchURL(HttpServletRequest aRequest) {
    StringBuffer requestURL = aRequest.getRequestURL();
    String queryString = aRequest.getQueryString();
    String fullURL;
    if (queryString == null) {
      fullURL = requestURL.toString();
    }
    else {
      fullURL = requestURL.append('?').append(queryString).toString();
    }
    return fullURL;
  }

  @Override
  protected void doPost(final HttpServletRequest aRequest, final HttpServletResponse aResponse) throws IOException {
    doGet(aRequest, aResponse);
  }

  /**
   * Retrieve/update the DB User.<br>
   * Steps followed:
   * <ol>
   * <li>fetch the user UID in the URL</li>
   * <li>Obtain the TC object 'User' with this UID</li>
   * <li>Read user login from Tc user</li>
   * <li>Request ActiveDirectory for Data about this login (email, names etc.)</li>
   * <li>Fetch/Update/Create the user in the DB accordingly.</li>
   * </ol>
   */
  private DbUser retrieveUserInAdAndDb(Session session, final HttpServletRequest aRequest) throws InsufficientParametersException,
    NoConnectionToActiveDirectoryException, InvalidActiveDirectorySearchException, InvalidInputException {
    // Parse user ID
    String ownerLogin = aRequest.getParameter(URL_PARAMETER_OWNER_LOGIN);
    if (ownerLogin == null || ownerLogin.isEmpty()) {
      throw new InsufficientParametersException("No user UID was provided in URL");
    }

    // Find this same login in the ActiveDirectory
    UserData adUser;
    if (DEV_MODE) {
      adUser = ActiveDirectory.getGuestUser();
    }
    else {
      adUser = ActiveDirectory.obtainUserData(ownerLogin);
    }

    // Find / update / create corresponding user in DB
    return new MngUser().fetchOrUpdateOrCreateUser(session, adUser);
  }

  /**
   * Generates a report according the the request.
   *
   * @param aRequest the {@link HttpServletRequest}
   * @return a report Execution
   * @throws InsufficientParametersException
   */
  protected abstract T processFunctionalParams(final HttpServletRequest aRequest) throws InsufficientParametersException;

}
