data "aws_wafv2_web_acl" "waf" {
  name  = "${var.environment}-orcd-cloudfront-waf-block"
  scope = "CLOUDFRONT"
}

resource "aws_s3_bucket" "output_bucket" {
  # checkov:skip=CKV_AWS_145:Using KMS encryption is recommended, but AES256 is also sufficient.
  # checkov:skip=CKV_AWS_21:Versioning should be enabled on S3 buckets

  bucket = "${var.environment}-${var.output_bucket_name}"
}

resource "aws_s3_bucket_acl" "output_bucket" {
  bucket = aws_s3_bucket.output_bucket.id
  acl    = "private"
}

resource "aws_s3_bucket_accelerate_configuration" "output_bucket" {
  bucket = aws_s3_bucket.output_bucket.id
  status = "Enabled"
}

resource "aws_s3_bucket_versioning" "output_bucket" {
  bucket = aws_s3_bucket.output_bucket.id

  versioning_configuration {
    status = var.versioning_enabled ? "Enabled" : "Disabled"
  }
}

resource "aws_s3_bucket_cors_configuration" "output_bucket" {
  count  = length(var.output_bucket_cors) == 0 ? 0 : 1
  bucket = aws_s3_bucket.output_bucket.id

  dynamic "cors_rule" {
    for_each = var.output_bucket_cors
    content {
      allowed_headers = cors_rule.value.allowed_headers
      allowed_methods = cors_rule.value.allowed_methods
      allowed_origins = cors_rule.value.allowed_origins
      expose_headers  = cors_rule.value.expose_headers
      max_age_seconds = cors_rule.value.max_age_seconds
    }
  }
}

resource "aws_s3_bucket_logging" "output_bucket" {
  count = length(var.logging_target_bucket)

  bucket        = aws_s3_bucket.output_bucket.id
  target_bucket = var.logging_target_bucket[count.index]
  target_prefix = "${var.environment}-${var.output_bucket_name}"
}

resource "aws_s3_bucket_server_side_encryption_configuration" "output_bucket" {
  bucket = aws_s3_bucket.output_bucket.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

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

  rule {
    id     = "abort-incomplete-multipart-upload-days"
    status = "Enabled"

    filter {} 
  
    abort_incomplete_multipart_upload {
      days_after_initiation = 7
    }
  }

  # Rule 2: Transition objects to S3 Glacier Deep Archive
  rule {
    id     = "transition-to-deep-archive"
    status = "Enabled"

    filter {
      prefix = ""
    }

    transition {
      days          = 180 
      storage_class = "DEEP_ARCHIVE"
    }
  }
}

