locals {
  apollo_user_data_secrets_list = [
    "APOLLO_AUTH0_CLIENT_SECRET",
    "AUTH_API_KEY",
    "RTI_AUTH0_CLIENT_SECRET",
    "SENTRY_DSN",
  ]

  apollo_user_data_secrets_mapping = [
    for k, v in aws_secretsmanager_secret.apollo_user_data_secret :
    {
      name      = k,
      valueFrom = "arn:aws:secretsmanager:${var.aws_region_id}:${local.account_id}:secret:${v["name"]}"
    }
  ]

  apollo_user_data_env_vars = [
    {
      name  = "CREATE_DB",
      value = ""
    },
    {
      name  = "DB_NAME",
      value = "user_data"
    },
    {
      name  = "AUTH0_DOMAIN",
      value = "https://prod-dna-apps.auth0.com"
    },
    {
      name  = "APOLLO_AUTH0_CLIENT_ID",
      value = "uJyvmAvj7z9lQcYEu48TD8LlZZcWTmOs"
    },
    {
      name  = "APOLLO_AUTH0_AUDIENCE",
      value = "https://prod-dna-apps.auth0.com/api/v2/"
    },
    {
      name  = "APOLLO_HOST",
      value = "https://${module.domains[local.env_prefix]["traefik"]}"
    },
    {
      name  = "RTI_AUTH0_CLIENT_ID",
      value = "4YFHHDWN8wAs46o7jZt4DTSOwAlCrgxJ"
    },
    {
      name  = "RTI_AUTH0_AUDIENCE",
      value = "https://api.sma.stream"
    },
    {
      name  = "RTI_HOST",
      value = "https://api.sma.stream"
    },
    {
      name  = "SERVICE_ENV",
      value = local.env_prefix
    },
    {
      name  = "NOTIFICATION_TOPIC_ARN",
      value = data.aws_sns_topic.notifications_topic.arn
    },
    {
      name  = "PUSH_TOPIC_ARN",
      value = data.aws_sns_topic.push_messages_topic.arn
    },
    {
      name  = "MESSAGES_TOPIC_ARN",
      value = data.aws_sns_topic.messages_topic.arn
    },
    {
      name  = "EVENT_BUS",
      value = "${local.name_prefix}-notifications"
    },
  ]
}

data "aws_ecr_repository" "apollo_user_data_ecr" {
  name = "${local.project_group}/user-data"

  provider = aws.gdb-apollo-dev
}

data "aws_ecr_repository" "apollo_user_data_sqitch_ecr" {
  name = "${local.project_group}/user-data-sqitch"

  provider = aws.gdb-apollo-dev
}

data "aws_secretsmanager_secret_version" "user_data_api_auth_key" {
  secret_id = "${local.secrets_name_prefix}/user-data/AUTH_API_KEY"
}

resource "aws_secretsmanager_secret" "apollo_user_data_secret" {
  for_each = toset(local.apollo_user_data_secrets_list)

  name       = "${local.secrets_name_prefix}/user-data/${each.value}"
  kms_key_id = data.aws_kms_key.secrets_general_key.id

  tags = merge(
    local.common_tags,
    {
      project                  = "Infrastructure",
      service                  = "Secrets Manager",
      plat_env_project_service = "${local.aggregated_tag}_INFRA_SCRT"
    }
  )
}

data "aws_iam_policy" "notifications_event_bus_publisher" {
  arn = "arn:aws:iam::${local.account_id}:policy/${local.name_prefix}-notifications_event_bus_publisher"
}

module "apollo_user_data_ecs_role" {
  source = "../../../modules/iam/roles/ecs_task_role"

  enable_default_role = false
  name_prefix         = local.name_prefix
  name                = "apollo_user_data_ecs_role"

  policies_list = [
    data.aws_iam_policy.secrets_deployment_ro.arn,
    data.aws_iam_policy.publish_notifications.arn,
    data.aws_iam_policy.publish_push_messages.arn,
    data.aws_iam_policy.notifications_event_bus_publisher.arn,
    data.aws_iam_policy.publish_messages_policy.arn,
  ]

