data "aws_ec2_managed_prefix_lists" "private_subnets" {
  tags = {
    environment  = var.environment
    subnet_group = "all"
    tier         = "private"
    vpc_id       = var.vpc_id
  }
}

data "aws_ec2_managed_prefix_list" "vpn_users" {
  for_each = toset(var.vpn_prefix_list_names)
  name     = each.value
}

module "cache_sns_topic" {
  source = "git@github.com:theorchard/terraform-sns.git//?ref=1.1.4"

  environment        = var.environment
  sns_topic_name     = "${var.service_name}-elasticache"
  application_family = var.application_family
}

resource "aws_security_group" "elasticache_redis_group" {
  count       = var.cache_engine == "redis" ? 1 : 0
  name        = "${var.environment}-${var.service_name}-elasticache-security-group"
  description = "${var.environment}-${var.service_name}-elasticache-security-group"
  vpc_id      = var.vpc_id

  tags = merge(local.combined_resource_tags, local.primsa_sg_exception_tags)
}

resource "aws_security_group_rule" "allow_redis" {
  count             = var.cache_engine == "redis" ? 1 : 0
  security_group_id = aws_security_group.elasticache_redis_group[0].id
  type              = "ingress"
  to_port           = 6379
  from_port         = 6379
  protocol          = "TCP"
  prefix_list_ids   = concat(
    data.aws_ec2_managed_prefix_lists.private_subnets.ids,
    [for pl in data.aws_ec2_managed_prefix_list.vpn_users : pl.id]
  )
}

resource "aws_security_group_rule" "allow_additional_redis_access_groups" {
  count             = var.cache_engine == "redis" ? var.additional_cidr_blocks_enabled ? 1 : 0 : 0
  security_group_id = aws_security_group.elasticache_redis_group[0].id
  type              = "ingress"
  to_port           = 6379
  from_port         = 6379
  protocol          = "TCP"
  cidr_blocks       = var.additional_cidr_blocks
}

resource "aws_security_group_rule" "allow_additional_source_security_group_ids_redis" {
  count = var.cache_engine == "redis" && length(var.additional_source_security_group_ids) != 0 ? length(var.additional_source_security_group_ids) : 0

  security_group_id        = aws_security_group.elasticache_redis_group[0].id
  type                     = "ingress"
  to_port                  = 6379
  from_port                = 6379
  protocol                 = "TCP"
  source_security_group_id = var.additional_source_security_group_ids[count.index]
}

resource "aws_security_group_rule" "allow_redis_egress" {
  count             = var.cache_engine == "redis" ? 1 : 0
  security_group_id = aws_security_group.elasticache_redis_group[0].id
  type              = "egress"
  to_port           = 0
  from_port         = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
}

resource "aws_security_group" "elasticache_memcached_group" {
  count       = var.cache_engine == "memcached" ? 1 : 0
  name        = "${var.environment}-${var.service_name}-elasticache-security-group"
  description = "${var.environment}-${var.service_name}-elasticache-security-group"
  vpc_id      = var.vpc_id

  tags = merge(local.combined_resource_tags, local.primsa_sg_exception_tags)
}

resource "aws_security_group_rule" "allow_memcached" {
  count             = var.cache_engine == "memcached" ? 1 : 0
  security_group_id = aws_security_group.elasticache_memcached_group[0].id
  type              = "ingress"
  to_port           = 11211
  from_port         = 11211
  protocol          = "TCP"
  prefix_list_ids   = concat(
    data.aws_ec2_managed_prefix_lists.private_subnets.ids,
    [for pl in data.aws_ec2_managed_prefix_list.vpn_users : pl.id]
  )
}

resource "aws_security_group_rule" "allow_additional_memcached_access_groups" {
  count             = var.cache_engine == "memcached" ? var.additional_cidr_blocks_enabled ? 1 : 0 : 0
  security_group_id = aws_security_group.elasticache_memcached_group[0].id
  type              = "ingress"
  to_port           = 11211
  from_port         = 11211
  protocol          = "TCP"
  cidr_blocks       = var.additional_cidr_blocks
}

resource "aws_security_group_rule" "allow_additional_source_security_group_ids_memcached" {
  count = var.cache_engine == "memcached" && length(var.additional_source_security_group_ids) != 0 ? length(var.additional_source_security_group_ids) : 0

  security_group_id        = aws_security_group.elasticache_memcached_group[0].id
  type                     = "ingress"
  to_port                  = 11211
  from_port                = 11211
  protocol                 = "TCP"
  source_security_group_id = var.additional_source_security_group_ids[count.index]
}

