locals {
  playlists_sync_secrets = [
    "ATLAS_CLIENT_SECRET",
    "DEEZER_SECRET",
    "GOOGLE_CLIENT_SECRET",
    "SENTRY_DSN",
    "SPOTIFY_CLIENT_SECRET",
  ]

  headers = {
    "Access-Control-Allow-Headers"     = "Content-Type,Authorization,X-App-Slug,X-Type"
    "Access-Control-Allow-Methods"     = "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT"
    "Access-Control-Allow-Origin"      = "*"
    "Access-Control-Allow-Credentials" = "true"
  }

  # Pick non-empty header values
  header_values = compact(values(local.headers))

  # Pick names that from non-empty header values
  header_names = matchkeys(
    keys(local.headers),
    values(local.headers),
    local.header_values
  )

  # Parameter names for method and integration responses
  parameter_names = formatlist("method.response.header.%s", local.header_names)

  # Map parameter list to "true" values
  true_list = split("|",
    replace(join("|", local.parameter_names), "/[^|]+/", "true")
  )

  # Integration response parameters
  integration_response_parameters = zipmap(
    local.parameter_names,
    formatlist("'%s'", local.header_values)
  )

  # Method response parameters
  method_response_parameters = zipmap(
    local.parameter_names,
    local.true_list
  )

  lambda_playlists_sync_execution_types = {
    "main" = {
      reserved_concurrent_executions = -1
      timeout                        = 180
    },
    "user" = {
      reserved_concurrent_executions = 10
      timeout                        = 300
    },
    "schedule" = {
      reserved_concurrent_executions = 5
      timeout                        = 900
    },
  }
}

data "aws_ecr_repository" "playlists_sync_ecr" {
  name = "${local.project_group}/lambda/playlists-sync"

  provider = aws.gdb-apollo-dev
}

data "local_file" "lambda_playlists_sync" {
  filename = "${path.module}/lambda_playlists_sync.tf"
}

resource "aws_cloudwatch_log_group" "playlists_sync_log_group" {
  for_each = local.lambda_playlists_sync_execution_types

  name              = "/aws/lambda/${local.name_prefix}-playlists_sync_${each.key}"
  retention_in_days = module.cloudwatch_logs_retention_by_env[local.env_prefix]

  tags = merge(
    local.common_tags,
    {
      project                  = "Portal",
      service                  = "CloudWatch",
      plat_env_project_service = "${local.aggregated_tag}_PRL_CW"
    }
  )
}

resource "aws_cloudwatch_log_group" "playlists_sync_api_gateway_log_group" {
  name              = "/aws/api-gateway/${local.name_prefix}-playlists_sync"
  retention_in_days = module.cloudwatch_logs_retention_by_env[local.env_prefix]

  tags = merge(
    local.common_tags,
    {
      project                  = "Portal",
      service                  = "CloudWatch",
      plat_env_project_service = "${local.aggregated_tag}_PRL_CW"
    }
  )
}

resource "aws_secretsmanager_secret" "playlists_sync_secret" {
  for_each = toset(local.playlists_sync_secrets)

  name       = "${local.secrets_name_prefix}/playlists_sync/${each.value}"
  kms_key_id = data.aws_kms_key.secrets_general_key.id

  tags = merge(
    local.common_tags,
    {
      project                  = "Infrastructure",
      service                  = "Secrets Manager",
      plat_env_project_service = "${local.aggregated_tag}_INFRA_SCRT"
    }
  )
}

data "aws_iam_policy_document" "playlists_sync_invocation_policy" {
  version = "2012-10-17"
  statement {
    effect    = "Allow"
    actions   = ["lambda:InvokeFunction"]
    resources = ["*"]
  }
}

resource "aws_iam_policy" "playlists_sync_invocation_policy" {
  name   = "${local.name_prefix}-playlists_sync_invocation"
  policy = data.aws_iam_policy_document.playlists_sync_invocation_policy.json
}

module "playlists_sync_role" {
  source = "../../../modules/iam/roles/lambda_role"

  name_prefix                = local.name_prefix
  name                       = "playlists_sync_role"
  allow_execution_inside_vpc = true

  common_tags = merge(
    local.common_tags,
    {
      project                  = "Portal",
      service                  = "Lambda",
      plat_env_project_service = "${local.aggregated_tag}_PRL_LMB"
    }
  )