  common_tags = merge(
    local.common_tags,
    {
      project                  = "Portal",
      service                  = "ECS",
      plat_env_project_service = "${local.aggregated_tag}_PRL_ECS"
    }
  )
}

module "apollo_user_data_one_time_task_def" {
  source = "../../../modules/ecs/tasks/fargate_v3"

  env_prefix      = local.env_prefix
  project_group   = local.project_group
  family          = "${local.env_prefix}-${local.project_group}-user-data-one-time"
  container_name  = "user-data"
  container_image = "${data.aws_ecr_repository.apollo_user_data_sqitch_ecr.repository_url}:${local.docker_image_env_tag[local.env_prefix]}"
  cpu             = 512
  memory          = 1024

  awslogs_create_group = false

  task_role_arn = module.apollo_user_data_ecs_role.role.arn
  exec_role_arn = data.aws_iam_role.standard_exec.arn

  env_vars = [
    {
      name  = "PGDATABASE",
      value = "user_data"
    }
  ]
  secrets = [
    {
      name      = "PGPASSWORD",
      valueFrom = "arn:aws:secretsmanager:${var.aws_region_id}:${local.account_id}:secret:maintenance/common/apollo/${local.env_prefix}/user_data_owner:password::"
    },
    {
      name      = "PGUSER",
      valueFrom = "arn:aws:secretsmanager:${var.aws_region_id}:${local.account_id}:secret:maintenance/common/apollo/${local.env_prefix}/user_data_owner:username::"
    },
    {
      name      = "PGHOST",
      valueFrom = "arn:aws:secretsmanager:${var.aws_region_id}:${local.account_id}:secret:maintenance/common/apollo/${local.env_prefix}/user_data_owner:host::"
    },
    {
      name      = "PGPORT",
      valueFrom = "arn:aws:secretsmanager:${var.aws_region_id}:${local.account_id}:secret:maintenance/common/apollo/${local.env_prefix}/user_data_owner:port::"
    },
  ]

  common_tags = merge(
    local.common_tags,
    {
      project                  = "Portal",
      service                  = "ECS",
      plat_env_project_service = "${local.aggregated_tag}_PRL_ECS"
    }
  )
}

module "apollo_user_data_task_def" {
  source = "../../../modules/ecs/tasks/fargate_v3"

  env_prefix      = local.env_prefix
  project_group   = local.project_group
  family          = "${local.env_prefix}-${local.project_group}-user-data"
  container_name  = "user-data"
  container_image = "${data.aws_ecr_repository.apollo_user_data_ecr.repository_url}:${local.docker_image_env_tag[local.env_prefix]}"
  cpu             = 1024
  memory          = 2048

  awslogs_retention = module.cloudwatch_logs_retention_by_env[local.env_prefix]

  healthcheck = {
    command     = ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"]
    retries     = 3
    timeout     = 5
    interval    = 30
    startPeriod = 5
  }

  task_role_arn = module.apollo_user_data_ecs_role.role.arn
  exec_role_arn = data.aws_iam_role.standard_exec.arn

  env_vars = local.apollo_user_data_env_vars
  secrets = concat(
    local.apollo_user_data_secrets_mapping,
    [
      {
        name      = "DB_USER",
        valueFrom = "arn:aws:secretsmanager:${var.aws_region_id}:${local.account_id}:secret:apollo/${local.env_prefix}/user_data/pg/user:username::"
      },
      {
        name      = "DB_PASS",
        valueFrom = "arn:aws:secretsmanager:${var.aws_region_id}:${local.account_id}:secret:apollo/${local.env_prefix}/user_data/pg/user:password::"
      },
      {
        name      = "DB_HOST",
        valueFrom = "arn:aws:secretsmanager:${var.aws_region_id}:${local.account_id}:secret:apollo/${local.env_prefix}/user_data/pg/user:host::"
      },
    ]
  )

