locals {
  default_policy = {
    "s3" = [data.aws_iam_policy_document.s3_access_logs_bucket_policy.json]
    "lb" = [data.aws_iam_policy_document.lb_access_logs_bucket_policy.json]
  }

  bucket_policy_overrides = concat(lookup(local.default_policy, var.access_log_type, []), var.bucket_policy_overrides)

  regional_postfix = var.regional_postfix == "" ? "" : "-${var.regional_postfix}"

  sqs_res_counter = var.custom_sqs_queue_arn == "" ? 1 : 0

  pre_2022_lb_regions = [
    "us-east-1",
    "us-east-2",
    "us-west-1",
    "us-west-2",
    "af-south-1",
    "ap-east-1",
    "ap-southeast-3",
    "ap-south-1",
    "ap-northeast-3",
    "ap-northeast-2",
    "ap-southeast-1",
    "ap-southeast-2",
    "ap-northeast-1",
    "ca-central-1",
    "eu-central-1",
    "eu-west-1",
    "eu-west-2",
    "eu-south-1",
    "eu-west-3",
    "eu-north-1",
    "me-south-1",
    "sa-east-1",
  ]
}

data "aws_region" "current" {}

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

  env                = var.environment
  application_family = var.application_family
  bucket_name        = "${var.service_name}-${var.access_log_type}-logs${local.regional_postfix}"

  apply_server_side_encryption_by_default = {
    sse_algorithm = "AES256"
  }

  // Don't configure access logging for S3 logging buckets themselves
  access_logging_enabled                                = var.access_log_type != "s3"
  custom_logging_bucket                                 = var.custom_access_logging_bucket
  custom_logging_prefix                                 = var.custom_access_logging_prefix
  bucket_policy_overrides                               = local.bucket_policy_overrides
  acl_policy_grants                                     = var.acl_policy_grants
  lifecycle_rules_options_noncurrent_version_transition = var.lifecycle_rules_options_noncurrent_version_transition
  lifecycle_rules_options_current_version_transition    = var.lifecycle_rules_options_current_version_transition
  apply_replication_destination_configuration           = var.apply_replication_destination_configuration
}

# Default S3 logs access policy
data "aws_iam_policy_document" "s3_access_logs_bucket_policy" {
  statement {
    sid    = "S3ServerAccessLogsPolicy"
    effect = "Allow"
    actions = [
      "s3:PutObject",
    ]

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

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

    condition {
      test     = "StringEquals"
      variable = "aws:SourceAccount"
      values = [
        data.aws_caller_identity.current.account_id
      ]
    }
  }
}

# Default LB logs access bucket policy
data "aws_elb_service_account" "current" {
  count = contains(local.pre_2022_lb_regions, data.aws_region.current.id) ? 1 : 0
}

data "aws_iam_policy_document" "lb_access_logs_bucket_policy" {
  statement {
    sid       = "AllowElbServiceAccount"
    effect    = "Allow"
    resources = ["${module.access_logs_bucket.s3_bucket_arn_output}/*"]
    actions = [
      "s3:PutObject",
    ]

    principals {
      type = contains(local.pre_2022_lb_regions, data.aws_region.current.id) ? "AWS" : "Service"
      identifiers = contains(
        local.pre_2022_lb_regions,
        data.aws_region.current.id
      ) ? [data.aws_elb_service_account.current[0].arn] : ["logdelivery.elasticloadbalancing.amazonaws.com"]
    }
  }
}

module "logs_sqs_queue_with_deadletter" {
  source = "git@github.com:theorchard/terraform-sqs.git?ref=2.3.3"
  count  = local.sqs_res_counter

  environment                          = var.environment
  application_family                   = var.application_family
  queue_name                           = "${var.service_name}-${var.access_log_type}-logs${local.regional_postfix}"
  sqs_deadletter_enabled               = "true"
  deadletter_max_recieve_count         = 200
  deadletter_delay_seconds             = 10
  deadletter_max_message_size          = 2048
  deadletter_message_retention_seconds = 86400
  deadletter_receive_wait_time_seconds = 10
  sqs_kms_master_key_id                = aws_kms_alias.access_logs_kms_key_alias.name
}

