data "aws_caller_identity" "current" {}

data "aws_iam_policy_document" "assume_role" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["apigateway.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "api_gateway_role" {
  name               = "api-gateway-${local.api_gateway_name}"
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

data "aws_iam_policy_document" "api_gateway_s3_policy" {
  statement {
    effect = "Allow"
    actions = [
      "s3:GetBucketAcl",
      "s3:GetBucketCORS",
      "s3:GetBucketLocation",
      "s3:GetBucketLogging",
      "s3:GetBucketNotification",
      "s3:GetBucketPolicy",
      "s3:GetBucketRequestPayment",
      "s3:GetBucketTagging",
      "s3:GetBucketVersioning",
      "s3:GetBucketWebsite",
      "s3:GetLifecycleConfiguration",
      "s3:GetObject",
      "s3:GetObjectAcl",
      "s3:GetObjectTorrent",
      "s3:GetObjectVersion",
      "s3:GetObjectVersionAcl",
      "s3:GetObjectVersionTorrent",
      "s3:ListAllMyBuckets",
      "s3:ListBucket",
      "s3:ListBucketMultipartUploads",
      "s3:ListBucketVersions",
      "s3:ListMultipartUploadParts"
    ]
    resources = [
      "arn:aws:s3:::${local.s3_bucket}/${var.s3_path}",
      "arn:aws:s3:::${local.s3_bucket}/${var.s3_path}/*"
    ]
  }
}

resource "aws_iam_policy" "api_gateway_s3_policy" {
  name   = "api-gateway-${local.api_gateway_name}"
  policy = data.aws_iam_policy_document.api_gateway_s3_policy.json
}

resource "aws_iam_role_policy_attachment" "api_gateway_s3_policy_attachment" {
  role       = aws_iam_role.api_gateway_role.name
  policy_arn = aws_iam_policy.api_gateway_s3_policy.arn
}

resource "aws_iam_role_policy_attachment" "api_gateway_cloudwatch_policy_attachment" {
  role       = aws_iam_role.api_gateway_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
}

resource "aws_api_gateway_rest_api" "new_release_json" {
  name        = local.api_gateway_name
  description = "New releases JSON API"
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_api_gateway_request_validator" "validator" {
  name                        = "${local.api_gateway_name}-validator"
  rest_api_id                 = aws_api_gateway_rest_api.new_release_json.id
  validate_request_body       = false
  validate_request_parameters = true
}

resource "aws_api_gateway_resource" "new_release" {
  rest_api_id = aws_api_gateway_rest_api.new_release_json.id
  parent_id   = aws_api_gateway_rest_api.new_release_json.root_resource_id
  path_part   = "releases"
}

resource "aws_api_gateway_method" "new_release_method" {
  rest_api_id          = aws_api_gateway_rest_api.new_release_json.id
  resource_id          = aws_api_gateway_resource.new_release.id
  http_method          = "GET"
  authorization        = "AWS_IAM"
  request_validator_id = aws_api_gateway_request_validator.validator.id

  # The parameter must be enabled in the method before usage in integration.
  request_parameters = {
    "method.request.path.file" = true
  }
}

resource "aws_api_gateway_integration" "api_integration" {
  rest_api_id             = aws_api_gateway_rest_api.new_release_json.id
  resource_id             = aws_api_gateway_resource.new_release.id
  http_method             = aws_api_gateway_method.new_release_method.http_method
  integration_http_method = aws_api_gateway_method.new_release_method.http_method
  type                    = "AWS"
  uri                     = "arn:aws:apigateway:${var.s3_region}:s3:path/${local.s3_bucket}/${var.s3_path}/${var.s3_object}"
  credentials             = aws_iam_role.api_gateway_role.arn

  request_parameters = {
    "integration.request.path.file" = "method.request.path.file"
  }
}

resource "aws_api_gateway_method_response" "response_200" {
  rest_api_id = aws_api_gateway_rest_api.new_release_json.id
  resource_id = aws_api_gateway_resource.new_release.id
  http_method = aws_api_gateway_method.new_release_method.http_method

  status_code = "200"
  response_models = {
    "application/json" = "Empty"
  }
}

resource "aws_api_gateway_integration_response" "ResourceMethodIntegration200" {
  rest_api_id = aws_api_gateway_rest_api.new_release_json.id
  resource_id = aws_api_gateway_resource.new_release.id
  http_method = aws_api_gateway_method.new_release_method.http_method
  status_code = aws_api_gateway_method_response.response_200.status_code
  depends_on  = [aws_api_gateway_integration.api_integration]
}

# Add swagger spec endpoint
resource "aws_api_gateway_resource" "spec" {
  rest_api_id = aws_api_gateway_rest_api.new_release_json.id
  parent_id   = aws_api_gateway_rest_api.new_release_json.root_resource_id
  path_part   = "spec"
}

resource "aws_api_gateway_method" "spec" {
  rest_api_id          = aws_api_gateway_rest_api.new_release_json.id
  resource_id          = aws_api_gateway_resource.spec.id
  http_method          = "GET"
  authorization        = "AWS_IAM"
  request_validator_id = aws_api_gateway_request_validator.validator.id

  # The parameter must be enabled in the method before usage in integration.
  request_parameters = {
    "method.request.path.file" = true
  }
}

resource "aws_api_gateway_integration" "spec_integration" {
  rest_api_id             = aws_api_gateway_rest_api.new_release_json.id
  resource_id             = aws_api_gateway_resource.spec.id
  http_method             = aws_api_gateway_method.spec.http_method
  integration_http_method = aws_api_gateway_method.spec.http_method
  type                    = "AWS"
  uri                     = "arn:aws:apigateway:${var.s3_region}:s3:path/${local.s3_bucket}/${var.s3_path}/${var.s3_spec_object}"
  credentials             = aws_iam_role.api_gateway_role.arn

  request_parameters = {
    "integration.request.path.file" = "method.request.path.file"
  }
}

resource "aws_api_gateway_method_response" "spec_200" {
  rest_api_id = aws_api_gateway_rest_api.new_release_json.id
  resource_id = aws_api_gateway_resource.spec.id
  http_method = aws_api_gateway_method.spec.http_method

  status_code = "200"
  response_models = {
    "application/x-yaml" = "Empty"
  }
}

resource "aws_api_gateway_integration_response" "SpecMethodIntegration200" {
  rest_api_id = aws_api_gateway_rest_api.new_release_json.id
  resource_id = aws_api_gateway_resource.spec.id
  http_method = aws_api_gateway_method.spec.http_method
  status_code = aws_api_gateway_method_response.spec_200.status_code
  depends_on  = [aws_api_gateway_integration.spec_integration]
}

resource "aws_api_gateway_deployment" "api_deployment" {
  rest_api_id = aws_api_gateway_rest_api.new_release_json.id
  depends_on  = [aws_api_gateway_method.new_release_method]
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_api_gateway_stage" "default_stage" {
  # checkov:skip=CKV_AWS_76:Ensure API Gateway has Access Logging enabled
  # checkov:skip=CKV2_AWS_4: Ensure API Gateway stage have logging level defined as appropriate
  # checkov:skip=CKV2_AWS_51: Ensure AWS API Gateway endpoints uses client certificate authentication
  # checkov:skip=CKV2_AWS_29: Ensure public API gateway are protected by WAF
  deployment_id = aws_api_gateway_deployment.api_deployment.id
  rest_api_id   = aws_api_gateway_rest_api.new_release_json.id
  stage_name    = "default_stage"
}

resource "aws_api_gateway_domain_name" "api_domain_name" {
  domain_name     = var.api_gateway_domain_name
  certificate_arn = var.api_gateway_certificate_arn
  security_policy = "TLS_1_2"
}

resource "aws_route53_record" "api_gateway_dns_record" {
  name    = aws_api_gateway_domain_name.api_domain_name.domain_name
  zone_id = var.aws_route53_params["zone_id"]
  type    = var.aws_route53_params["type"]

  alias {
    name                   = aws_api_gateway_domain_name.api_domain_name.cloudfront_domain_name
    zone_id                = aws_api_gateway_domain_name.api_domain_name.cloudfront_zone_id
    evaluate_target_health = true
  }
}

resource "aws_route53_record" "api_gateway_dns_record_networking" {
  provider = aws.networking

  name    = aws_api_gateway_domain_name.api_domain_name.domain_name
  zone_id = var.aws_route53_params_networking["zone_id"]
  type    = var.aws_route53_params_networking["type"]

  alias {
    name                   = aws_api_gateway_domain_name.api_domain_name.cloudfront_domain_name
    zone_id                = aws_api_gateway_domain_name.api_domain_name.cloudfront_zone_id
    evaluate_target_health = true
  }
}

resource "aws_api_gateway_base_path_mapping" "base_path_mapping" {
  api_id      = aws_api_gateway_rest_api.new_release_json.id
  domain_name = aws_api_gateway_domain_name.api_domain_name.domain_name
}

# Create an IAM user with permissions to invoke this API endpoint
resource "aws_iam_user" "invoke_api_user" {
  name = local.invoke_api_iam_user_name
  tags = {
    role = "service-user"
  }
}

data "aws_iam_policy_document" "invoke_api_policy" {
  statement {
    effect = "Allow"
    actions = [
      "execute-api:Invoke",
      "execute-api:GET",
    ]
    resources = [
      "arn:aws:execute-api:::${aws_api_gateway_rest_api.new_release_json.id}/*/${aws_api_gateway_method.new_release_method.http_method}/${aws_api_gateway_resource.new_release.path_part}",
      "arn:aws:execute-api:${var.s3_region}:${data.aws_caller_identity.current.account_id}:${aws_api_gateway_rest_api.new_release_json.id}/${aws_api_gateway_stage.default_stage.stage_name}/${aws_api_gateway_method.new_release_method.http_method}/${aws_api_gateway_resource.new_release.path_part}",
    ]
  }
}

resource "aws_iam_policy" "invoke_api_policy" {
  name   = "api-gateway-invoke-${local.api_gateway_name}"
  policy = data.aws_iam_policy_document.invoke_api_policy.json
}

resource "aws_iam_user_policy_attachment" "invoke_api_gateway_policy_attachment" {
  user       = aws_iam_user.invoke_api_user.name
  policy_arn = aws_iam_policy.invoke_api_policy.arn
}

data "aws_wafv2_web_acl" "waf" {
  name  = "prod-public-shared-regional-waf-block"
  scope = "REGIONAL"
}

resource "aws_wafv2_web_acl_association" "api_gateway_waf_association" {
  resource_arn = "${aws_api_gateway_rest_api.new_release_json.arn}/stages/default_stage"
  web_acl_arn  = data.aws_wafv2_web_acl.waf.arn
}