  datadog_enabled    = true
  dd_apm_enabled     = true
  dd_logs_injection  = false
  dd_container_image = "public.ecr.aws/datadog/agent:latest"
  dd_awslogs_group   = aws_cloudwatch_log_group.datadog_agent.name
  dd_env_vars = [
    {
      name  = "DD_APM_IGNORE_RESOURCES",
      value = "GET /health"
    },
    {
      name = "DD_TAGS",
      value = "service:apollo-user-data-api application_family:apollo"
    },
  ]
  dd_secrets = [
    {
      name      = "DD_API_KEY",
      valueFrom = "arn:aws:secretsmanager:us-east-1:${local.account_id}:secret:apollo/${local.env_prefix}/common/DD_API_KEY"
    }
  ]

  docker_labels = {
    "traefik.enable" = "true"

    "traefik.http.services.apollo-user-data.loadBalancer.server.port"          = "8000"
    "traefik.http.services.apollo-user-data.loadBalancer.server.scheme"        = "http"
    "traefik.http.services.apollo-user-data.loadBalancer.healthCheck.interval" = "10s"
    "traefik.http.services.apollo-user-data.loadBalancer.healthCheck.path"     = "/health"
    "traefik.http.services.apollo-user-data.loadBalancer.healthCheck.port"     = "8000"
    "traefik.http.services.apollo-user-data.loadBalancer.healthCheck.scheme"   = "http"
    "traefik.http.services.apollo-user-data.loadBalancer.healthCheck.timeout"  = "5s"

    // request via traefik
    "traefik.http.routers.user-data-api.rule"        = "Host(`${module.domains[local.env_prefix]["traefik"]}`, `${module.domains.uat["traefik"]}`) && PathPrefix(`/user-data-api`)"
    "traefik.http.routers.user-data-api.entrypoints" = "web"
    "traefik.http.routers.user-data-api.service"     = "apollo-user-data"
    "traefik.http.routers.user-data-api.middlewares" = join(",", [
      "user-data-api-cors@ecs",
      "user-data-api-gzip@ecs",
      "user-data-api-auth@ecs",
      "user-data-api-addAuthHeader@ecs",
      "user-data-api-stripPrefix@ecs",
      "user-data-api-addPrefix@ecs",
    ])

    // /apollo-api/v2/push-notifications/bulk/
    "traefik.http.routers.user-data-api-push-notifications-bulk.rule"        = "Host(`${module.domains[local.env_prefix]["traefik"]}`, `${module.domains.uat["traefik"]}`) && Path(`/apollo-api/v2/push-notifications/bulk/`) && Method(`POST`)"
    "traefik.http.routers.user-data-api-push-notifications-bulk.entryPoints" = "web"
    "traefik.http.routers.user-data-api-push-notifications-bulk.service"     = "apollo-user-data"
    "traefik.http.routers.user-data-api-push-notifications-bulk.middlewares" = join(",", [
      "user-data-api-cors@ecs",
      "user-data-api-gzip@ecs",
      "user-data-api-auth@ecs",
      "user-data-api-addAuthHeader@ecs",
      "user-data-api-stripPrefix-push-notifications-bulk@ecs",
      "user-data-api-addPrefix-push-notifications-bulk@ecs",
    ])

    "traefik.http.middlewares.user-data-api-stripPrefix-push-notifications-bulk.stripPrefix.prefixes"   = "/apollo-api/v2/push-notifications/bulk"
    "traefik.http.middlewares.user-data-api-stripPrefix-push-notifications-bulk.stripPrefix.forceSlash" = "true"
    "traefik.http.middlewares.user-data-api-addPrefix-push-notifications-bulk.addPrefix.prefix"         = "/api/users/messages/feed/view"


    // /apollo-api/push-notifications/
    "traefik.http.routers.user-data-api-push-notifications.rule"        = "Host(`${module.domains[local.env_prefix]["traefik"]}`, `${module.domains.uat["traefik"]}`) && Path(`/apollo-api/push-notifications/`) && Method(`GET`)"
    "traefik.http.routers.user-data-api-push-notifications.entryPoints" = "web"
    "traefik.http.routers.user-data-api-push-notifications.service"     = "apollo-user-data"
    "traefik.http.routers.user-data-api-push-notifications.middlewares" = join(",", [
      "user-data-api-cors@ecs",
      "user-data-api-gzip@ecs",
      "user-data-api-auth@ecs",
      "user-data-api-addAuthHeader@ecs",
      "user-data-api-stripPrefix-push-notifications@ecs",
      "user-data-api-addPrefix-push-notifications@ecs",
    ])

    "traefik.http.middlewares.user-data-api-stripPrefix-push-notifications.stripPrefix.prefixes"   = "/apollo-api/push-notifications"
    "traefik.http.middlewares.user-data-api-stripPrefix-push-notifications.stripPrefix.forceSlash" = "true"
    "traefik.http.middlewares.user-data-api-addPrefix-push-notifications.addPrefix.prefix"         = "/api/users/messages/feed"

    "traefik.http.middlewares.user-data-api-auth.forwardAuth.address"             = "https://${module.domains[local.env_prefix]["auth-api"]}/authorize"
    "traefik.http.middlewares.user-data-api-auth.forwardAuth.trustForwardHeader"  = "true"
    "traefik.http.middlewares.user-data-api-auth.forwardAuth.authResponseHeaders" = "X-User-Id"

    "traefik.http.middlewares.user-data-api-addAuthHeader.headers.customRequestHeaders.Authorization" = data.aws_secretsmanager_secret_version.user_data_api_auth_key.secret_string

    "traefik.http.middlewares.user-data-api-stripPrefix.stripPrefix.prefixes"   = "/user-data-api"
    "traefik.http.middlewares.user-data-api-stripPrefix.stripPrefix.forceSlash" = "true"
    "traefik.http.middlewares.user-data-api-addPrefix.addPrefix.prefix"         = "/api"

    "traefik.http.middlewares.user-data-api-cors.headers.accessControlAllowOriginList" = join(",", concat(formatlist("https://%s", [
      module.domains[local.env_prefix]["playlists_duplication"],
      module.domains.uat["apolloapp"],
      module.domains.local["apolloapp"],
      ]), formatlist("http://%s", [
      module.domains.local["apolloapp"],
    ])))
    "traefik.http.middlewares.user-data-api-cors.headers.accessControlMaxAge" = "600"
    "traefik.http.middlewares.user-data-api-cors.headers.addVaryHeader"       = "true"

    "traefik.http.middlewares.user-data-api-gzip.compress" = "true"

    // direct requests
    "traefik.http.routers.user-data-api-internal.rule"        = "Host(`${module.domains[local.env_prefix]["user-data-api"]}`)"
    "traefik.http.routers.user-data-api-internal.entrypoints" = "web"
    "traefik.http.routers.user-data-api-internal.service"     = "apollo-user-data"
    "traefik.http.routers.user-data-api-internal.middlewares" = "user-data-api-gzip@ecs"
  }

