# Network.
#
# One VPC, two PUBLIC subnets across two availability zones, an internet gateway, and NO NAT
# gateway.
#
# The NAT decision is the one worth reading. A private-subnet-plus-NAT layout is the reflex here,
# and it would add about 35 dollars a month to a stack estimated at 85-95. It buys nothing in this
# design: inbound is already restricted to the ALB security group either way, and NAT performs no
# egress filtering, so the tasks could still reach anything they can reach today. Paying for the
# shape of security rather than the property is how a small stack doubles in cost while its actual
# posture stays where it was.
#
# What the tasks need egress FOR: the TiDB Cloud management API, the SQL endpoint of a customer's
# cluster, and the operational data store. All public internet endpoints. Public IPs with a
# closed inbound security group is the honest way to say that.

data "aws_availability_zones" "available" {
  state = "available"
}

resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = { Name = "mcp-vpc" }
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  tags   = { Name = "mcp-igw" }
}

# Two AZs because an ALB requires at least two subnets in different zones, and because the official
# service runs two tasks that are worth putting in different zones.
resource "aws_subnet" "public" {
  count = 2

  vpc_id                  = aws_vpc.main.id
  cidr_block              = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index)
  availability_zone       = data.aws_availability_zones.available.names[count.index]
  map_public_ip_on_launch = true

  tags = { Name = "mcp-public-${data.aws_availability_zones.available.names[count.index]}" }
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }

  tags = { Name = "mcp-public" }
}

resource "aws_route_table_association" "public" {
  count = length(aws_subnet.public)

  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

# Security groups.
#
# Two ALBs, each with its own group, and each task group accepting traffic ONLY from its own ALB
# group. The separation between the official service and the partner POC is carried by these
# groups, by separate secrets, and by separate task roles. Of those three, the task roles are the
# real boundary; these groups are what stops one ALB reaching the other's tasks.

resource "aws_security_group" "alb_official" {
  name        = "mcp-alb-official"
  description = "Public ingress for the official MCP endpoint"
  vpc_id      = aws_vpc.main.id

  tags = { Name = "mcp-alb-official" }
}

resource "aws_security_group" "alb_poc" {
  name        = "mcp-alb-poc"
  description = "Public ingress for the partner POC endpoint"
  vpc_id      = aws_vpc.main.id

  tags = { Name = "mcp-alb-poc" }
}

# Rules as separate resources rather than inline blocks. Inline `ingress`/`egress` blocks are
# authoritative: a rule added by anything else is silently removed on the next apply, and the
# removal shows up as an outage rather than as a diff anyone read.
resource "aws_vpc_security_group_ingress_rule" "alb_official_https" {
  security_group_id = aws_security_group.alb_official.id
  cidr_ipv4         = "0.0.0.0/0"
  from_port         = 443
  to_port           = 443
  ip_protocol       = "tcp"
  description       = "HTTPS from anywhere"
}

# Port 80 exists to redirect to 443, and before a certificate exists it is how smoke tests run.
# Customer API keys travel in headers, so this port must never carry a real credential: the runbook
# says dummy credentials only until TLS is live, and that is a procedural guard, not a technical
# one. It is the reason the listener on 80 redirects rather than serves.
resource "aws_vpc_security_group_ingress_rule" "alb_official_http" {
  security_group_id = aws_security_group.alb_official.id
  cidr_ipv4         = "0.0.0.0/0"
  from_port         = 80
  to_port           = 80
  ip_protocol       = "tcp"
  description       = "HTTP, redirected to HTTPS once a certificate exists"
}

resource "aws_vpc_security_group_egress_rule" "alb_official_all" {
  security_group_id = aws_security_group.alb_official.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "-1"
  description       = "To the tasks"
}

resource "aws_vpc_security_group_ingress_rule" "alb_poc_https" {
  security_group_id = aws_security_group.alb_poc.id
  cidr_ipv4         = "0.0.0.0/0"
  from_port         = 443
  to_port           = 443
  ip_protocol       = "tcp"
  description       = "HTTPS from anywhere"
}

resource "aws_vpc_security_group_ingress_rule" "alb_poc_http" {
  security_group_id = aws_security_group.alb_poc.id
  cidr_ipv4         = "0.0.0.0/0"
  from_port         = 80
  to_port           = 80
  ip_protocol       = "tcp"
  description       = "HTTP, redirected to HTTPS once a certificate exists"
}

resource "aws_vpc_security_group_egress_rule" "alb_poc_all" {
  security_group_id = aws_security_group.alb_poc.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "-1"
  description       = "To the tasks"
}

# Task groups. Inbound from the matching ALB group ONLY, by group reference rather than by CIDR, so
# the rule keeps holding when addresses change.
resource "aws_security_group" "task_official" {
  name        = "mcp-task-official"
  description = "Official service tasks"
  vpc_id      = aws_vpc.main.id

  tags = { Name = "mcp-task-official" }
}

resource "aws_vpc_security_group_ingress_rule" "task_official_from_alb" {
  security_group_id            = aws_security_group.task_official.id
  referenced_security_group_id = aws_security_group.alb_official.id
  from_port                    = 3000
  to_port                      = 3000
  ip_protocol                  = "tcp"
  description                  = "From the official ALB only"
}

# Egress is open, and that is the honest position rather than an oversight. The tasks call the TiDB
# Cloud API, customer SQL endpoints on ports that vary, and the operational store. Any allowlist
# narrow enough to be worth writing would be wrong the first time a customer used a cluster in a
# region we did not enumerate.
resource "aws_vpc_security_group_egress_rule" "task_official_all" {
  security_group_id = aws_security_group.task_official.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "-1"
  description       = "TiDB Cloud API, customer SQL endpoints, operational store"
}

resource "aws_security_group" "task_poc" {
  name        = "mcp-task-poc"
  description = "Partner POC tasks"
  vpc_id      = aws_vpc.main.id

  tags = { Name = "mcp-task-poc" }
}

resource "aws_vpc_security_group_ingress_rule" "task_poc_from_alb" {
  security_group_id            = aws_security_group.task_poc.id
  referenced_security_group_id = aws_security_group.alb_poc.id
  from_port                    = 3000
  to_port                      = 3000
  ip_protocol                  = "tcp"
  description                  = "From the POC ALB only"
}

resource "aws_vpc_security_group_egress_rule" "task_poc_all" {
  security_group_id = aws_security_group.task_poc.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "-1"
  description       = "TiDB Cloud API, customer SQL endpoints, operational store"
}
