locals {
  apollo_gate_api_secrets_list = [
    "AUTH_API_KEY",
    "SENTRY_DSN",
  ]

  apollo_gate_api_common_secrets = {
    DSPAPI_APPKEY      = aws_secretsmanager_secret.dsp_api_secret["DSPAPI_APPKEY"]
    APOLLOAPI_APPKEY   = aws_secretsmanager_secret.apollo_api_secret["AUTH_API_KEY"]
    VENDORAPI_APPKEY   = aws_secretsmanager_secret.vendor_api_secret["VENDORAPI_APPKEY"]
    ADMINAPI_APPKEY    = aws_secretsmanager_secret.common_secret["ADMINAPI_KEY"]
    USERDATAAPI_APPKEY = aws_secretsmanager_secret.apollo_user_data_secret["AUTH_API_KEY"]
  }

  apollo_gate_api_secrets_mapping = [
    for k, v in merge(aws_secretsmanager_secret.apollo_gate_api_secret, local.apollo_gate_api_common_secrets) :
    {
      name      = k,
      valueFrom = "arn:aws:secretsmanager:${var.aws_region_id}:${local.account_id}:secret:${v["name"]}"
    }
  ]

  apollo_gate_api_env_vars = [
    {
      name  = "SERVICE_ENV",
      value = local.environment
    },
    {
      name  = "DSPAPI_SCHEMA",
      value = "https"
    },
    {
      name  = "DSPAPI_HOSTNAME",
      value = module.domains[local.env_prefix]["dsp-api"]
    },
    {
      name  = "APOLLOAPI_SCHEMA",
      value = "https"
    },
    {
      name  = "APOLLOAPI_HOSTNAME",
      value = module.domains[local.env_prefix]["apollo-api"]
    },
    {
      name  = "VENDORAPI_SCHEMA",
      value = "https"
    },
    {
      name  = "VENDORAPI_HOSTNAME",
      value = module.domains[local.env_prefix]["vendor-api"]
    },
    {
      name  = "ADMINAPI_HOSTNAME",
      value = module.domains[local.env_prefix]["admin-api-proxy"]
    },
    {
      name  = "ADMINAPI_SCHEMA",
      value = "https"
    },
    {
      name  = "USERDATAAPI_SCHEMA",
      value = "https"
    },
    {
      name  = "USERDATAAPI_HOSTNAME",
      value = module.domains[local.env_prefix]["user-data-api"]
    },
    {
      name  = "REDIS_HOST",
      value = data.aws_elasticache_cluster.main.cache_nodes[0].address
    },
    {
      name  = "ELASTICSEARCH_URL",
      value = "https://search-prod-apollo-filtr-admin-ge64mq3zgpmsgkpo4m2sabvugi.us-east-1.es.amazonaws.com"
    },
    {
      name  = "USERS_VISITS_MESSAGE_QUEUE_URL",
      value = data.aws_sqs_queue.track_visit_queue.url
    },
    {
      name  = "CORE_IMAGE_SERVICE_HOSTNAME",
      value = "${module.atlas_domains[local.env_prefix]["images-api"]}/v2/"
    },
    {
      name  = "CORE_IMAGE_SERVICE_SCHEMA",
      value = "https"
    },
  ]
}