  port_mappings = [
    {
      containerPort = 8000
      hostPort      = 8000
      protocol      = "tcp"
    }
  ]

  common_tags = merge(
    local.common_tags,
    {
      project                  = "Portal",
      service                  = "ECS",
      plat_env_project_service = "${local.aggregated_tag}_PRL_ECS"
    }
  )
}

module "apollo_user_data_service" {
  source = "../../../modules/ecs/services/service_taskless_v2"

  env_prefix    = local.env_prefix
  project       = "user-data"
  project_group = local.project_group

  cluster_arn   = data.aws_ecs_cluster.common.arn
  desired_count = 1

  security_groups = [data.aws_security_group.main.id]
  subnets         = data.aws_subnets.private_subnets.ids
  vpc_id          = data.aws_vpc.main.id

  task_definition_arn = module.apollo_user_data_task_def.arn

  alb_enabled             = true
  https_listener_arn      = data.aws_lb_listener.external_https_listener.arn
  tg_health_check_path    = "/health"
  tg_health_check_matcher = "200"
  container_port          = 8000
  url                     = "user-data.apollo.stream" // module.domains[local.env_prefix]["user-data-api"]

  sd_enabled   = true
  namespace_id = aws_service_discovery_private_dns_namespace.main.id

  enable_ecs_managed_tags = true

