module "default_bucket_policy" {
  source              = "../bucket_policies/default_policy_template"
  template_bucket_arn = var.template_bucket_arn
}

locals {
  versioning_enabled = var.versioning ? ["enabled"] : []
  cors_enabled       = var.cors != null ? ["enabled"] : []
  acl_enabled        = var.object_ownership != "BucketOwnerEnforced" ? ["enabled"] : []

  bucket_policy         = var.bucket_policy == "" && var.default_policy ? module.default_bucket_policy.document.json : var.bucket_policy
  bucket_policy_enabled = local.bucket_policy != "" ? ["enabled"] : []

  standard_ia_enabled  = var.standard_ia_days > 0
  archive_enabled      = var.archive_days > 0
  deep_archive_enabled = var.deep_archive_days > 0
  # Removing incomplete parts can only be a part of a valid lifecycle
  # If no other lifecycle options has been selected, it will set expirations days to 0 (which have no effect) thus enabling removing incomplete parts
  expiration_enabled = var.expiration_days > 0 || var.remove_incomplete_multipart

  # Check whether default lifecycle should be enabled
  default_lifecycle_enabled = local.expiration_enabled || local.standard_ia_enabled || local.archive_enabled || local.deep_archive_enabled

  # Don't pass abort_incomplete_multipart_upload_days if it's explicitly disabled via parameters
  abort_incomplete_multipart_upload_days = var.remove_incomplete_multipart ? var.abort_incomplete_multipart_upload_days : null

  # Create a map from var.intelligent_tiering_rules so it can be used in for_each statement
  intelligent_tiering_rules                      = { for r in var.intelligent_tiering_rules : r.name => r }
  intelligent_tiering_rules_with_archive_options = { for r in var.intelligent_tiering_rules : r.name => r if(r.deep_archive_days != 0 || r.archive_days != 0) }
}

/*
moved {
  from = aws_s3_bucket.main
  to   = aws_s3_bucket.this
}
*/

resource "aws_s3_bucket" "this" {
  bucket        = var.bucket_name
  force_destroy = var.force_destroy
  tags          = merge(var.common_tags, { "Name" = var.bucket_name })
}

resource "aws_s3_bucket_acl" "this" {
  for_each = toset(local.acl_enabled)
  bucket   = aws_s3_bucket.this.id
  acl      = "private"
}

resource "aws_s3_bucket_logging" "this" {
  bucket        = aws_s3_bucket.this.id
  target_bucket = var.logs_bucket_id
  target_prefix = "s3/${var.logs_object_prefix}"
}

resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
  bucket = aws_s3_bucket.this.id
  rule {
    bucket_key_enabled = var.kms_master_key_id != "" # false if there are no KMS key
    apply_server_side_encryption_by_default {
      kms_master_key_id = var.kms_master_key_id != "" ? var.kms_master_key_id : null
      sse_algorithm     = var.kms_master_key_id != "" ? "aws:kms" : "AES256"
    }
  }
}

