import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as snsSubscriptions from 'aws-cdk-lib/aws-sns-subscriptions';
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import * as cloudwatchActions from 'aws-cdk-lib/aws-cloudwatch-actions';
import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as logs from 'aws-cdk-lib/aws-logs';
export interface GlueJobStackProps extends StackProps {
  accountStage: string;
}

export class GlueJobStack extends Stack {
  constructor(scope: Construct, id: string, props?: GlueJobStackProps) {
    super(scope, id, props);

    // Define SNS Topic
    const notificationTopic = new sns.Topic(this, 'GlueJobNotificationTopic', {
      displayName: 'Glue Job Monitoring Notifications',
    });

    // Create Lambda layer with dependencies
    const dependenciesLayer = new lambda.LayerVersion(this, 'GlueJobMonitorDependences', {
      code: lambda.Code.fromAsset('lambda/glue-jobs/layers', {
        bundling: {
          image: lambda.Runtime.PYTHON_3_9.bundlingImage,
          command: ['bash', '-c', 'pip install -r requirements.txt -t /asset-output/python && ' + 'cp requirements.txt /asset-output/'],
        },
      }),
      compatibleRuntimes: [lambda.Runtime.PYTHON_3_9],
      description: 'Lambda layer with Python dependencies',
    });

    const lambdaFunction = new lambda.Function(this, 'glueJobMonitor', {
      runtime: lambda.Runtime.PYTHON_3_9,
      handler: 'main.handler',
      code: lambda.Code.fromAsset('lambda/glue-jobs'),
      functionName: 'glue-job-monitor',
      timeout: Duration.minutes(15),
      layers: [dependenciesLayer],
      memorySize: 1024,
    });

    notificationTopic.addSubscription(new snsSubscriptions.LambdaSubscription(lambdaFunction));
    notificationTopic.addSubscription(new snsSubscriptions.EmailSubscription('jinhui@suno.com'));
    notificationTopic.addSubscription(new snsSubscriptions.EmailSubscription('rider@suno.com'));

    // Define a list of Glue job names
    const glueJobNames = [
      'rds_to_s3_bots_discordinfo_hourly_all',
      'rds_to_s3_bots_generatedlyrics_hourly_updated',
      'rds_to_s3_bots_usercaptchastate_hourly_updated',
      'rds_to_s3_bots_personaclip_hourly_created',
      'rds_to_s3_bots_userpersonareaction_hourly_updated',
      'rds_to_s3_bots_botjail_hourly_updated',
      'rds_to_s3_bots_contestclip_hourly_updated',
      'rds_to_s3_bots_iosinvitepromocode_hourly_updated',
      'rds_to_s3_bots_iosinvitepromocodeusage_hourly_updated',
      'rds_to_s3_bots_usernotification_hourly_updated',
      'rds_to_s3_bots_invitehistory_hourly_updated',
      'rds_to_s3_bots_userreaction_hourly_updated',
      'rds_to_s3_bots_generatedclipextra_hourly_updated',
      'rds_to_s3_bots_generatedclip_hourly_updated',
      'rds_to_s3_bots_persona_hourly_updated',
      'rds_to_s3_bots_playlistclip_hourly_updated',
      'rds_to_s3_auth_user_hourly_created',
      'rds_to_s3_bots_sessionhistroy_hourly_updated',
      'rds_to_s3_bots_remixcontestsoundclash_hourly_updated',
      'rds_to_s3_bots_periodcreditusage_daily_updated',
      'rds_to_s3_bots_userstats_daily_all',
      'rds_to_s3_bots_profilefollow_daily_all',
      'rds_to_s3_auth_user_groups_daily_all',
      'rds_to_s3_bots_playlist_daily_updated',
      'rds_to_s3_bots_clip_hourly_created',
      'rds_to_s3_bots_clip_daily_all_public',
      'rds_to_s3_bots_clip_weekly_filtered_for_genere',
      'rds_to_s3_bots_audioupload_hourly_created',
      'rds_to_s3_bots_userjail_daily_updated',
      'rds_to_s3_bots_onetimefreeusage_hourly_updated',
      'rds_to_s3_bots_projectclip_hourly_updated',
      'rds_to_s3_bots_project_hourly_updated',
      'rds_to_s3_bots_userdeleterequest_daily_updated',
      'rds_to_s3_bots_userhcaptchahistory_daily_insert',
      'rds_to_s3_tables_ids_weekly_all',
      'rds_to_s3_clip_table_ids_weekly_all'
    ];

    glueJobNames.forEach((jobName) => {
      // Monitor Glue job for failures
      const glueJobFailureMetric = new cloudwatch.Metric({
        namespace: 'AWS/Glue',
        metricName: 'GlueJobRunErrors',
        dimensionsMap: {
          JobName: jobName,
        },
        statistic: 'Sum',
        period: Duration.minutes(1),
      });

      const failureAlarm = new cloudwatch.Alarm(this, `GlueJobFailureAlarm-${jobName}`, {
        metric: glueJobFailureMetric,
        threshold: 1,
        evaluationPeriods: 1,
        alarmDescription: 'Alarm for Glue job failures: ' + jobName,
        alarmName: 'GlueJobFailureAlarm-' + jobName,
      });
      failureAlarm.addAlarmAction(new cloudwatchActions.SnsAction(notificationTopic));

      // Create a CloudWatch log group for Glue job logs
      const glueLogGroup = new logs.LogGroup(this, `GlueJobLogGroup-${jobName}`, {
        logGroupName: `/aws-glue/jobs/${jobName}/output`,
        retention: logs.RetentionDays.ONE_WEEK,
      });

      // Create a metric filter for Glue job duration
      const glueJobDurationMetric = new logs.MetricFilter(this, `GlueJobDurationMetric-${jobName}`, {
        filterName: `GlueJobDurationMetric-${jobName}`,
        logGroup: glueLogGroup,
        metricNamespace: 'Glue',
        metricName: 'JobDuration',
        filterPattern: logs.FilterPattern.exists('$.jobRunTime'),
        metricValue: '$.jobRunTime',
      });

      // Create a CloudWatch alarm for Glue job duration
      const glueJobDurationAlarm = new cloudwatch.Alarm(this, `GlueJobDurationAlarm-${jobName}`, {
        metric: glueJobDurationMetric.metric({
          statistic: 'Maximum',
          period: Duration.minutes(1),
        }),
        threshold: 20 * 60, // 20 minutes in seconds
        evaluationPeriods: 1,
        alarmDescription: 'Alarm when any Glue job runs for more than 20 minutes',
        alarmName: `GlueJobDurationAlarm-${jobName}`,
        comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
      });

      // Add SNS notification to the alarm
      glueJobDurationAlarm.addAlarmAction(new cloudwatchActions.SnsAction(notificationTopic));
    });

    // EventBridge rule for Glue job state changes
    const glueJobStateRule = new events.Rule(this, 'GlueJobStateChangeRule', {
      eventPattern: {
        source: ['aws.glue'],
        detailType: ['Glue Job State Change'],
        detail: {
          jobName: glueJobNames,
          state: ['FAILED'],
        },
      },
    });

    glueJobStateRule.addTarget(new targets.SnsTopic(notificationTopic));
  }
}