  common_tags = merge(
    local.common_tags,
    {
      project                  = "Portal",
      service                  = "ECS",
      plat_env_project_service = "${local.aggregated_tag}_PRL_ECS"
    }
  )
}

module "apollo_user_data_appautoscaling" {
  source = "../../../modules/appautoscaling/ecs"

  env_prefix           = local.env_prefix
  max_capacity         = 10
  min_capacity         = 2
  project              = "user-data"
  project_group        = local.project_group
  resource_id          = "service/${data.aws_ecs_cluster.common.cluster_name}/${module.apollo_user_data_service.ecs_service.name}"
  upscaling_adjustment = 1
}

resource "aws_cloudwatch_metric_alarm" "apollo_user_data_high_usage" {
  alarm_name          = "${local.name_prefix}-user_data_high_usage"
  alarm_description   = "Memory/CPU usage is higher than threshold."
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = 1
  threshold           = 1
  actions_enabled     = true
  alarm_actions       = [module.apollo_user_data_appautoscaling.scaling_up_policy.arn]

  metric_query {
    id          = "check_thresholds"
    expression  = "(cpu >= 10 OR memory >= 30)"
    label       = "CPU and Memory utilization limits"
    return_data = true
  }

  metric_query {
    id          = "cpu"
    return_data = false

    metric {
      metric_name = "CPUUtilization"
      namespace   = "AWS/ECS"
      period      = "60"
      stat        = "Average"

      dimensions = {
        ClusterName = data.aws_ecs_cluster.common.cluster_name
        ServiceName = module.apollo_user_data_service.ecs_service.name
      }
    }
  }

  metric_query {
    id          = "memory"
    return_data = false

    metric {
      metric_name = "MemoryUtilization"
      namespace   = "AWS/ECS"
      period      = "60"
      stat        = "Average"

      dimensions = {
        ClusterName = data.aws_ecs_cluster.common.cluster_name
        ServiceName = module.apollo_user_data_service.ecs_service.name
      }
    }
  }
}

resource "aws_cloudwatch_metric_alarm" "apollo_user_data_low_usage" {
  alarm_name          = "${local.name_prefix}-user_data_low_usage"
  alarm_description   = "Memory/CPU usage is lower than threshold."
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = 3
  threshold           = 1
  actions_enabled     = true
  alarm_actions       = [module.apollo_user_data_appautoscaling.scaling_down_policy.arn]

  metric_query {
    id          = "check_thresholds"
    expression  = "(cpu < 10 AND memory < 30)"
    label       = "CPU and Memory utilization limits"
    return_data = true
  }

  metric_query {
    id          = "cpu"
    return_data = false

    metric {
      metric_name = "CPUUtilization"
      namespace   = "AWS/ECS"
      period      = "180"
      stat        = "Average"

      dimensions = {
        ClusterName = data.aws_ecs_cluster.common.cluster_name
        ServiceName = module.apollo_user_data_service.ecs_service.name
      }
    }
  }

  metric_query {
    id          = "memory"
    return_data = false

    metric {
      metric_name = "MemoryUtilization"
      namespace   = "AWS/ECS"
      period      = "180"
      stat        = "Average"

      dimensions = {
        ClusterName = data.aws_ecs_cluster.common.cluster_name
        ServiceName = module.apollo_user_data_service.ecs_service.name
      }
    }
  }
}

resource "aws_route53_record" "apollo_user_data_record" {
  zone_id = data.aws_route53_zone.apollo_stream_zone.zone_id
  name    = "user-data.apollo.stream" // module.domains[local.env_prefix]["user-data-api"]
  type    = "A"

  alias {
    name                   = data.aws_lb.external_alb.dns_name
    zone_id                = data.aws_lb.external_alb.zone_id
    evaluate_target_health = false
  }

  provider = aws.gdb-delphi-dev
}