  policies_list = [
    data.aws_iam_policy.logging_policy.arn,
    data.aws_iam_policy.secrets_services_ro.arn,
    aws_iam_policy.playlists_sync_invocation_policy.arn,
    data.aws_iam_policy.playlists_sync_user_execution_queue_consumer_policy.arn,
    data.aws_iam_policy.playlists_sync_user_execution_queue_producer_policy.arn,
    data.aws_iam_policy.playlists_sync_schedule_execution_queue_consumer_policy.arn,
    data.aws_iam_policy.playlists_sync_schedule_execution_queue_producer_policy.arn,
  ]
}

resource "aws_lambda_function" "playlists_sync_lambda" {
  for_each = local.lambda_playlists_sync_execution_types

  function_name = "${local.name_prefix}-playlists_sync_${each.key}"
  image_uri     = "${data.aws_ecr_repository.playlists_sync_ecr.repository_url}:${local.docker_image_env_tag[local.env_prefix]}"
  package_type  = "Image"
  role          = module.playlists_sync_role.role.arn
  description   = "Managed by Terraform."

  memory_size = 128
  timeout     = lookup(each.value, "timeout")

  reserved_concurrent_executions = lookup(each.value, "reserved_concurrent_executions")

  vpc_config {
    security_group_ids = [data.aws_security_group.lambda.id]
    subnet_ids         = data.aws_subnets.private_subnets.ids
  }

  image_config {
    command = ["datadog_lambda.handler.handler"]
  }

  environment {
    variables = {
      ENVIRONMENT    = local.environment
      SENTRY_DSN     = aws_secretsmanager_secret.playlists_sync_secret["SENTRY_DSN"].name
      MYSQL_USER     = aws_secretsmanager_secret.apollo_api_secret["MYSQL_DB_USER"].name
      MYSQL_PASSWORD = aws_secretsmanager_secret.apollo_api_secret["MYSQL_DB_PASS"].name
      MYSQL_DATABASE = "dbSony_dbo"
      MYSQL_HOST     = module.domains[local.env_prefix]["main-db-rw"]
      MYSQL_PORT     = 3306

      ATLAS_BASE_URL      = "https://${module.atlas_domains[local.env_prefix]["atlas-um"]}/"
      ATLAS_CLIENT_ID     = "25d43fb4-5589-4c94-84d9-3a0afc4e0b1f"
      ATLAS_CLIENT_SECRET = aws_secretsmanager_secret.playlists_sync_secret["ATLAS_CLIENT_SECRET"].name
      ATLAS_AUDIENCE      = "apollo|api"

      AUTH_URL            = "https://${module.domains[local.env_prefix]["auth-api"]}/authorize"
      IMAGE_BASE_URL      = "https://${module.atlas_domains.prod["images-api"]}"
      VENDOR_API_BASE_URL = "https://${module.domains[local.env_prefix]["traefik"]}/vendor-api"

      DD_API_KEY_SECRET_ARN = aws_secretsmanager_secret.common_secret["DD_API_KEY"].arn
      DD_TRACE_ENABLED      = "true"
      DD_SITE               = "datadoghq.com"
      DD_LAMBDA_HANDLER     = "src.lambda_handler"

      LAMBDA_FUNCTION_NAME   = "${local.name_prefix}-playlists_sync_main"
      API_URL_PREFIX         = local.env_prefix
      GOOGLE_CLIENT_ID       = "1066394547140-bpkvl6vd6detutas90bvm98d7sgbusqa.apps.googleusercontent.com"
      GOOGLE_CLIENT_SECRET   = aws_secretsmanager_secret.playlists_sync_secret["GOOGLE_CLIENT_SECRET"].name
      DEEZER_APP_ID          = "153991"
      DEEZER_SECRET          = aws_secretsmanager_secret.playlists_sync_secret["DEEZER_SECRET"].name
      SPOTIFY_CLIENT_ID      = "0b2c48cb8fed43d2ae640ba54667beba"
      SPOTIFY_CLIENT_SECRET  = aws_secretsmanager_secret.playlists_sync_secret["SPOTIFY_CLIENT_SECRET"].name
      APPLE_MUSICKIT_KEYID   = "SQY3784VMW"
      APPLE_MUSICKIT_TEAM_ID = "Y38Y7276CK"
      APPLE_MUSICKIT_KEY     = aws_secretsmanager_secret.apollo_app_secret["APPLE_MUSICKIT_KEY"].name

      CORS_HEADERS = jsonencode(local.headers)

      USER_EXECUTION_SQS_URL     = data.aws_sqs_queue.playlists_sync_user_execution_queue.url
      SCHEDULE_EXECUTION_SQS_URL = data.aws_sqs_queue.playlists_sync_schedule_execution_queue.url

      EXECUTION_TYPE = each.key
    }
  }

  tags = merge(
    local.common_tags,
    {
      project                  = "Portal",
      service                  = "Lambda",
      plat_env_project_service = "${local.aggregated_tag}_PRL_LMB"
    }
  )

  depends_on = [
    aws_secretsmanager_secret.playlists_sync_secret,
    aws_cloudwatch_log_group.playlists_sync_log_group,
    module.playlists_sync_role,
  ]

  lifecycle {
    ignore_changes = [
      image_uri
    ]
  }
}