resource "aws_security_group_rule" "allow_memcached_egress" {
  count             = var.cache_engine == "memcached" ? 1 : 0
  security_group_id = aws_security_group.elasticache_memcached_group[0].id
  type              = "egress"
  to_port           = 0
  from_port         = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
}


resource "aws_elasticache_replication_group" "redis" {
  # checkov:skip=CKV_AWS_191:Redis is encrypted at rest using AWS service-managed encryption. We will add KMS CMK support when we have an established workflow on how different services can interact using a single CMK.
  # checkov:skip=CKV2_AWS_50:Ensure AWS ElastiCache Redis cluster with Multi-AZ Automatic Failover feature set to enabled. Disabling check for single node clusters, failover is enabled by default for clusters with more than 1 node.

  count                      = var.cache_engine == "redis" ? 1 : 0
  replication_group_id       = "${var.environment}-${var.service_name}"
  description                = "${var.environment}-${var.service_name}"
  node_type                  = var.cache_node_type
  num_cache_clusters         = var.cache_node_count
  engine_version             = var.redis_engine_version
  parameter_group_name       = var.cache_parameter_group_name != "" ? var.cache_parameter_group_name : local.cache_parameter_default_group[var.cache_engine]
  subnet_group_name          = var.cache_subnet_group_name
  security_group_ids         = [aws_security_group.elasticache_redis_group[0].id]
  snapshot_window            = var.redis_snapshot_window
  snapshot_retention_limit   = var.redis_snapshot_retention_limit
  maintenance_window         = var.cache_maintenance_window
  auto_minor_version_upgrade = var.auto_minor_version_upgrade_enabled
  automatic_failover_enabled = var.cache_node_count > 1 ? true : false
  multi_az_enabled           = var.cache_node_count > 1 ? true : false
  notification_topic_arn     = module.cache_sns_topic.topic_arn

  at_rest_encryption_enabled = var.cache_at_rest_encryption_enabled
  transit_encryption_enabled = var.cache_transit_encryption_enabled
  auth_token                 = var.cache_auth_token

  port              = 6379
  apply_immediately = true

  tags = local.combined_resource_tags

  # Ignore selected changes
  lifecycle {
    ignore_changes = [engine_version]
  }
}

resource "aws_elasticache_cluster" "memcached" {
  count                  = var.cache_engine == "memcached" ? 1 : 0
  cluster_id             = "${var.environment}-${var.service_name}"
  engine                 = "memcached"
  engine_version         = var.memcached_engine_version
  maintenance_window     = var.cache_maintenance_window
  node_type              = var.cache_node_type
  num_cache_nodes        = var.cache_node_count
  parameter_group_name   = var.cache_parameter_group_name != "" ? var.cache_parameter_group_name : local.cache_parameter_default_group[var.cache_engine]
  subnet_group_name      = var.cache_subnet_group_name
  security_group_ids     = [aws_security_group.elasticache_memcached_group[0].id]
  notification_topic_arn = module.cache_sns_topic.topic_arn
  az_mode                = var.cache_node_count > 1 ? "cross-az" : "single-az"
  port                   = 11211
  apply_immediately      = true

  tags = {
    terraformed  = "true"
    Environment  = var.environment
    service_name = var.service_name
  }

  # Ignore selected changes
  lifecycle {
    ignore_changes = [engine_version]
  }
}

resource "aws_route53_record" "networking_memcached_cluster_cname" {
  provider = aws.dns
  count    = var.cache_engine == "memcached" && var.route53_record_creation_enabled ? 1 : 0

  zone_id = local.route53_zone_id
  name    = "${var.environment}-${var.service_name}-${var.cache_engine}"
  type    = "CNAME"
  ttl     = var.route53_ttl
  records = aws_elasticache_cluster.memcached[*].cluster_address
}

resource "aws_route53_record" "networking_redis_cluster_cname" {
  provider = aws.dns
  count    = var.cache_engine == "redis" && var.route53_record_creation_enabled ? 1 : 0

  zone_id = local.route53_zone_id
  name    = "${var.environment}-${var.service_name}-${var.cache_engine}"
  type    = "CNAME"
  ttl     = var.route53_ttl
  records = aws_elasticache_replication_group.redis[*].primary_endpoint_address
}
