#
# AWS
# Cloud API - has access to redis.
#

provider "aws" {
  region = "us-west-2"
}

variable "network_name" {
}

variable "fleet_name" {
}

variable "name" {
  type    = string
  default = "cloud_api"
}

variable "ami" {
  type = string
}

variable "instance_type" {
  type    = string
  default = "t2.micro"
}

variable "key_name" {
  type = string
}

variable "elb_certificate_arn" {
  type = string
}

variable "rds_security_group_ids" {
  type        = map(string)
  description = "Mapping of network names to RDS security group IDs"
  default = {
    main = "sg-9b144be5"
    test = "sg-9b144be5"
    dev  = "sg-9b144be5"
  }
}

variable "pm2_memory_high" {
  description = "Cgroup MemoryHigh for pm2-ubuntu.service (reclaim pressure). Systemd syntax: '75%' of RAM, '24G', or 'infinity'."
  type        = string
  default     = "75%"
}

variable "pm2_memory_max" {
  description = "Cgroup MemoryMax for pm2-ubuntu.service (cgroup OOM threshold). Systemd syntax: '85%' of RAM, '28G', or 'infinity'."
  type        = string
  default     = "85%"
}

locals {
  cloud_api_server_name = "${var.network_name}-${var.fleet_name}-cloud-api"
  network_fleet_name    = "${var.network_name}_${var.fleet_name}"
}


resource "aws_instance" "cloud_api" {
  ami             = var.ami
  instance_type   = var.instance_type
  placement_group = "${local.network_fleet_name}_placement_group"
  key_name        = var.key_name
  security_groups = [
    aws_security_group.cloud_api_security_group.name,

    # Developer: SSH access security group
    "[Bigscreen] SSH Server Access"
  ]

  tags = {
    Name = "[${local.network_fleet_name}] ${local.cloud_api_server_name}"
  }

  root_block_device {
    volume_type = "gp2"
    volume_size = 128
    encrypted   = true
  }

  # nginx config
  provisioner "file" {
    source      = "/home/ubuntu/networks/${var.network_name}/${var.fleet_name}/config/${var.network_name}_${var.fleet_name}_cloud_api.nginx.conf"
    destination = "/home/ubuntu/default.nginx.conf"
  }

  # Set up the following:
  # - nginx
  # - node 24
  # - pm2
  provisioner "remote-exec" {
    inline = [
      # Install Node.js 24
      "curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -",
      "sudo apt-get install -y nodejs",


      # Install PM2
      "sudo npm install -g pm2",

      # Register PM2 as a systemd service (pm2-ubuntu.service) with cgroup memory
      # limits. Without this, Jenkins' "pm2 start" over SSH leaves PM2 running in
      # the SSH session scope — a worker memory blowup then triggers a global OOM
      # that takes the whole instance offline. As a system service with MemoryMax,
      # the OOM kill is scoped to PM2's children and sshd/networkd stay alive.
      "sudo mkdir -p /etc/systemd/system/pm2-ubuntu.service.d",
      "printf '[Service]\\nMemoryHigh=%s\\nMemoryMax=%s\\n' '${var.pm2_memory_high}' '${var.pm2_memory_max}' | sudo tee /etc/systemd/system/pm2-ubuntu.service.d/override.conf > /dev/null",
      "sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u ubuntu --hp /home/ubuntu",

      # Install nginx (if not already installed)
      "sudo apt-get update",
      "sudo apt-get install -y nginx",

      # Configure nginx
      "sudo cp /home/ubuntu/default.nginx.conf /etc/nginx/sites-enabled/default",
      "sudo service nginx restart"
    ]
  }

  connection {
    type        = "ssh"
    host        = self.public_dns
    user        = "ubuntu"
    private_key = file("/home/ubuntu/networks/${var.network_name}/${var.fleet_name}/aws/${var.key_name}.pem")
    timeout     = "5m"
  }
}


# Security group for the cloud api server itself.
resource "aws_security_group" "cloud_api_security_group" {
  name        = "${local.cloud_api_server_name}_security_group"
  description = "${local.cloud_api_server_name} Internal"
  vpc_id      = "vpc-6284a71b"

  # ELB forwarding along port 80 for https for the cloud_api endpoints.
  ingress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.cloud_api_elb_security_group.id]
  }

  # Jenkins server ssh support
  ingress {
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = ["sg-0577fa74bb0447c98"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "[${local.network_fleet_name}] ${local.cloud_api_server_name}_security_group"
  }
}

# Grant the cloud_api server access to the RDS database.
resource "aws_security_group_rule" "allow_cloud_api_to_rds" {
  security_group_id        = lookup(var.rds_security_group_ids, var.network_name)
  type                     = "ingress"
  from_port                = 5432
  to_port                  = 5432
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.cloud_api_security_group.id
}


#
# HTTP Server ELB
#

resource "aws_security_group" "cloud_api_elb_security_group" {
  name        = "${local.cloud_api_server_name}_elb_security_group"
  description = "${local.cloud_api_server_name} ELB"
  vpc_id      = "vpc-6284a71b"

  ingress {
    from_port = 443
    to_port   = 443
    protocol  = "tcp"
    # Please restrict your ingress to only necessary IPs and ports.
    # Opening to 0.0.0.0/0 can lead to security vulnerabilities.
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "[${local.network_fleet_name}] ${local.cloud_api_server_name}_elb_security_group"
  }
}


# cloud_api Server API ELB, sending data
resource "aws_lb" "cloud_api_elb" {
  name               = "${local.cloud_api_server_name}-elb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.cloud_api_elb_security_group.id]
  subnets            = ["subnet-08218e43", "subnet-9e79f4e7", "subnet-bc5736e6", "subnet-41185f69"]

  enable_deletion_protection = var.network_name == "main"
}


resource "aws_lb_target_group" "cloud_api-target" {
  name     = "${local.cloud_api_server_name}-target"
  port     = 80
  protocol = "HTTP"
  vpc_id   = "vpc-6284a71b"
}

# The cloud_api ELB points to the cloud api server instance!
resource "aws_lb_target_group_attachment" "cloudApiAttachment" {
  target_group_arn = aws_lb_target_group.cloud_api-target.arn
  target_id        = aws_instance.cloud_api.id
  port             = 80
}


resource "aws_lb_listener" "cloud_api_elb" {
  load_balancer_arn = aws_lb.cloud_api_elb.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS13-1-2-2021-06"
  certificate_arn   = var.elb_certificate_arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.cloud_api-target.arn
  }
}


#
# Outputs - DNS addresses for the server, and the ELBs
#
# Note cloud_api is different capitalization to how it's used elsewhere (e.g. cloud_api vs CloudHttp).
# TODO: make the capitalization of cloud_api consistent across the projects.
#
output "cloud_api_dns" {
  value = aws_instance.cloud_api.public_dns
}

output "cloud_api_elb_dns" {
  value = aws_lb.cloud_api_elb.dns_name
}

output "security_group_id" {
  value = aws_security_group.cloud_api_security_group.id
}