resource "aws_s3_bucket_public_access_block" "access_output" {
  bucket = aws_s3_bucket.output_bucket.id

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

resource "aws_cloudfront_origin_access_identity" "output_bucket_origin_access_identity" {
  comment = "access-identity-${aws_s3_bucket.output_bucket.id}.s3.amazonaws.com"
}

data "aws_iam_policy_document" "output_bucket_policy" {
  statement {
    actions   = ["s3:GetObject"]
    resources = ["arn:aws:s3:::${aws_s3_bucket.output_bucket.id}/*"]

    principals {
      type        = "AWS"
      identifiers = [aws_cloudfront_origin_access_identity.output_bucket_origin_access_identity.iam_arn]
    }
  }

  statement {
    effect  = "Deny"
    actions = ["s3:PutObject"]
    not_resources = [
      "arn:aws:s3:::${aws_s3_bucket.output_bucket.id}/*.pdf",
      "arn:aws:s3:::${aws_s3_bucket.output_bucket.id}/*.jpg",
      "arn:aws:s3:::${aws_s3_bucket.output_bucket.id}/*.jpeg",
      "arn:aws:s3:::${aws_s3_bucket.output_bucket.id}/*.tif",
      "arn:aws:s3:::${aws_s3_bucket.output_bucket.id}/*.wav",
      "arn:aws:s3:::${aws_s3_bucket.output_bucket.id}/*.flac",
      "arn:aws:s3:::${aws_s3_bucket.output_bucket.id}/*.dat",
      "arn:aws:s3:::${aws_s3_bucket.output_bucket.id}/*.mp3",
    ]
    principals {
      type        = "AWS"
      identifiers = [aws_cloudfront_origin_access_identity.output_bucket_origin_access_identity.iam_arn]
    }
  }

  statement {
    sid    = "DenyRequestsWithoutSSL"
    effect = "Deny"
    principals {
      type        = "*"
      identifiers = ["*"]
    }
    actions = ["s3:*"]
    resources = [
      aws_s3_bucket.output_bucket.arn,
      "${aws_s3_bucket.output_bucket.arn}/*",
    ]
    condition {
      test     = "Bool"
      variable = "aws:SecureTransport"
      values   = ["false"]
    }
  }
}

resource "aws_s3_bucket_policy" "podcast_output_bucket_policy" {
  policy = data.aws_iam_policy_document.output_bucket_policy.json
  bucket = aws_s3_bucket.output_bucket.id
}

resource "aws_cloudfront_distribution" "output_assets_cdn" {
  # checkov:skip=CKV_AWS_310:failover criteria is not applicable to this case
  # checkov:skip=CKV_AWS_305:default root object is not applicable to this case
  # checkov:skip=CKV2_AWS_47:We have rules to protect form Log4j vulnerability
  # checkov:skip=CKV_AWS_374 Geo restriction is disabled
  enabled         = true
  is_ipv6_enabled = false
  comment         = "Distribution for podcast Megaphone assets ${var.output_assets_domain}"
  price_class     = "PriceClass_All"
  http_version    = "http2"
  web_acl_id      = data.aws_wafv2_web_acl.waf.arn
  aliases         = [var.output_assets_domain]

  origin {
    domain_name = "${aws_s3_bucket.output_bucket.id}.s3.amazonaws.com"
    origin_id   = "S3-${aws_s3_bucket.output_bucket.id}"

    s3_origin_config {
      origin_access_identity = aws_cloudfront_origin_access_identity.output_bucket_origin_access_identity.cloudfront_access_identity_path
    }
  }

  default_cache_behavior {
    viewer_protocol_policy = "https-only"
    compress               = false
    allowed_methods        = ["GET", "HEAD", "OPTIONS"]
    cached_methods         = ["GET", "HEAD"]

    response_headers_policy_id = var.environment == "dev" ? null : data.aws_cloudfront_response_headers_policy.response_headers_policy[0].id

    target_origin_id = "S3-${aws_s3_bucket.output_bucket.id}"
    min_ttl          = var.cdn_min_ttl
    default_ttl      = var.cdn_default_ttl
    max_ttl          = var.cdn_max_ttl
    trusted_signers  = ["self"]

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
      headers = [
        "Origin",
        "Access-Control-Request-Headers",
        "Access-Control-Request-Method",
      ]
    }
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.cloudfront_logs_bucket
  }

  viewer_certificate {
    acm_certificate_arn      = var.output_assets_cdn_certificate_arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }
}

data "aws_cloudfront_response_headers_policy" "response_headers_policy" {
  count = var.environment == "dev" ? 0 : 1
  name = "${var.environment}-default"
}

data "aws_iam_policy_document" "output_bucket_readwrite_policy" {
  statement {
    actions = [
      "s3:ListBucket",
    ]

    effect    = "Allow"
    resources = [aws_s3_bucket.output_bucket.arn]
  }

  statement {
    actions = [
      "s3:GetObject",
      "s3:HeadObject",
      "s3:CopyObject",
      "s3:PutObject",
      "s3:PutObjectAcl",
      "s3:PutObjectTagging",
      "s3:PutObjectVersionAcl",
      "s3:PutObjectVersionTagging"
    ]

    effect    = "Allow"
    resources = ["${aws_s3_bucket.output_bucket.arn}/*"]
  }
}

resource "aws_iam_policy" "output_bucket_readwrite_policy" {
  name        = "S3-${aws_s3_bucket.output_bucket.id}-RW"
  description = "Read / Write only access policy for output bucket"
  policy      = data.aws_iam_policy_document.output_bucket_readwrite_policy.json
}