resource "aws_s3_bucket_versioning" "this" {
  for_each = toset(local.versioning_enabled)
  bucket   = aws_s3_bucket.this.id

  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_lifecycle_configuration" "this" {
  bucket = aws_s3_bucket.this.id
  # generic lifecycle_rule
  dynamic "rule" {
    for_each = local.default_lifecycle_enabled ? ["Enabled"] : []
    content {
      id     = "GeneralLifecycle"
      status = "Enabled"

      abort_incomplete_multipart_upload {
        days_after_initiation = local.abort_incomplete_multipart_upload_days
      }

      dynamic "expiration" {
        for_each = local.expiration_enabled ? ["Enabled"] : []
        content {
          days                         = var.expiration_days
          expired_object_delete_marker = var.expiration_days == 0 ? false : null
        }
      }

      dynamic "transition" {
        for_each = local.standard_ia_enabled ? ["Enabled"] : []
        content {
          days          = var.standard_ia_days
          storage_class = "STANDARD_IA"
        }
      }

      dynamic "transition" {
        for_each = local.archive_enabled ? ["Enabled"] : []
        content {
          days          = var.archive_days
          storage_class = "GLACIER"
        }
      }

      dynamic "transition" {
        for_each = local.deep_archive_enabled ? ["Enabled"] : []
        content {
          days          = var.deep_archive_days
          storage_class = "DEEP_ARCHIVE"
        }
      }

      dynamic "noncurrent_version_expiration" {
        for_each = var.noncurrent_version_expiration_days != 0 ? ["Enabled"] : []
        content {
          noncurrent_days = var.noncurrent_version_expiration_days
        }
      }
    }
  }

  dynamic "rule" {
    for_each = var.custom_expiration
    content {
      id     = rule.key
      status = "Enabled"

      filter {
        prefix = rule.key
      }

      abort_incomplete_multipart_upload {
        days_after_initiation = local.abort_incomplete_multipart_upload_days
      }

      expiration {
        days = rule.value
      }
    }
  }

  dynamic "rule" {
    for_each = var.custom_standard_ia
    content {
      id     = rule.key
      status = "Enabled"

      filter {
        prefix = rule.key
      }

      abort_incomplete_multipart_upload {
        days_after_initiation = local.abort_incomplete_multipart_upload_days
      }

      transition {
        days          = rule.value
        storage_class = "STANDARD_IA"
      }
    }
  }

  dynamic "rule" {
    for_each = var.custom_archive
    content {
      id     = rule.key
      status = "Enabled"

      filter {
        prefix = rule.key
      }

      abort_incomplete_multipart_upload {
        days_after_initiation = local.abort_incomplete_multipart_upload_days
      }

      transition {
        days          = rule.value
        storage_class = "GLACIER"
      }
    }
  }

  dynamic "rule" {
    for_each = var.custom_deep_archive
    content {
      id     = rule.key
      status = "Enabled"

      filter {
        prefix = rule.key
      }

      abort_incomplete_multipart_upload {
        days_after_initiation = local.abort_incomplete_multipart_upload_days
      }

      transition {
        days          = rule.value
        storage_class = "DEEP_ARCHIVE"
      }
    }
  }

  dynamic "rule" {
    for_each = local.intelligent_tiering_rules
    content {
      id     = "IntelligentTier${rule.value.name}"
      status = rule.value.enabled ? "Enabled" : "Disabled"

      dynamic "filter" {
        for_each = rule.value.prefix != "" ? ["Enabled"] : []
        content {
          prefix = each.value.prefix
        }
      }

      transition {
        days          = rule.value.transition
        storage_class = "INTELLIGENT_TIERING"
      }
    }
  }
}

resource "aws_s3_bucket_intelligent_tiering_configuration" "this" {
  for_each = local.intelligent_tiering_rules_with_archive_options

  bucket = aws_s3_bucket.this.id
  name   = "IntelligentTier${each.value.name}Archive"

  status = each.value.enabled ? "Enabled" : "Disabled"

  dynamic "filter" {
    for_each = each.value.prefix != "" ? ["Enabled"] : []
    content {
      prefix = each.value.prefix
    }
  }

  dynamic "tiering" {
    for_each = each.value.archive_days != 0 ? ["Enabled"] : []
    content {
      access_tier = "ARCHIVE_ACCESS"
      days        = each.value.archive_days
    }
  }

  dynamic "tiering" {
    for_each = each.value.deep_archive_days != 0 ? ["Enabled"] : []
    content {
      access_tier = "DEEP_ARCHIVE_ACCESS"
      days        = each.value.deep_archive_days
    }
  }
}

resource "aws_s3_bucket_cors_configuration" "this" {
  for_each = toset(local.cors_enabled)
  bucket   = aws_s3_bucket.this.id

  cors_rule {
    allowed_headers = lookup(var.cors, "allowed_headers", null)
    allowed_methods = lookup(var.cors, "allowed_methods")
    allowed_origins = lookup(var.cors, "allowed_origins")
    expose_headers  = lookup(var.cors, "expose_headers", null)
    max_age_seconds = lookup(var.cors, "max_age_seconds", null)
  }
}

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

  rule {
    object_ownership = var.object_ownership
  }
}

resource "aws_s3_bucket_policy" "this" {
  count  = length(local.bucket_policy_enabled)
  bucket = aws_s3_bucket.this.id
  policy = replace(local.bucket_policy, var.template_bucket_arn, aws_s3_bucket.this.arn)
  depends_on = [
    aws_s3_bucket_ownership_controls.this
  ]
}

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

  block_public_acls       = var.block_public_acls
  block_public_policy     = var.block_public_policy
  ignore_public_acls      = var.ignore_public_acls
  restrict_public_buckets = var.restrict_public_buckets
}
