provider "aws" {
  region = var.region
}

terraform {
  backend "s3" {
    bucket  = "prod-songwhip-terraform-state"
    key     = "prod/songwhip-hls-content/terraform.tfstate"
    region  = "us-east-1"
    encrypt = "true"
  }
}

data "cloudflare_zone" "cloudflare_domain" {
  name = var.cloudflare_zone
}

data "cloudflare_zone" "additional_domains" {
  for_each = toset(var.additional_hls_domains)
  name     = each.value
}

module "hls_s3_bucket" {
  source = "git@github.com:theorchard/terraform-s3.git//modules/s3_bucket?ref=3.15.7"

  env                   = var.environment
  bucket_name           = var.bucket_name
  custom_logging_bucket = "${var.environment}-songwhip-s3-logs"
  s3_read_only_policy   = true
  application_family    = var.application_family

  apply_server_side_encryption_by_default = {
    se_algorithm = "AES256"
  }

  bucket_policy_overrides = [
    data.aws_iam_policy_document.service_bucket_policy.json
  ]

  bucket_cors_rule = [
    {
      allowed_headers = ["*"]
      allowed_methods = ["GET", "HEAD"],
      allowed_origins = concat(
        [
          "https://songwhip.com",
          "http://localhost:3000",
          "http://localhost:3001",
        ],
        [for d in var.additional_hls_domains : "https://${d}"]
      )
      expose_headers  = ["ETag", "Content-Type", "Content-Length"]
      max_age_seconds = 3600
    }
  ]
}

data "aws_route53_zone" "route53_zone" {
  name = "aws-prod.songwhip.com"
}

data "aws_acm_certificate" "certificate" {
  domain = "*.${data.aws_route53_zone.route53_zone.name}"
  types  = ["AMAZON_ISSUED"]
}