resource "aws_secretsmanager_secret" "apollo_gate_api_secret" {
  for_each = toset(local.apollo_gate_api_secrets_list)

  name       = "${local.secrets_name_prefix}/gate-api/${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_ecr_repository" "gate_api_ecr" {
  name = "${local.project_group}/gate-api"

  provider = aws.gdb-apollo-dev
}

data "aws_secretsmanager_secret_version" "gate_api_auth_key" {
  secret_id = "${local.secrets_name_prefix}/gate-api/AUTH_API_KEY"
}

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

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

  policies_list = [
    data.aws_iam_policy.track_visit_producer_policy.arn,
    data.aws_iam_policy.secrets_services_ro.arn,
    data.aws_iam_policy.logging_policy.arn,
  ]

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

module "apollo_gate_api_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}-gate-api"
  container_name  = "gate-api"
  container_image = "${data.aws_ecr_repository.gate_api_ecr.repository_url}:${local.docker_image_env_tag[local.env_prefix]}"
  cpu             = 512
  memory          = 1024

  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.gate_api_ecs_role.role.arn
  exec_role_arn = data.aws_iam_role.standard_exec.arn

  env_vars = local.apollo_gate_api_env_vars
  secrets  = local.apollo_gate_api_secrets_mapping

  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-gate-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-gate-api.loadBalancer.server.port"          = "8000"
    "traefik.http.services.apollo-gate-api.loadBalancer.server.scheme"        = "http"
    "traefik.http.services.apollo-gate-api.loadBalancer.healthCheck.interval" = "10s"
    "traefik.http.services.apollo-gate-api.loadBalancer.healthCheck.path"     = "/health"
    "traefik.http.services.apollo-gate-api.loadBalancer.healthCheck.port"     = "8000"
    "traefik.http.services.apollo-gate-api.loadBalancer.healthCheck.scheme"   = "http"
    "traefik.http.services.apollo-gate-api.loadBalancer.healthCheck.timeout"  = "5s"

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

    // starred-tracks
    "traefik.http.routers.gate-api-apollo-starred.rule"        = "Host(`${module.domains[local.env_prefix]["traefik"]}`) && PathPrefix(`/apollo-api/v1/starred-tracks`)"
    "traefik.http.routers.gate-api-apollo-starred.entryPoints" = "web"
    "traefik.http.routers.gate-api-apollo-starred.service"     = "apollo-gate-api"
    "traefik.http.routers.gate-api-apollo-starred.middlewares" = join(",", [
      "gate-api-cors@ecs",
      "gate-api-gzip@ecs",
      "gate-api-auth@ecs",
      "gate-api-addAuthHeader@ecs",
      "gate-api-apollo-starred-stripPrefix@ecs",
      "gate-api-apollo-starred-addPrefix@ecs",
    ])

    "traefik.http.middlewares.gate-api-apollo-starred-stripPrefix.stripPrefix.prefixes"   = "/apollo-api/v1/starred-tracks"
    "traefik.http.middlewares.gate-api-apollo-starred-stripPrefix.stripPrefix.forceSlash" = "true"
    "traefik.http.middlewares.gate-api-apollo-starred-addPrefix.addPrefix.prefix"         = "/api/v0/users/favorites/tracks"

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

    "traefik.http.middlewares.gate-api-stripPrefix-register-device.stripPrefix.prefixes"   = "/apollo-api/push-notifications/v1/register-device"
    "traefik.http.middlewares.gate-api-stripPrefix-register-device.stripPrefix.forceSlash" = "true"
    "traefik.http.middlewares.gate-api-addPrefix-register-device.addPrefix.prefix"         = "/api/users/devices/register"

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

    "traefik.http.middlewares.gate-api-stripPrefix-unregister-device.stripPrefix.prefixes"   = "/apollo-api/push-notifications/v1/unregister-device"
    "traefik.http.middlewares.gate-api-stripPrefix-unregister-device.stripPrefix.forceSlash" = "true"
    "traefik.http.middlewares.gate-api-addPrefix-unregister-device.addPrefix.prefix"         = "/api/users/devices/unregister"

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

    "traefik.http.middlewares.gate-api-addAuthHeader.headers.customRequestHeaders.Authorization" = data.aws_secretsmanager_secret_version.gate_api_auth_key.secret_string

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

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

    "traefik.http.middlewares.gate-api-gzip.compress" = "true"

    // direct requests
    "traefik.http.routers.gate-api-internal.rule"        = "Host(`${module.domains[local.env_prefix]["gate-api"]}`)"
    "traefik.http.routers.gate-api-internal.entryPoints" = "web"
    "traefik.http.routers.gate-api-internal.service"     = "apollo-gate-api"
    "traefik.http.routers.gate-api-internal.middlewares" = "gate-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_gate_api_service" {
  source = "../../../modules/ecs/services/service_taskless_v2"

  env_prefix    = local.env_prefix
  project       = "gate-api"
  project_group = local.project_group

  cluster_arn   = data.aws_ecs_cluster.main.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_gate_api_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                     = "gate-api.apollo.stream"

  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_gate_api_appautoscaling" {
  source = "../../../modules/appautoscaling/ecs"

  env_prefix           = local.env_prefix
  max_capacity         = 10
  min_capacity         = 2
  project              = "gate-api"
  project_group        = local.project_group
  resource_id          = "service/${data.aws_ecs_cluster.main.cluster_name}/${module.apollo_gate_api_service.ecs_service.name}"
  upscaling_adjustment = 1
}

resource "aws_cloudwatch_metric_alarm" "apollo_gate_api_high_usage" {
  alarm_name          = "${local.name_prefix}-gate-api_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_gate_api_appautoscaling.scaling_up_policy.arn]

  metric_query {
    id          = "check_thresholds"
    expression  = "(cpu >= 25 OR memory >= 70)"
    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.main.cluster_name
        ServiceName = module.apollo_gate_api_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.main.cluster_name
        ServiceName = module.apollo_gate_api_service.ecs_service.name
      }
    }
  }
}

resource "aws_cloudwatch_metric_alarm" "apollo_gate_api_low_usage" {
  alarm_name          = "${local.name_prefix}-gate-api_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_gate_api_appautoscaling.scaling_down_policy.arn]

  metric_query {
    id          = "check_thresholds"
    expression  = "(cpu < 25 AND memory < 70)"
    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.main.cluster_name
        ServiceName = module.apollo_gate_api_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.main.cluster_name
        ServiceName = module.apollo_gate_api_service.ecs_service.name
      }
    }
  }
}

resource "aws_route53_record" "apollo_gate_api_record" {
  zone_id = data.aws_route53_zone.apollo_stream_zone.zone_id
  name    = "gate-api.apollo.stream"
  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
}
