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"] : []

  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
}

resource "aws_s3_bucket" "main" {
  bucket        = var.bucket_name
  acl           = "private"
  force_destroy = var.force_destroy

  logging {
    target_bucket = var.logs_bucket_id
    target_prefix = "s3/${var.logs_object_prefix}"
  }

  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }

  dynamic "versioning" {
    for_each = local.versioning_enabled
    content {
      enabled = true
    }
  }

  # generic lifecycle_rule
  dynamic "lifecycle_rule" {
    for_each = local.default_lifecycle_enabled ? ["Enabled"] : []
    content {
      id                                     = "GeneralLifecycle"
      enabled                                = true
      abort_incomplete_multipart_upload_days = 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 {
          days = var.noncurrent_version_expiration_days
        }
      }
    }
  }

  dynamic "lifecycle_rule" {
    for_each = var.custom_expiration
    content {
      prefix  = lifecycle_rule.key
      enabled = true

      abort_incomplete_multipart_upload_days = local.abort_incomplete_multipart_upload_days
      expiration {
        days = lifecycle_rule.value
      }
    }
  }

  dynamic "lifecycle_rule" {
    for_each = var.custom_standard_ia
    content {
      prefix  = lifecycle_rule.key
      enabled = true

      abort_incomplete_multipart_upload_days = local.abort_incomplete_multipart_upload_days
      transition {
        days          = lifecycle_rule.value
        storage_class = "STANDARD_IA"
      }
    }
  }

  dynamic "lifecycle_rule" {
    for_each = var.custom_archive
    content {
      prefix  = lifecycle_rule.key
      enabled = true

      abort_incomplete_multipart_upload_days = local.abort_incomplete_multipart_upload_days
      transition {
        days          = lifecycle_rule.value
        storage_class = "GLACIER"
      }
    }
  }

  dynamic "lifecycle_rule" {
    for_each = var.custom_deep_archive
    content {
      prefix  = lifecycle_rule.key
      enabled = true

      abort_incomplete_multipart_upload_days = local.abort_incomplete_multipart_upload_days
      transition {
        days          = lifecycle_rule.value
        storage_class = "DEEP_ARCHIVE"
      }
    }
  }

  dynamic "cors_rule" {
    for_each = local.cors_enabled
    content {
      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)
    }
  }

  tags = merge(
    var.common_tags,
    {
      "Name" = var.bucket_name
    },
  )
}

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

  rule {
    object_ownership = var.object_ownership
  }
}

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

resource "aws_s3_bucket_public_access_block" "this" {
  bucket = aws_s3_bucket.main.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
}
