resource "aws_s3_bucket" "test_data" {
  bucket        = local.s3_bucket_name
  force_destroy = var.s3_force_destroy

  tags = merge(local.deployment_tags, {
    Name    = local.s3_bucket_name
    Purpose = "cdc-and-backup-export-e2e"
  })
}

resource "aws_s3_bucket_public_access_block" "test_data" {
  bucket = aws_s3_bucket.test_data.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_s3_bucket_ownership_controls" "test_data" {
  bucket = aws_s3_bucket.test_data.id

  rule {
    object_ownership = "BucketOwnerEnforced"
  }
}

resource "aws_s3_bucket_lifecycle_configuration" "test_data" {
  bucket = aws_s3_bucket.test_data.id

  # Import fixtures are deliberately excluded from expiration: import tasks may
  # run over longer windows and must not have their s3://.../import/ source
  # deleted out from under them. AWS lifecycle filters are positive-only (no
  # negative filter like AliCloud OSS), so each disposable prefix gets its own
  # rule; new disposable prefixes must be added here deliberately.
  rule {
    id     = "expire-cdc-test-objects-after-one-day"
    status = "Enabled"

    filter {
      prefix = "cdc/"
    }

    expiration {
      days = 1
    }
  }

  rule {
    id     = "expire-backup-export-test-objects-after-one-day"
    status = "Enabled"

    filter {
      prefix = "backup-export/"
    }

    expiration {
      days = 1
    }
  }

  rule {
    id     = "expire-cluster-export-test-objects-after-one-day"
    status = "Enabled"

    filter {
      prefix = "cluster-export/"
    }

    expiration {
      days = 1
    }
  }

  rule {
    id     = "abort-incomplete-multipart-uploads"
    status = "Enabled"

    filter {
      prefix = ""
    }

    abort_incomplete_multipart_upload {
      days_after_initiation = 7
    }
  }
}

resource "aws_iam_role" "s3_access" {
  name        = "${local.deployment_name}-s3-access"
  description = "Assumable role for CDC, backup export, Essential export, and import-read E2E access"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = concat(
      [merge(
        {
          Sid    = "AllowConfiguredPrincipals"
          Effect = "Allow"
          Action = "sts:AssumeRole"
          Principal = {
            AWS = sort(tolist(local.s3_role_trusted_principal_arns))
          }
        },
        var.s3_role_external_id == null ? {} : {
          Condition = {
            StringEquals = {
              "sts:ExternalId" = var.s3_role_external_id
            }
          }
        },
      )],
      [for account_id, external_ids in local.s3_assume_role_external_ids_by_account : {
        Sid    = "AllowCloudAssumeRole${account_id}"
        Effect = "Allow"
        Action = "sts:AssumeRole"
        Principal = {
          AWS = "arn:aws:iam::${account_id}:root"
        }
        Condition = {
          StringEquals = {
            "sts:ExternalId" = external_ids
          }
        }
      }],
    )
  })
  max_session_duration = 43200

  tags = merge(local.deployment_tags, {
    Name = "${local.deployment_name}-s3-access"
  })
}

resource "aws_iam_user" "s3_access" {
  name          = "${local.deployment_name}-s3-access"
  path          = "/date-integration/"
  force_destroy = true

  tags = merge(local.deployment_tags, {
    Name = "${local.deployment_name}-s3-access"
  })
}

resource "aws_iam_access_key" "s3_access" {
  user = aws_iam_user.s3_access.name
}

resource "aws_iam_policy" "s3_access" {
  name        = "${local.deployment_name}-s3-access"
  description = "CDC, backup export, Essential export, and import-read access to the deployment test bucket"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "BucketMetadata"
        Effect = "Allow"
        Action = [
          "s3:GetBucketLocation",
          "s3:ListBucket",
          "s3:ListBucketMultipartUploads",
        ]
        Resource = aws_s3_bucket.test_data.arn
      },
      {
        Sid    = "CDCAndBackupExportObjects"
        Effect = "Allow"
        Action = [
          "s3:AbortMultipartUpload",
          "s3:DeleteObject",
          "s3:DeleteObjectTagging",
          "s3:GetObject",
          "s3:GetObjectAttributes",
          "s3:GetObjectTagging",
          "s3:ListMultipartUploadParts",
          "s3:PutObject",
          "s3:PutObjectAcl",
          "s3:PutObjectTagging",
        ]
        Resource = [
          "${aws_s3_bucket.test_data.arn}/cdc/*",
          "${aws_s3_bucket.test_data.arn}/backup-export/*",
          "${aws_s3_bucket.test_data.arn}/cluster-export/*",
        ]
      },
      {
        Sid    = "ImportReadObjects"
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:GetObjectAttributes",
          "s3:GetObjectTagging",
          "s3:ListMultipartUploadParts",
        ]
        Resource = "${aws_s3_bucket.test_data.arn}/import/*"
      },
    ]
  })

  tags = merge(local.deployment_tags, {
    Name = "${local.deployment_name}-s3-access"
  })
}

resource "aws_iam_user_policy_attachment" "s3_access" {
  user       = aws_iam_user.s3_access.name
  policy_arn = aws_iam_policy.s3_access.arn
}

resource "aws_iam_role_policy_attachment" "s3_access" {
  role       = aws_iam_role.s3_access.name
  policy_arn = aws_iam_policy.s3_access.arn
}

resource "aws_s3_bucket_policy" "test_data" {
  bucket = aws_s3_bucket.test_data.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = concat(
      [{
        Sid       = "DenyInsecureTransport"
        Effect    = "Deny"
        Action    = "s3:*"
        Principal = "*"
        Resource = [
          aws_s3_bucket.test_data.arn,
          "${aws_s3_bucket.test_data.arn}/*",
        ]
        Condition = {
          Bool = {
            "aws:SecureTransport" = "false"
          }
        }
      }],
      length(var.s3_backup_export_access_grants) == 0 ? [] : [{
        Sid    = "AllowBackupExportWriters"
        Effect = "Allow"
        Action = [
          "s3:PutObject",
          "s3:PutObjectAcl",
        ]
        Principal = {
          AWS = sort(distinct([
            for grant in values(var.s3_backup_export_access_grants) : "arn:aws:iam::${grant.account_id}:root"
          ]))
        }
        Resource = "${aws_s3_bucket.test_data.arn}/backup-export/*"
        Condition = {
          ArnLike = {
            "aws:PrincipalArn" = sort([
              for grant in values(var.s3_backup_export_access_grants) : grant.role_arn_pattern
            ])
          }
        }
      }],
      length(var.s3_assume_role_grants) == 0 ? [] : [{
        Sid    = "AllowCDCList"
        Effect = "Allow"
        Action = "s3:ListBucket"
        Principal = {
          AWS = aws_iam_role.s3_access.arn
        }
        Resource = aws_s3_bucket.test_data.arn
        Condition = {
          StringLike = {
            "s3:prefix" = ["cdc/", "cdc/*"]
          }
        }
      }],
      length(var.s3_assume_role_grants) == 0 ? [] : [{
        Sid    = "AllowCDCObjects"
        Effect = "Allow"
        Action = [
          "s3:DeleteObject",
          "s3:GetObject",
          "s3:PutObject",
        ]
        Principal = {
          AWS = aws_iam_role.s3_access.arn
        }
        Resource = "${aws_s3_bucket.test_data.arn}/cdc/*"
      }],
    )
  })

  depends_on = [aws_s3_bucket_public_access_block.test_data]
}
