import * as cdk from "@aws-cdk/core";
import * as cw from "@aws-cdk/aws-cloudwatch";
import { Duration } from "@aws-cdk/core";
import * as sns from "@aws-cdk/aws-sns";
import * as cw_actions from "@aws-cdk/aws-cloudwatch-actions";
import { AlarmSNSTopics } from "./alarm-sns-topics";

/**
 * Interface for creating CloudWatch alarms that alarm if Autohost Worker stops
 * hosting or unhosting.
 */
export interface WorkerAlarmsProps {
  /**
   * The SNS topics for Cloudwatch alarms.
   */
  readonly cloudwatchAlarmSNSTopics: AlarmSNSTopics;

  /**
   * The "Stage" telemetry CloudWatch metrics dimension that identifies Autohost Worker's environment.
   * E.g. staging, production
   */
  readonly stageDimension: string;
}

interface ActionStoppedAlarmProps {
  readonly enabled: boolean;
  readonly region: string;
  readonly snsTopic: sns.ITopic;
  readonly alarmID: string;
  readonly alarmDescription: string;
  readonly dependencyDimension: string;
  readonly evaluationPeriods: number;
  readonly stageDimension: string;
  readonly operationDimension: string;
}

/**
 * WorkerAlarms creates CloudWatch alarms that alarm if Autohost Worker stops
 * hosting or unhosting.
 */
export class WorkerAlarms extends cdk.Construct {
  constructor(scope: cdk.Construct, id: string, props: WorkerAlarmsProps) {
    super(scope, id);

    const stack = cdk.Stack.of(this);
    const region = stack.region;

    if (!props.cloudwatchAlarmSNSTopics.nonCritical) {
      throw "nonCritical topic not defined";
    }
    const nonCriticalTopic: sns.ITopic =
      props.cloudwatchAlarmSNSTopics.nonCritical;

    const common = {
      enabled: true,
      region,
      snsTopic: nonCriticalTopic, // Each alert is >= sev 2.5 and should go to the non-critical topic.
      stageDimension: props.stageDimension
    };
    const alarmProps: ActionStoppedAlarmProps[] = [
      {
        ...common,
        alarmID: "StreamUpUnhostStoppedAlarm",
        alarmDescription:
          "Autohost Worker has stopped unhosting channels that have gone live. (Sev 2.5)",
        dependencyDimension: "twitchclient/Clue:Unhost",
        evaluationPeriods: 10,
        operationDimension: "UpdateLiveChannels"
      }
    ];

    for (let alarmProp of alarmProps) {
      this.addActionStoppedAlarm(alarmProp);
    }
  }

  addActionStoppedAlarm(props: ActionStoppedAlarmProps) {
    const period = Duration.minutes(1);
    const sumMetric = new cw.Metric({
      metricName: "DependencySuccess",
      namespace: "Autohost Worker",
      period,
      statistic: "Sum",
      dimensions: {
        Dependency: props.dependencyDimension,
        Operation: props.operationDimension,
        Region: props.region,
        Service: "Autohost Worker",
        Stage: props.stageDimension,
        Substage: "primary"
      }
    });
    const filledSumMetric = new cw.MathExpression({
      expression: "FILL(successSum, 0)",
      label: "WorkerActionCount",
      period,
      usingMetrics: {
        successSum: sumMetric
      }
    });

    const alarm = new cw.Alarm(this, props.alarmID, {
      actionsEnabled: props.enabled,
      alarmDescription: props.alarmDescription,
      comparisonOperator: cw.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD,
      evaluationPeriods: props.evaluationPeriods,
      metric: filledSumMetric,
      threshold: 0,
      treatMissingData: cw.TreatMissingData.BREACHING
    });

    alarm.addAlarmAction(new cw_actions.SnsAction(props.snsTopic));
    alarm.addOkAction(new cw_actions.SnsAction(props.snsTopic));
  }
}
