resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
  for_each = local.s3_cdn_map
  comment  = each.value.name
}

resource "aws_s3_bucket_policy" "origin" {
  for_each = local.s3_cdn_map

  bucket = replace(each.value.s3_bucket_arn, "arn:aws:s3:::", "")
  policy = data.aws_iam_policy_document.s3_origin[each.key].json
}

resource "aws_cloudfront_distribution" "cdn" {
  for_each = local.cdn_map
  comment  = "${each.value.name}-cdn"
  # requires ssl cert verification
  aliases         = each.value.ssl_cert_verified ? each.value.public_dns : []
  price_class     = "PriceClass_200"
  enabled         = true
  is_ipv6_enabled = false
  http_version    = "http2and3"

  default_root_object = local.default_root_object[each.key]

  origin {
    domain_name = each.value.origin_type == "s3" ? each.value.s3_bucket_domain : each.value.lb_dns_name
    origin_id   = "${each.value.name}-cdn"

    dynamic "s3_origin_config" {
      for_each = each.value.origin_type == "s3" ? [1] : []
      content {
        origin_access_identity = aws_cloudfront_origin_access_identity.origin_access_identity[each.key].cloudfront_access_identity_path
      }
    }

    dynamic "custom_origin_config" {
      for_each = each.value.origin_type == "s3" ? [] : [1]
      content {
        http_port              = 80
        https_port             = 443
        origin_protocol_policy = "https-only"
        origin_ssl_protocols   = ["TLSv1.2"]
      }
    }
  }

  default_cache_behavior {
    allowed_methods = each.value.origin_type == "s3" ? ["GET", "HEAD", "OPTIONS"] : ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods  = ["GET", "HEAD"]
    target_origin_id           = "${each.value.name}-cdn"
    response_headers_policy_id = "60669652-455b-4ae9-85a4-c4c02393f86c"

    compress = true
    forwarded_values {
      query_string = each.value.origin_type != "s3"

      cookies {
        forward = each.value.origin_type == "s3" ? "none" : "all"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 3600
  }

  # SPA routing: S3 returns 403/404 for deep paths; serve index.html instead
  dynamic "custom_error_response" {
    for_each = each.value.origin_type == "s3" ? [403, 404] : []
    content {
      error_code            = custom_error_response.value
      response_code         = 200
      response_page_path    = "/index.html"
      error_caching_min_ttl = 0
    }
  }

  # requires ssl cert verification
  dynamic "viewer_certificate" {
    for_each = each.value.ssl_cert_verified ? [1] : []
    content {
      acm_certificate_arn      = each.value.ssl_cert_arn
      ssl_support_method       = "sni-only"
      minimum_protocol_version = "TLSv1.2_2021"
    }
  }

  dynamic "viewer_certificate" {
    for_each = each.value.ssl_cert_verified ? [] : [1]
    content {
      cloudfront_default_certificate = true
    }
  }

  web_acl_id = try(each.value.waf_acl_id, null)
  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  logging_config {
    bucket          = aws_s3_bucket.cdn_logs[each.value.name].bucket_domain_name
    include_cookies = false
  }

  tags = merge(var.service_tags, {
    Name = "${each.value.name}-cdn"
  })

  depends_on = [
    aws_s3_bucket.cdn_logs,
    aws_s3_bucket_ownership_controls.cdn_logs,
    aws_s3_bucket_acl.cdn_logs,
    aws_s3_bucket_policy.origin,
  ]
}

resource "aws_s3_bucket" "cdn_logs" {
  for_each = local.cdn_map
  bucket   = "${each.value.name}-logs"

  tags = merge(var.service_tags, {
    Name = "${each.value.name}-logs"
  })
}

resource "aws_s3_bucket_versioning" "cdn_logs" {
  for_each   = local.cdn_map
  bucket     = aws_s3_bucket.cdn_logs[each.value.name].id
  depends_on = [aws_s3_bucket.cdn_logs]
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "cdn_logs" {
  for_each   = local.cdn_map
  bucket     = aws_s3_bucket.cdn_logs[each.value.name].id
  depends_on = [aws_s3_bucket.cdn_logs]
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
    bucket_key_enabled = true
  }
}

resource "aws_s3_bucket_ownership_controls" "cdn_logs" {
  for_each = local.cdn_map
  bucket   = aws_s3_bucket.cdn_logs[each.value.name].id
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}

resource "aws_s3_bucket_acl" "cdn_logs" {
  for_each   = local.cdn_map
  bucket     = aws_s3_bucket.cdn_logs[each.value.name].id
  depends_on = [aws_s3_bucket.cdn_logs, aws_s3_bucket_ownership_controls.cdn_logs]

  access_control_policy {
    grant {
      grantee {
        id   = data.aws_canonical_user_id.current.id
        type = "CanonicalUser"
      }
      permission = "FULL_CONTROL"
    }
    grant {
      grantee {
        id   = data.aws_cloudfront_log_delivery_canonical_user_id.cloudfront.id
        type = "CanonicalUser"
      }
      permission = "FULL_CONTROL"
    }
    owner {
      id = data.aws_canonical_user_id.current.id
    }
  }
}

resource "aws_s3_bucket_lifecycle_configuration" "cdn_logs" {
  for_each   = local.cdn_map
  bucket     = aws_s3_bucket.cdn_logs[each.value.name].id
  depends_on = [aws_s3_bucket.cdn_logs]
  rule {
    id = "Expire in 365 Days"
    expiration {
      days = 365
    }
    noncurrent_version_expiration {
      noncurrent_days = 365
    }
    filter {
      prefix = ""
    }
    status = "Enabled"
  }
}

resource "aws_s3_bucket_policy" "cdn_logs" {
  for_each   = local.cdn_map
  bucket     = aws_s3_bucket.cdn_logs[each.value.name].id
  depends_on = [aws_s3_bucket.cdn_logs]
  policy     = data.aws_iam_policy_document.cdn_logs_enforce_ssl[each.value.name].json
}

resource "aws_s3_bucket_logging" "cdn_logs" {
  for_each = local.cdn_map
  bucket   = aws_s3_bucket.cdn_logs[each.value.name].bucket

  target_bucket = var.access_logs_bucket
  target_prefix = ""
  target_object_key_format {
    partitioned_prefix {
      partition_date_source = "EventTime"
    }
  }
}

resource "aws_s3_bucket_public_access_block" "cdn_logs" {
  for_each                = local.cdn_map
  bucket                  = aws_s3_bucket.cdn_logs[each.value.name].id
  block_public_acls       = true
  ignore_public_acls      = true
  block_public_policy     = true
  restrict_public_buckets = true
}
