package com.capgemini.reports.queuing.consumer;

import com.capgemini.framework.common.SettingsHelper;
import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.exceptions.NoTeamcenterConnectionException;
import com.capgemini.reports.exceptions.InvalidUserInputException;
import com.capgemini.reports.exceptions.NoConfChangesException;
import com.capgemini.reports.execution.AbstractExecutionReport;
import org.jboss.ejb3.annotation.ResourceAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;

/**
 * This class represents a generic Consumer of messages.<br>
 * It is a Message-Driven Bean (MDB) whose properties are also defined in ejb-jar.xml (in the war's WEB-INF folder) .
 */
@MessageDriven(name = "jobConsumer", activationConfig = {
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "/queues/kurtQueue"),
    @ActivationConfigProperty(propertyName = "consumerWindowSize", propertyValue = "0")})
@ResourceAdapter("hornetq-ra.rar")
public class ReportConsumer implements MessageListener {
  private static final Logger LOGGER = LoggerFactory.getLogger(ReportConsumer.class);

  /**
   * This method processes incoming messages containing reports to be performed. <br>
   *
   * @param message a {@link Message} containing an {@link AbstractExecutionReport} to perform.
   */
  @Override
  public void onMessage(final Message message) {
    AbstractExecutionReport<?, ?> report;

    try {
      report = obtainReport(message);
    }
    catch (UnsupportedOperationException e) {
      LOGGER.warn("Unable to handle report request, dropping the request", e);
      return;
    }

    // If CLEANUP is set, ignore the incoming reports. Treat them as a Technical errors in the DB.
    if (SettingsHelper.isCleanup()) {
      final String optionalStartTime = report.getStartTime() != null ? " Start:" + report.getStartTime() : "";
      LOGGER.error("[CLEANUP] {} {}", report.getReportTechnicalUid(), optionalStartTime);
      report.getExecutionStrategy().handleTechnicalError(report, new Throwable("Report cleaned up"));
      return;
    }

    try (TcSession session = new TcSession()) {
      session.login();
      report.doStart(session);
    }
    catch (NoTeamcenterConnectionException e) {
      // Unable to create a connection to the server dispatcher in the first place
      report.getExecutionStrategy().handleTechnicalError(report, e);
    }
    catch (NoConfChangesException e) {
      report.getExecutionStrategy().handleFunctionalError(report, e);
    }
    catch (InvalidUserInputException e) {
      report.getExecutionStrategy().handleFunctionalError(report, e);
    }
    catch (Exception e) {
      LOGGER.error("Exception encountered", e);
      // This Throwable is caught in order for this message to NOT be re-posted immediately and indefinitely by HornetQ, who thinks the message has not been delivered correctly!
      report.getExecutionStrategy().handleTechnicalError(report, e);
    }
  }

  private AbstractExecutionReport<?, ?> obtainReport(final Message message) {
    if (!(message instanceof ObjectMessage)) {
      throw new UnsupportedOperationException("onMessage() received an unhandled message: " + message.getClass().getName() + ", expecting an ObjectMessage");
    }
    try {
      Object object = ((ObjectMessage) message).getObject();
      if (!(AbstractExecutionReport.class.isInstance(object))) {
        throw new UnsupportedOperationException("onMessage() received an unhandled object inside the message: " + object.toString() + " of type " + object.getClass().getName() + ", expecting an AbstractExecutionReport");
      }
      else {
        // message.acknowledge(); <-- No need because the queue was set up using AUTO_ACKNOWLEDGE
        return (AbstractExecutionReport<?, ?>) object;
      }
    }
    catch (JMSException e) {
      throw new UnsupportedOperationException("onMessage() could not obtain the Object packed inside the message", e);
    }
  }
}