resource "aws_api_gateway_rest_api" "playlists_sync_api" {
  name = "${local.name_prefix}-playlists-sync"
}

resource "aws_api_gateway_resource" "playlists_sync_proxy_resource" {
  rest_api_id = aws_api_gateway_rest_api.playlists_sync_api.id
  parent_id   = aws_api_gateway_rest_api.playlists_sync_api.root_resource_id
  path_part   = "{proxy+}"
}

resource "aws_api_gateway_method" "playlists_sync_any_method" {
  rest_api_id   = aws_api_gateway_rest_api.playlists_sync_api.id
  resource_id   = aws_api_gateway_resource.playlists_sync_proxy_resource.id
  http_method   = "ANY"
  authorization = "NONE"

  request_parameters = {
    "method.request.path.proxy" = true
  }
}

resource "aws_api_gateway_method" "playlists_sync_options_method" {
  rest_api_id   = aws_api_gateway_rest_api.playlists_sync_api.id
  resource_id   = aws_api_gateway_resource.playlists_sync_proxy_resource.id
  http_method   = "OPTIONS"
  authorization = "NONE"
}

resource "aws_api_gateway_integration" "playlists_sync_any_integration" {
  rest_api_id             = aws_api_gateway_rest_api.playlists_sync_api.id
  resource_id             = aws_api_gateway_resource.playlists_sync_proxy_resource.id
  http_method             = aws_api_gateway_method.playlists_sync_any_method.http_method
  integration_http_method = "POST"
  type                    = "AWS_PROXY"
  content_handling        = "CONVERT_TO_TEXT"
  uri                     = aws_lambda_function.playlists_sync_lambda["main"].invoke_arn

  cache_key_parameters = [
    "method.request.path.proxy",
  ]
}

resource "aws_api_gateway_integration" "playlists_sync_options_mock_integration" {
  rest_api_id             = aws_api_gateway_rest_api.playlists_sync_api.id
  resource_id             = aws_api_gateway_resource.playlists_sync_proxy_resource.id
  http_method             = aws_api_gateway_method.playlists_sync_options_method.http_method
  integration_http_method = "OPTIONS"
  type                    = "MOCK"

  request_templates = {
    "application/json" = jsonencode({ statusCode = 200 })
  }

  lifecycle {
    ignore_changes = [integration_http_method]
  }
}

resource "aws_lambda_permission" "playlists_sync_apigw_lambda" {
  statement_id  = "AllowExecutionFromAPIGateway"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.playlists_sync_lambda["main"].function_name
  principal     = "apigateway.amazonaws.com"
  source_arn    = "${aws_api_gateway_rest_api.playlists_sync_api.execution_arn}/*/*"
}

resource "aws_api_gateway_method_response" "playlists_sync_options_response" {
  rest_api_id = aws_api_gateway_rest_api.playlists_sync_api.id
  resource_id = aws_api_gateway_resource.playlists_sync_proxy_resource.id
  http_method = aws_api_gateway_method.playlists_sync_options_method.http_method
  status_code = "200"

  response_parameters = local.method_response_parameters
}

resource "aws_api_gateway_integration_response" "playlists_sync_options_mock_integration_response" {
  rest_api_id = aws_api_gateway_rest_api.playlists_sync_api.id
  resource_id = aws_api_gateway_resource.playlists_sync_proxy_resource.id
  http_method = aws_api_gateway_method.playlists_sync_options_method.http_method
  status_code = aws_api_gateway_method_response.playlists_sync_options_response.status_code

  response_parameters = local.integration_response_parameters
}