resource "aws_sns_topic" "logs_sns_topic" {
  name              = "${var.environment}-${var.service_name}-${var.access_log_type}-logs${local.regional_postfix}"
  kms_master_key_id = aws_kms_alias.access_logs_kms_key_alias.name
}

data "aws_iam_policy_document" "logs_sns_topic_policy" {
  override_policy_documents = var.sns_override_policy_documents

  statement {
    actions = [
      "SNS:Publish",
    ]

    condition {
      test     = "ArnLike"
      variable = "AWS:SourceArn"

      values = [
        module.access_logs_bucket.s3_bucket_arn_output,
      ]
    }

    effect = "Allow"

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

    resources = [
      aws_sns_topic.logs_sns_topic.arn,
    ]
  }
}

resource "aws_sns_topic_policy" "logs_sns_topic_policy" {
  arn    = aws_sns_topic.logs_sns_topic.arn
  policy = data.aws_iam_policy_document.logs_sns_topic_policy.json
}

resource "aws_sns_topic_subscription" "logs_sns_sqs_target" {
  topic_arn = aws_sns_topic.logs_sns_topic.arn
  protocol  = "sqs"
  endpoint  = var.custom_sqs_queue_arn != "" ? var.custom_sqs_queue_arn : module.logs_sqs_queue_with_deadletter[0].queue_arn
}

# Allow SNS to send message to SQS
data "aws_iam_policy_document" "sqs_queue_policy_document" {
  count = local.sqs_res_counter
  statement {
    effect = "Allow"

    actions = [
      "sqs:SendMessage",
    ]

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

    resources = [
      module.logs_sqs_queue_with_deadletter[0].queue_arn,
    ]

    condition {
      test     = "ArnEquals"
      variable = "aws:SourceArn"

      values = concat([aws_sns_topic.logs_sns_topic.arn], var.allowed_sns_topic_arns)
    }
  }
}

# Allow SNS to send message to SQS
resource "aws_sqs_queue_policy" "sqs_queue_policy" {
  count     = local.sqs_res_counter
  queue_url = module.logs_sqs_queue_with_deadletter[0].queue_url
  policy    = data.aws_iam_policy_document.sqs_queue_policy_document[0].json
}

resource "aws_s3_bucket_notification" "logs_bucket_notification" {
  bucket      = module.access_logs_bucket.s3_bucket_name_output
  eventbridge = true

  topic {
    topic_arn = aws_sns_topic.logs_sns_topic.arn
    events    = var.s3_bucket_notification_sns_events
  }

  depends_on = [
    aws_sns_topic_policy.logs_sns_topic_policy,
  ]
}

data "aws_iam_policy_document" "access_logs_combined_policy" {
  statement {
    effect = "Allow"

    actions = [
      "s3:GetObject",
      "s3:GetObjectVersion",
      "s3:ListBucket",
    ]

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

  dynamic "statement" {
    for_each = toset(module.logs_sqs_queue_with_deadletter[*].queue_arn)
    content {
      effect = "Allow"

      actions = [
        "sqs:ChangeMessageVisibility",
        "sqs:DeleteMessage",
        "sqs:GetQueueAttributes",
        "sqs:ListDeadLetterSourceQueues",
        "sqs:ReceiveMessage",
      ]

      resources = [
        statement.value,
      ]
    }
  }

  statement {
    effect = "Allow"

    actions = [
      "sqs:ListQueues",
    ]

    resources = [
      "*"
    ]
  }

  statement {
    effect = "Allow"
    resources = [
      aws_kms_key.access_logs_kms_key.arn,
    ]

    actions = [
      "kms:GenerateDataKey",
      "kms:Decrypt",
    ]
  }
}

resource "aws_iam_policy" "access_logs_combined_policy" {
  name   = "${var.environment}-${var.service_name}-${var.access_log_type}${local.regional_postfix}-access-policy"
  policy = data.aws_iam_policy_document.access_logs_combined_policy.json
}
