data "aws_caller_identity" "current" {}

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

locals {
  prisma_sg_exception_tags = {
    "eiso-exception" = "aws.08.30"
  }

  # Determine Route53 zone ID based on environment unless overridden
  route53_zone_id = var.override_route53_zone_id != null ? var.override_route53_zone_id : (var.environment == "dev" ? "Z21XEY26C989RH" : "Z0183645HDT0XCWHLW7S")
}

# Create EFS file system.
resource "aws_efs_file_system" "efs_file_system" {
  # checkov:skip=CKV_AWS_184:EFS is encrypted using an AWS-managed KMS key, which is sufficient at this moment. We will add KMS CMK support when we have an established workflow on how different services can interact using a single CMK.

  creation_token                  = "${var.environment}-${var.service_name}"
  performance_mode                = var.efs_performance_mode
  throughput_mode                 = var.efs_throughput_mode
  encrypted                       = var.efs_encrypted
  provisioned_throughput_in_mibps = var.efs_throughput_mode == "provisioned" ? var.efs_provisioned_throughput_in_mibps : null

  dynamic "lifecycle_policy" {
    for_each = var.efs_transition_to_ia == "" ? [] : [1]
    content {
      transition_to_ia = var.efs_transition_to_ia
    }
  }

  tags = merge(
    local.combined_resource_tags,
    { "efs:backup" = var.backup_enabled ? "enabled" : "disabled" } # This tag is used in backup plans for EFS backup selection
  )
}

# EFS file system policy document.
data "aws_iam_policy_document" "aws_efs_file_system_policy_document" {
  statement {
    effect = "Allow"
    resources = [
      aws_efs_file_system.efs_file_system.arn
    ]

    actions = [
      "elasticfilesystem:DescribeTags",
    ]

    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
    }
  }
}

# EFS file system policy.
resource "aws_efs_file_system_policy" "policy" {
  file_system_id = aws_efs_file_system.efs_file_system.id
  policy         = data.aws_iam_policy_document.aws_efs_file_system_policy_document.json
}

# EFS iam policy document.
data "aws_iam_policy_document" "aws_efs_file_system_iam_policy_document" {
  statement {
    effect = "Allow"
    resources = [
      "*"
    ]

    actions = [
      "elasticfilesystem:ClientMount",
    ]

    condition {
      test     = "StringEquals"
      variable = "elasticfilesystem:AccessPointArn"
      values = [
        aws_efs_access_point.efs_access_point.arn
      ]
    }
  }
  statement {
    effect = "Allow"
    resources = [
      aws_efs_file_system.efs_file_system.arn
    ]

    actions = [
      "elasticfilesystem:ClientWrite",
    ]

    condition {
      test     = "StringEquals"
      variable = "elasticfilesystem:AccessPointArn"
      values = [
        aws_efs_access_point.efs_access_point.arn
      ]
    }
  }
  dynamic "statement" {
    for_each = var.owner_uid == "0" || var.owner_gid == "0" ? [1] : []

    content {
      effect = "Allow"
      resources = [
        aws_efs_file_system.efs_file_system.arn
      ]

      actions = [
        "elasticfilesystem:ClientRootAccess",
      ]

      condition {
        test     = "StringEquals"
        variable = "elasticfilesystem:AccessPointArn"
        values = [
          aws_efs_access_point.efs_access_point.arn
        ]
      }
    }
  }
}

# EFS IAM policy resource
resource "aws_iam_policy" "efs_file_system_iam_policy" {
  name   = "EFS-${var.environment}-${var.service_name}-policy"
  policy = data.aws_iam_policy_document.aws_efs_file_system_iam_policy_document.json
}

# EFS mount targets in AZ.
resource "aws_efs_mount_target" "efs_mount_target" {
  for_each        = var.subnet_ids
  file_system_id  = aws_efs_file_system.efs_file_system.id
  subnet_id       = each.value
  security_groups = aws_security_group.efs_security_group[*].id
}

# EFS security group
resource "aws_security_group" "efs_security_group" {
  name        = "${var.environment}-${var.service_name}-efs-security-group"
  description = "EFS security group for ${var.environment}-${var.service_name}"
  vpc_id      = var.vpc_id

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

# EFS CIDR blocks access rule.
resource "aws_security_group_rule" "allow_efs_cidr_inbound" {
  type              = "ingress"
  from_port         = var.efs_port
  to_port           = var.efs_port
  protocol          = "TCP"
  cidr_blocks       = var.efs_ingress_additional_cidr_blocks
  prefix_list_ids   = data.aws_ec2_managed_prefix_lists.private_subnets.ids
  security_group_id = aws_security_group.efs_security_group.id
}

# EFS file system route53 cname.
resource "aws_route53_record" "networking_efs_cname" {
  count    = var.route53_record_creation_enabled ? 1 : 0
  provider = aws.dns
  zone_id  = local.route53_zone_id
  name     = "${var.environment}-${var.service_name}-efs"
  type     = "CNAME"
  ttl      = var.route53_ttl
  records  = [aws_efs_file_system.efs_file_system.dns_name]
}

# EFS access point resource.
resource "aws_efs_access_point" "efs_access_point" {
  # checkov:skip=CKV_AWS_329:root directories are configured based on a ternary, which Checkov isn't able to parse.
  file_system_id = aws_efs_file_system.efs_file_system.id
  tags           = local.combined_resource_tags

  root_directory {
    path = var.custom_access_point_path != "" ? var.custom_access_point_path : "/${var.service_name}"
    creation_info {
      owner_uid   = var.owner_uid
      owner_gid   = var.owner_gid
      permissions = var.access_point_permissions
    }
  }

  posix_user {
    uid = var.owner_uid
    gid = var.owner_gid
  }
}

resource "aws_efs_backup_policy" "policy" {
  file_system_id = aws_efs_file_system.efs_file_system.id

  # Disable AWS's default backups as we use a custom backup plan which selects EFS filesystems based on the value of the efs:backup tag
  backup_policy {
    status = "DISABLED"
  }
}