resource "aws_acm_certificate" "hls_multi_domain" {
  domain_name = "${var.bucket_domain}.${data.aws_route53_zone.route53_zone.name}"
  subject_alternative_names = [
    for d in var.additional_hls_domains : "${var.bucket_domain}.${d}"
  ]
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

locals {
  route53_validation_options = [
    for dvo in aws_acm_certificate.hls_multi_domain.domain_validation_options : dvo
    if endswith(dvo.domain_name, "songwhip.com")
  ]
  cloudflare_validation_options = [
    for dvo in aws_acm_certificate.hls_multi_domain.domain_validation_options : dvo
    if !endswith(dvo.domain_name, "songwhip.com")
  ]
}

resource "aws_route53_record" "hls_cert_validation" {
  for_each = {
    for dvo in local.route53_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  allow_overwrite = true
  name            = each.value.name
  records         = [each.value.record]
  ttl             = 60
  type            = each.value.type
  zone_id         = data.aws_route53_zone.route53_zone.zone_id
}

resource "cloudflare_record" "hls_cert_validation" {
  for_each = {
    for dvo in local.cloudflare_validation_options : dvo.domain_name => {
      name  = dvo.resource_record_name
      value = dvo.resource_record_value
      type  = dvo.resource_record_type
      domain = [
        for d in var.additional_hls_domains : d
        if endswith(dvo.domain_name, d)
      ][0]
    }
  }

  zone_id = data.cloudflare_zone.additional_domains[each.value.domain].id
  name    = trimsuffix(each.value.name, ".")
  value   = trimsuffix(each.value.value, ".")
  type    = each.value.type
  comment = "ACM cert validation for ${var.bucket_domain}.${each.value.domain}"
}

resource "aws_acm_certificate_validation" "hls_multi_domain" {
  certificate_arn = aws_acm_certificate.hls_multi_domain.arn
  validation_record_fqdns = [
    for dvo in aws_acm_certificate.hls_multi_domain.domain_validation_options :
    dvo.resource_record_name
  ]

  depends_on = [
    aws_route53_record.hls_cert_validation,
    cloudflare_record.hls_cert_validation,
  ]
}

data "aws_iam_policy_document" "service_bucket_policy" {
  statement {
    sid = "DenyDestructiveActions"

    effect = "Deny"

    actions = [
      "s3:DeleteBucket",
      "s3:DeleteBucketPolicy",
    ]

    resources = [
      module.hls_s3_bucket.s3_bucket_arn_output,
      "${module.hls_s3_bucket.s3_bucket_arn_output}/*"
    ]

    principals {
      type        = "*"
      identifiers = ["*"]
    }
  }

  statement {
    actions = [
      "s3:GetObject",
    ]

    resources = [
      "${module.hls_s3_bucket.s3_bucket_arn_output}/*"
    ]

    condition {
      test     = "StringEquals"
      variable = "AWS:SourceArn"
      values = [
        aws_cloudfront_distribution.distribution.arn
      ]
    }

    principals {
      type        = "Service"
      identifiers = ["cloudfront.amazonaws.com"]
    }
  }
}

data "aws_cloudfront_response_headers_policy" "response_headers_policy" {
  name = "Managed-SimpleCORS"
}

data "aws_cloudfront_cache_policy" "cache_policy" {
  name = "Managed-CachingOptimized"
}

resource "aws_cloudfront_origin_access_control" "origin_access_control" {
  name                              = "${var.environment}-${var.service_name}"
  description                       = "${var.environment}-${var.service_name} Policy"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

resource "aws_cloudfront_distribution" "distribution" {
  # checkov:skip=CKV2_AWS_47:Ensure AWS CloudFront attached WAFv2 WebACL is configured with AMR for Log4j Vulnerability
  # checkov:skip=CKV_AWS_310:Ensure CloudFront distributions should have origin failover configured
  # checkov:skip=CKV_AWS_374:Ensure AWS CloudFront web distribution has geo restriction enabled

  enabled         = true
  is_ipv6_enabled = false
  comment         = "Distribution for Songwhip HLS transcoded content"
  price_class     = "PriceClass_All"
  http_version    = "http2"
  aliases = concat(
    ["${var.bucket_domain}.aws-prod.songwhip.com"],
    [for d in var.additional_hls_domains : "${var.bucket_domain}.${d}"]
  )
  web_acl_id          = module.songwhip_hls_waf.waf_blocking_arn_output
  default_root_object = "index.html"

  origin {
    domain_name              = "${module.hls_s3_bucket.s3_bucket_name_output}.s3.amazonaws.com"
    origin_id                = "S3-${module.hls_s3_bucket.s3_bucket_name_output}"
    origin_access_control_id = aws_cloudfront_origin_access_control.origin_access_control.id
  }

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

    response_headers_policy_id = data.aws_cloudfront_response_headers_policy.response_headers_policy.id
    cache_policy_id            = data.aws_cloudfront_cache_policy.cache_policy.id
    target_origin_id           = "S3-${module.hls_s3_bucket.s3_bucket_name_output}"
    trusted_key_groups = [
      aws_cloudfront_key_group.hls_key_group.id,
    ]
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  logging_config {
    include_cookies = false
    bucket          = var.cloudfront_logs_bucket
  }

  viewer_certificate {
    acm_certificate_arn      = aws_acm_certificate_validation.hls_multi_domain.certificate_arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }
}

resource "aws_cloudfront_public_key" "hls_public_key" {
  comment     = "HLS Cookie Public Key"
  encoded_key = file("public_key.pem")
  name        = "hls-cookie-public-key"
}

resource "aws_cloudfront_key_group" "hls_key_group" {
  comment = "HLS Cookie Key Group"
  items   = [aws_cloudfront_public_key.hls_public_key.id]
  name    = "hls-cookie-key-group"
}

module "songwhip_hls_waf" {
  source            = "git@github.com:theorchard/terraform-aws-waf.git//?ref=2.0.2"
  environment       = var.environment
  service_name      = "${var.service_name}-cloudfront"
  aws_region        = var.region
  acl_scope         = "CLOUDFRONT"
  count_waf_enabled = false
  block_waf_enabled = true

  // Enabling this causes the error: "Error: WAFv2 RuleGroup not found for name: qa-gsirt-ioc-rule-group"
  gsirt_ip_block_rule_group_enabled = false

  excluded_rules = [
    "SizeRestrictions_BODY",
  ]
}

resource "aws_route53_record" "route53_record" {
  name    = var.bucket_domain
  zone_id = data.aws_route53_zone.route53_zone.zone_id
  type    = "CNAME"
  ttl     = "60"
  records = [aws_cloudfront_distribution.distribution.domain_name]
}

resource "cloudflare_record" "hls_additional_domains" {
  for_each = toset(var.additional_hls_domains)

  zone_id = data.cloudflare_zone.additional_domains[each.key].id
  name    = var.bucket_domain
  value   = aws_cloudfront_distribution.distribution.domain_name
  type    = "CNAME"
  comment = "HLS content for Songwhip - ${each.value} domain"
}
