locals {
  apollo_traefik_alb_rules_host_to_tg_mapping_apollo_stream_domain = {
    "${module.domains[local.env_prefix]["apollo-api"]}"        = "public-proxy",
    "${module.domains[local.env_prefix]["dsp-api"]}"           = "public-proxy",
    "${module.domains[local.env_prefix]["gate-api"]}"          = "public-proxy",
    "${module.domains[local.env_prefix]["traefik"]}"           = "public-proxy",
    "${module.domains[local.env_prefix]["traefik-dashboard"]}" = "private-proxy",
    "${module.domains[local.env_prefix]["auth-api"]}"          = "public-proxy",
    // Available options are: public-proxy private-proxy debug-proxy"
  }

  apollo_traefik_alb_rules_host_to_tg_mapping_filtr_com_domain = {
    "${module.domains[local.env_prefix]["private-api"]}" = "public-proxy",
    // Available options are: public-proxy internal private-proxy"
  }

  htaccess_list = [for x in split("\n", data.aws_secretsmanager_secret_version.traefik_htaccess.secret_string) : trimspace(x) if trimspace(x) != ""]
}

resource "aws_secretsmanager_secret" "traefik_secret" {

  name       = "${local.secrets_name_prefix}/traefik/HTACCESS"
  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_secretsmanager_secret_version" "traefik_htaccess" {
  secret_id = "${local.secrets_name_prefix}/traefik/HTACCESS"
}

data "aws_iam_policy_document" "traefik_policy_doc" {
  version = "2012-10-17"
  statement {
    sid    = "TraefikECSReadAccess"
    effect = "Allow"
    actions = [
      "ecs:ListClusters",
      "ecs:DescribeClusters",
      "ecs:ListTasks",
      "ecs:DescribeTasks",
      "ecs:DescribeContainerInstances",
      "ecs:DescribeTaskDefinition",
      "ec2:DescribeInstances",
    ]
    resources = ["*"]
  }
}

