package com.cloudera.cmf.service.config;

import com.cloudera.cmf.model.DbConfig;
import com.cloudera.cmf.service.ServiceHandlerRegistry;
import com.cloudera.cmf.service.Validation;
import com.cloudera.cmf.service.Validation.ValidationState;
import com.cloudera.cmf.service.ValidationContext;
import com.cloudera.cmf.tsquery.InvalidTsqueryFunction;
import com.cloudera.cmf.tsquery.QueryException;
import com.cloudera.cmf.tsquery.TsqueryUtils;
import com.cloudera.cmon.AlarmConfig;
import com.cloudera.cmon.AlarmConfig.ExpressionEditorConfig;
import com.cloudera.cmon.AlarmConfigException;
import com.cloudera.enterprise.JsonUtil;
import com.cloudera.enterprise.JsonUtil.JsonRuntimeException;
import com.cloudera.enterprise.MessageWithArgs;
import com.cloudera.server.web.cmf.view.ViewBinder;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.codehaus.jackson.type.TypeReference;
import org.drools.core.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AlarmConfigParamSpec extends ParagraphParamSpec
{
  private static Logger LOG = LoggerFactory.getLogger(AlarmConfigParamSpec.class);

  public static Builder<?> builder()
  {
    return new Builder();
  }

  protected AlarmConfigParamSpec(Builder<?> builder)
  {
    super(builder);
  }

  protected Collection<Validation> validate(ServiceHandlerRegistry shr, ValidationContext context, Object value)
  {
    Preconditions.checkNotNull(shr);
    String stringVal = (String)value;
    Collection validations = super.validate(shr, context, value);
    for (Validation validation : validations)
      if (validation.getState() == Validation.ValidationState.ERROR)
        return validations;
    Validation v;
    try
    {
      validateAlarams(context, stringVal);
      v = Validation.check(context);
    }
    catch (AlarmConfigException ex)
    {
      v = Validation.warning(context, MessageWithArgs.of(ex.getMessage(), new String[0]));
    } catch (JsonUtil.JsonRuntimeException ex) {
      LOG.warn("Failed to parse JSON for triggers configuration", ex);
      v = Validation.warning(context, MessageWithArgs.of("message.bad_alarms_json", new String[0]));
    }
    catch (DuplicateAlarmNameException ex) {
      LOG.warn("Duplicate trigger name encountered", ex);
      v = Validation.warning(context, MessageWithArgs.of("message.duplicate_alarm_name", new String[] { ex.getAlarmName() }));
    }
    catch (InvalidTsqueryFunction ex)
    {
      v = Validation.warning(context, MessageWithArgs.of(ex.getMessage(), new String[0]));
    }
    return Collections.singleton(v);
  }

  private String getEntityParamName(ValidationContext context)
  {
    Preconditions.checkNotNull(context);
    String attr = context.getConfig().getAttr();
    if (attr.equals("host_triggers"))
      return "$HOSTID";
    if (attr.equals("role_triggers")) {
      return "$ROLENAME";
    }
    Preconditions.checkArgument(attr.equals("service_triggers"));
    return "$SERVICENAME";
  }

  private void validateAlarams(ValidationContext context, String value)
    throws AlarmConfigException, DuplicateAlarmNameException
  {
    Preconditions.checkNotNull(context);
    Preconditions.checkNotNull(value);
    String entityParaName = getEntityParamName(context);

    Set namesEncountered = Sets.newHashSet();

    List alarmConfigs = (List)JsonUtil.valueFromString(new TypeReference()
    {
    }
    , value);

    for (AlarmConfig config : alarmConfigs) {
      if (StringUtils.isEmpty(config.getTriggerName())) {
        throw AlarmConfigException.newMissingAlarmName();
      }
      if (StringUtils.isEmpty(config.getTriggerExpression())) {
        throw AlarmConfigException.newMissingTriggerExpression(config.getTriggerName());
      }

      if (config.getStreamThreshold() < 0) {
        throw AlarmConfigException.newBadAlarmConfigThreshold(config.getTriggerName());
      }

      if (namesEncountered.contains(config.getTriggerName())) {
        throw new DuplicateAlarmNameException(config.getTriggerName());
      }
      if (config.getValidityWindowInMs() <= 0L) {
        throw AlarmConfigException.newInvalidValidityWindow(config.getTriggerName());
      }
      namesEncountered.add(config.getTriggerName());

      if (config.getExpressionEditorConfig() != null) {
        String editorTriggerExpression = config.getExpressionEditorConfig().getExpression(entityParaName);

        String editorTriggerExpressionV1 = config.getExpressionEditorConfig().getExpressionV1(entityParaName);

        if ((!config.getTriggerExpression().equals(editorTriggerExpression)) && (!config.getTriggerExpression().equals(editorTriggerExpressionV1)))
        {
          throw AlarmConfigException.newConflictingAlarmExpression(config.getTriggerName());
        }

      }

      try
      {
        TsqueryUtils.getParsedAlarm(ViewBinder.dummyBind(config.getTriggerExpression()));
      }
      catch (QueryException ex) {
        LOG.info("Bad syntax for trigger " + config.getTriggerName() + ". Expression " + config.getTriggerExpression() + " could not " + "be parsed: " + ex.getMessage());

        throw AlarmConfigException.newBadAlarmSyntax(config.getTriggerName());
      } catch (InvalidTsqueryFunction ex) {
        LOG.info("Invalid tsquery function used for trigger: " + config.getTriggerName() + ". Error:" + ex.getMessage());

        throw ex;
      }
    }
  }

  public static class Builder<S extends Builder<S>> extends ParagraphParamSpec.Builder<Builder<S>>
  {
    public AlarmConfigParamSpec build()
    {
      return new AlarmConfigParamSpec(this);
    }
  }
}