#
# Core Bigscreen API.
#

provider "aws" {
  region = "us-west-2"
}

variable "network_name" {
}

variable "fleet_name" {
}

variable "ami" {
}

variable "instance_type" {
}

variable "key_name" {
}

variable "elb_certificate_arn" {
  type = string
}

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 {
  api_server_name    = "${var.network_name}-${var.fleet_name}-api"
  network_fleet_name = "${var.network_name}_${var.fleet_name}"
}


resource "aws_instance" "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.api_security_group.name,

    # Developer: SSH access security group
    "[Bigscreen] SSH Server Access"
  ]

  tags = {
    Name = "[${local.network_fleet_name}] ${local.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}_api.nginx.conf"
    destination = "/home/ubuntu/${var.network_name}_${var.fleet_name}_api.nginx.conf"
  }

  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 and python3-opencv (must run before Python 3.10 becomes default)
      "sudo apt-get update",
      "sudo apt-get install -y software-properties-common nginx python3-opencv",

      # Configure nginx
      "sudo cp /home/ubuntu/${var.network_name}_${var.fleet_name}_api.nginx.conf /etc/nginx/sites-enabled/default",
      "sudo service nginx restart",

      # Install Python 3.10 for fabricator preprocessor (pinned deps require <3.11)
      # This must be last — switching the default python3 breaks apt_pkg hooks
      "sudo add-apt-repository -y ppa:deadsnakes/ppa",
      "sudo apt-get update",
      "sudo apt-get install -y python3.10 python3.10-venv",
      "sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.10 2",
      "sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.12 1",
      "sudo update-alternatives --set python3 /usr/bin/python3.10"
    ]
  }

  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 api server itself.
resource "aws_security_group" "api_security_group" {
  name        = "${local.api_server_name}_security_group"
  description = "${local.api_server_name} Internal"
  vpc_id      = "vpc-6284a71b"

  # ELB forwarding along port 80 for https for the api server.
  ingress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.api_elb_security_group.id]
  }

  # Jenkins SSH access
  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.api_server_name}_security_group"
  }
}

#
# RDS access
#

# Lookup table for security group ids
variable "rds_security_group_ids" {
  type        = map(string)
  description = "Mapping of group names to security group names"
  default = {
    main = "sg-9b144be5"
    test = "sg-9b144be5"
    dev  = "sg-9b144be5"
  }
}

# Find the specific id for the RDS security group.
data "aws_security_group" "network_rds_security_group" {
  id = lookup(var.rds_security_group_ids, var.network_name)
}

# Grant access to the existing RDS security group.
resource "aws_security_group_rule" "allow_ec2_to_rds" {
  security_group_id        = aws_security_group.api_security_group.id
  type                     = "ingress"
  from_port                = 5432
  to_port                  = 5432
  protocol                 = "tcp"
  source_security_group_id = lookup(var.rds_security_group_ids, var.network_name)
}


#
# api Server ELB
#

# Security Group for the api Server ELB
resource "aws_security_group" "api_elb_security_group" {
  name        = "${local.api_server_name}_elb_security_group"
  description = "${local.api_server_name} ELB"
  vpc_id      = "vpc-6284a71b"

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    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.api_server_name}_elb_security_group"
  }
}


# api Server API ELB, sending data
resource "aws_lb" "api_elb" {
  name               = "${local.api_server_name}-elb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.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" "api-target" {
  name     = "${local.api_server_name}-target"
  port     = 80
  protocol = "HTTP"
  vpc_id   = "vpc-6284a71b"
}


resource "aws_lb_target_group_attachment" "test" {
  target_group_arn = aws_lb_target_group.api-target.arn
  target_id        = aws_instance.api.id
  port             = 80
}


resource "aws_lb_listener" "api_elb" {
  load_balancer_arn = aws_lb.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.api-target.arn
  }
}

#
# Outputs - DNS addresses for the server, and the ELBs
#
output "api_dns" {
  value = aws_instance.api.public_dns
}

output "api_elb_dns" {
  value = aws_lb.api_elb.dns_name
}

output "security_group_id" {
  value = aws_security_group.api_security_group.id
}