resource "aws_iam_policy" "traefik_policy" {
  name        = "${local.name_prefix}-traefik"
  path        = "/"
  description = "Allow read ecs and ec2 information"
  policy      = data.aws_iam_policy_document.traefik_policy_doc.json
}

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

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

  policies_list = [
    aws_iam_policy.traefik_policy.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 "traefik_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}-traefik"
  container_name  = "traefik"
  container_image = "725764004555.dkr.ecr.us-east-1.amazonaws.com/traefik:latest"
  cpu             = 1024
  memory          = 2048

  awslogs_retention = module.cloudwatch_logs_retention_by_env[local.env_prefix]

  command = [
    "--providers.ecs.autoDiscoverClusters=false",
    "--providers.ecs.clusters=${data.aws_ecs_cluster.main.cluster_name},${data.aws_ecs_cluster.common.cluster_name}",
    "--providers.ecs.exposedByDefault=false",
    "--providers.ecs.region=us-east-1",
    "--accesslog=true",
    "--accesslog.format=json",
    "--accesslog.fields.defaultmode=keep",
    "--accesslog.fields.names.ClientUsername=drop",
    "--accesslog.fields.headers.defaultmode=keep",
    "--accesslog.fields.headers.names.User-Agent=redact",
    "--accesslog.fields.headers.names.Authorization=drop",
    "--accesslog.fields.headers.names.Content-Type=keep",
    "--log.level=ERROR",
    "--metrics.datadog=true",
    "--entryPoints.web.address=:80",
    "--entryPoints.web.transport.respondingTimeouts.idleTimeout=60",
    "--entryPoints.ping.address=:8000",
    "--entryPoints.traefik.address=:8080",
    "--api=true",
    "--api.dashboard=true",
    "--api.insecure=false",
    "--ping=true",
    "--ping.entryPoint=ping",
    "--tracing.datadog=true",
    "--tracing.datadog.globalTags.application_family=apollo",
    "--experimental.localPlugins.pathresponse.moduleName=github.com/traefik/plugin-pathresponse",
  ]

  docker_labels = {
    "traefik.enable"                                = "true"
    "traefik.http.routers.api.rule"                 = "Host(`${module.domains[local.env_prefix]["traefik-dashboard"]}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
    "traefik.http.routers.api.service"              = "api@internal"
    "traefik.http.routers.api.entrypoints"          = "traefik"
    "traefik.http.routers.api.middlewares"          = "auth@ecs"
    "traefik.http.middlewares.auth.basicauth.users" = join(",", local.htaccess_list)
  }

  datadog_enabled    = true
  dd_apm_enabled     = true
  dd_logs_injection  = true
  dd_container_image = "public.ecr.aws/datadog/agent:latest"
  dd_awslogs_group   = aws_cloudwatch_log_group.datadog_agent.name
  dd_env_vars = [
    {
      name  = "DD_DOGSTATSD_NON_LOCAL_TRAFFIC",
      value = true
    },
    {
      name  = "DD_APM_IGNORE_RESOURCES",
      value = "GET /ping"
    },
    {
      name  = "DD_TAGS",
      value = "service:traefik 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"
    }
  ]
  task_role_arn = module.traefik_ecs_role.role.arn
  exec_role_arn = data.aws_iam_role.standard_exec.arn

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

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

module "traefik_service" {
  source = "../../../modules/ecs/services/service_taskless_multi_tg"

  env_prefix    = local.env_prefix
  project       = "traefik"
  project_group = local.project_group

  cluster_arn   = data.aws_ecs_cluster.main.arn
  desired_count = 0

  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.traefik_task_def.arn

  target_groups_settings = [
    {
      name                 = "public-proxy"
      tg_port              = 80
      container_port       = 80
      deregistration_delay = 60
      health_check_path    = "/ping"
      health_check_port    = 8000
      health_check_matcher = 200
      url                  = "${local.env_prefix}-public-proxy.local"
      https_listener_arn   = data.aws_lb_listener.external_https_listener.arn
    },
    {
      name                 = "internal-proxy"
      tg_port              = 80
      container_port       = 80
      deregistration_delay = 60
      health_check_path    = "/ping"
      health_check_port    = 8000
      health_check_matcher = 200
      url                  = "${local.env_prefix}-internal-proxy.local"
      https_listener_arn   = data.aws_lb_listener.internal_https_listener.arn
    },
    {
      name                 = "private-proxy"
      tg_port              = 80
      container_port       = 8080
      deregistration_delay = 60
      health_check_path    = "/ping"
      health_check_port    = 8000
      health_check_matcher = 200
      url                  = "${local.env_prefix}-private-proxy.local"
      https_listener_arn   = data.aws_lb_listener.internal_https_listener.arn
    }
  ]

  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 "alb_rules_proxy_to_external_apis" {
  source   = "../../../modules/ec2/alb/https_rule_with_redirect"
  for_each = merge(local.apollo_traefik_alb_rules_host_to_tg_mapping_apollo_stream_domain, local.apollo_traefik_alb_rules_host_to_tg_mapping_filtr_com_domain)

  https_listener_arn = each.value == "public-proxy" ? data.aws_lb_listener.external_https_listener.arn : data.aws_lb_listener.internal_https_listener.arn
  tg_arn             = module.traefik_service.target_groups[each.value].arn
  host-header        = each.key
}

resource "aws_route53_record" "apollo_traefik_proxy_records" {
  for_each = local.apollo_traefik_alb_rules_host_to_tg_mapping_apollo_stream_domain

  zone_id = data.aws_route53_zone.apollo_stream_zone.zone_id
  name    = each.key
  type    = "A"

  alias {
    name                   = each.value == "public-proxy" ? data.aws_lb.external_alb.dns_name : data.aws_lb.internal_alb.dns_name
    zone_id                = each.value == "public-proxy" ? data.aws_lb.external_alb.zone_id : data.aws_lb.internal_alb.zone_id
    evaluate_target_health = false
  }

  provider = aws.gdb-delphi-dev
}

resource "aws_route53_record" "apollo_traefik_proxy_records_for_filtr" {
  for_each = local.apollo_traefik_alb_rules_host_to_tg_mapping_filtr_com_domain

  zone_id = data.aws_route53_zone.filtr_com_zone.zone_id
  name    = each.key
  type    = "A"

  alias {
    name                   = each.value == "public-proxy" ? data.aws_lb.external_alb.dns_name : data.aws_lb.internal_alb.dns_name
    zone_id                = each.value == "public-proxy" ? data.aws_lb.external_alb.zone_id : data.aws_lb.internal_alb.zone_id
    evaluate_target_health = false
  }

  provider = aws.gdb-delphi-dev
}
/*
module "traefik_appautoscaling" {
  source = "../../../modules/appautoscaling/ecs"

  env_prefix           = local.env_prefix
  max_capacity         = 10
  min_capacity         = 1
  project              = "traefik"
  project_group        = local.project_group
  resource_id          = "service/${data.aws_ecs_cluster.main.cluster_name}/${module.traefik_service.ecs_service.name}"
  upscaling_adjustment = 1
}

resource "aws_cloudwatch_metric_alarm" "traefik_high_usage" {
  alarm_name          = "${local.name_prefix}-traefik_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.traefik_appautoscaling.scaling_up_policy.arn]

  metric_query {
    id          = "check_thresholds"
    expression  = "(cpu >= 40 OR memory >= 25)"
    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.traefik_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.traefik_service.ecs_service.name
      }
    }
  }
}

resource "aws_cloudwatch_metric_alarm" "traefik_low_usage" {
  alarm_name          = "${local.name_prefix}-traefik_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.traefik_appautoscaling.scaling_down_policy.arn]

  metric_query {
    id          = "check_thresholds"
    expression  = "(cpu < 30 AND memory < 20)"
    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.traefik_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.traefik_service.ecs_service.name
      }
    }
  }
}
*/