resource "aws_api_gateway_deployment" "playlists_sync_deployment" {
  rest_api_id = aws_api_gateway_rest_api.playlists_sync_api.id
  triggers = {
    redeployment = data.local_file.lambda_playlists_sync.content_sha512
  }

  lifecycle {
    create_before_destroy = true
  }
  depends_on = [
    aws_api_gateway_integration.playlists_sync_any_integration,
  ]
}

resource "aws_api_gateway_stage" "playlists_sync_stage" {
  deployment_id = aws_api_gateway_deployment.playlists_sync_deployment.id
  rest_api_id   = aws_api_gateway_rest_api.playlists_sync_api.id
  stage_name    = "prod"

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.playlists_sync_api_gateway_log_group.arn
    format          = "$context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] \"$context.httpMethod $context.resourcePath $context.protocol\" $context.status $context.responseLength $context.requestId"
  }

  lifecycle {
    ignore_changes = [cache_cluster_size]
  }
}

resource "aws_api_gateway_domain_name" "playlists_sync" {
  domain_name     = module.domains[local.env_prefix]["playlists_sync"]
  certificate_arn = data.aws_acm_certificate.wildcard_filtr_apollo_cert.arn
}

resource "aws_route53_record" "playlists_sync" {
  name    = aws_api_gateway_domain_name.playlists_sync.domain_name
  type    = "A"
  zone_id = data.aws_route53_zone.apollo_stream_zone.zone_id

  alias {
    name                   = aws_api_gateway_domain_name.playlists_sync.cloudfront_domain_name
    zone_id                = aws_api_gateway_domain_name.playlists_sync.cloudfront_zone_id
    evaluate_target_health = false
  }

  provider = aws.gdb-delphi-dev
}

resource "aws_api_gateway_base_path_mapping" "playlists_sync" {
  api_id      = aws_api_gateway_rest_api.playlists_sync_api.id
  stage_name  = aws_api_gateway_stage.playlists_sync_stage.stage_name
  domain_name = aws_api_gateway_domain_name.playlists_sync.domain_name
}

// create an event with permissions to run the lambda once a day
resource "aws_lambda_permission" "playlists_sync_lambda_permission" {
  statement_id  = "AllowExecutionFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.playlists_sync_lambda["main"].function_name
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.playlists_sync_event_rule.arn
}

resource "aws_cloudwatch_event_rule" "playlists_sync_event_rule" {
  name                = "${local.name_prefix}-playlists_sync"
  schedule_expression = "rate(1 day)"
}

resource "aws_cloudwatch_event_target" "playlists_sync_event_target" {
  rule      = aws_cloudwatch_event_rule.playlists_sync_event_rule.name
  target_id = "lambda"
  arn       = aws_lambda_function.playlists_sync_lambda["main"].arn
}

// create an event with permissions to keep lambda warm
resource "aws_lambda_permission" "playlists_sync_warm_up_lambda_permission" {
  statement_id  = "AllowWarmUpFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.playlists_sync_lambda["main"].function_name
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.playlists_sync_warm_up_event_rule.arn
}

resource "aws_cloudwatch_event_rule" "playlists_sync_warm_up_event_rule" {
  name                = "${local.name_prefix}-playlists_sync_warm_up"
  schedule_expression = "rate(15 minutes)"
}

resource "aws_cloudwatch_event_target" "playlists_sync_warm_up_event_target" {
  rule      = aws_cloudwatch_event_rule.playlists_sync_warm_up_event_rule.name
  target_id = "lambda"
  arn       = aws_lambda_function.playlists_sync_lambda["main"].arn

  input = jsonencode({ warmup : true })
}

// trigger from user execution SQS
resource "aws_lambda_event_source_mapping" "playlists_sync_lambda_user_sqs" {
  event_source_arn = data.aws_sqs_queue.playlists_sync_user_execution_queue.arn
  function_name    = aws_lambda_function.playlists_sync_lambda["user"].arn
  batch_size       = 10
}

// trigger from schedule execution SQS
resource "aws_lambda_event_source_mapping" "playlists_sync_lambda_schedule_sqs" {
  event_source_arn = data.aws_sqs_queue.playlists_sync_schedule_execution_queue.arn
  function_name    = aws_lambda_function.playlists_sync_lambda["schedule"].arn
  batch_size       = 5
}
