package com.capgemini.reports.common.scheduling;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.reports.checkdata.CheckingDataHelper;
import com.capgemini.reports.common.ServletMessageHelper;
import com.capgemini.reports.database.DataBaseReportHelper;
import com.capgemini.reports.database.HibernateUtil;
import com.capgemini.reports.database.javabean.DbSchedulingReport;
import com.capgemini.reports.database.javabean.DbUser;
import com.capgemini.reports.database.javabean.DbUser.UserData;
import com.capgemini.reports.database.manager.MngSchedulingCriteriaVal.SchedulingCriteriaValue;
import com.capgemini.reports.database.manager.MngSchedulingReport;
import com.capgemini.reports.database.manager.MngUser;
import com.capgemini.reports.exceptions.*;
import com.capgemini.reports.execution.AbstractExecutionReport;
import com.capgemini.reports.execution.posting.SchedulingPostingStrategy;
import com.capgemini.reports.execution.strategies.ScheduledExecutionStrategy;
import com.capgemini.reports.logging.ReportLoggerHelper;
import com.capgemini.reports.periodicity.SchedulingPeriod;
import com.capgemini.reports.quartz.TriggerPreparator;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.json.JSONObject;
import org.quartz.SchedulerException;
import org.quartz.Trigger;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * Class providing the framework for submitting a new report Scheduling request.<br>
 * Also checks in advance the parameters to produce the scheduled report.<br>
 * <b>Note:</b> The reports generated from a right click on a TC object cannot be scheduled.<br>
 * For examples : R15, R17,...
 */
@Slf4j
@WebServlet("/scheduling/submit")
public class SubmitServletScheduling extends AbstractGenerateScheduledTemplateServlet {

  private static final long serialVersionUID = -7776388563908130383L;

  /**
   * Record a new Scheduling job into Quartz and the DB.
   *
   * @throws NotSupportedException         If the report instance could not be created (not developped yet?)
   * @throws FileLoadingException          If unable to load policies to perform the quick checks
   * @throws InvalidUserInputException     If any report parameter or scheduling parameter could not be understood
   * @throws SchedulerException            if a scheduling operation failed.
   * @throws DuplicateSchedulingEntryFound
   */
  @Override
  protected JSONObject perform(final HttpServletRequest request, final TcSession tcSession)
    throws SchedulerException, NotSupportedException, InvalidUserInputException, FileLoadingException, DuplicateSchedulingEntryFound {
    SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session session = sessionFactory.openSession();
    try {
      String schedulingName = getPlanificationName(request);
      DbSchedulingReport previous = new MngSchedulingReport().searchByName(session, schedulingName);
      if (previous != null) {
        throw new DuplicateSchedulingEntryFound(
          "La planification " + schedulingName + " existe d&eacute;j&agrave;. veuillez saisir un autre nom.");
      }

      // Get the implementation of the report by reflection thanks to his code and the request
      AbstractExecutionReport<?, ?> reportImpl = getExecutionReportImpl(request);

      // Prepare a Session in order to perform the quick checks
      String reportCode = reportImpl.getReportIdentifier().getReportCode();
      log.debug("Beginning quick checks for the report {}...", reportCode);

      // Do a quick Check. If it fails, the exception thrown will carry a message to the user's webpage
      CheckingDataHelper.doQuickCheck(tcSession, reportImpl);
      log.debug("All quick checks have passed for {}", reportCode);

      // Get scheduling parameters
      SchedulingPeriod schedulingPeriod = getSchedulingData(request);

      // Record which admin has asked for this job
      DbSchedulingReport theSchedulingEntry = recordSchedulingRequest(request, reportImpl, schedulingName, schedulingPeriod.asCriteriaValues());

      // Get the trigger thanks to the scheduling parameters from the request
      Trigger trigger;
      try {
        trigger = TriggerPreparator.prepareScheduledTrigger(theSchedulingEntry.getId(), reportImpl.getPriorityCode(), schedulingName,
          schedulingPeriod);
      }
      catch (InvalidInputException e) {
        throw new InvalidUserInputException("Some scheduling parameters could ntot be understood", e);
      }
      // Setup the scheduling parameters
      log.info("Scheduling name={}, id={}", theSchedulingEntry.getSchedulingName(), theSchedulingEntry.getId());
      reportImpl.setScheduling(theSchedulingEntry.getId(), theSchedulingEntry.getSchedulingName());
      reportImpl.setExecutionStrategy(new ScheduledExecutionStrategy());

      // POST the report
      new SchedulingPostingStrategy(schedulingServer, trigger).handleRequest(reportImpl);
      return successMessage(reportImpl);
    }
    finally {
      session.close();
    }
  }

  private DbSchedulingReport recordSchedulingRequest(final HttpServletRequest request, final AbstractExecutionReport<?, ?> reportImpl,
                                                     final String schedulingName, final List<SchedulingCriteriaValue> schedulingCriteriaValues) {
    SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session session = sessionFactory.openSession();
    try {
      // Identify the user using the LDAP
      UserData adUser = identifyUser(request);

      // Find / update / create corresponding user in DB if it doesn't already exist
      DbUser dbUser = new MngUser().fetchOrUpdateOrCreateUser(session, adUser);

      // Record the report launch in the database
      DbSchedulingReport theSchedulingEntry = DataBaseReportHelper.registerScheduledTemplate(reportImpl, dbUser, schedulingCriteriaValues, schedulingName);

      // Log the scheduling request
      ReportLoggerHelper.logSchedulingRequestWaiting(session, dbUser, theSchedulingEntry, reportImpl);
      return theSchedulingEntry;
    }
    finally {
      session.close();
    }
  }

  private JSONObject successMessage(final AbstractExecutionReport<?, ?> reportImpl) {
    return ServletMessageHelper.successMessage("Le rapport \"" + reportImpl.getReportIdentifier().getReportTitle() + "\" est planifié");
  }
}
