provider "aws" {
  region = var.aws_region
}

resource "aws_vpc" "default" {
  cidr_block           = var.vpc_cidr_block
  enable_dns_hostnames = true

  tags = {
    Name         = "${var.environment}-${var.service_name}-vpc"
    environment  = var.environment
    service_name = var.service_name
  }
}

# Add public subnets
resource "aws_subnet" "public_default" {
  count             = length(var.vpc_public_subnets)
  vpc_id            = aws_vpc.default.id
  cidr_block        = element(values(var.vpc_public_subnets), count.index)
  availability_zone = element(keys(var.vpc_public_subnets), count.index)

  tags = {
    Name         = "${var.environment}-${var.service_name}-public-subnet-${count.index}"
    environment  = var.environment
    service_name = var.service_name
  }
}

# Add route tables
resource "aws_route_table" "public_default" {
  count  = length(var.vpc_public_subnets)
  vpc_id = aws_vpc.default.id

  tags = {
    Name         = "${var.environment}-${var.service_name}-public-route-table-${count.index}"
    environment  = var.environment
    service_name = var.service_name
  }
}

# Associate route tables with public subnets
resource "aws_route_table_association" "public_default" {
  count          = length(var.vpc_public_subnets)
  subnet_id      = element(aws_subnet.public_default.*.id, count.index)
  route_table_id = element(aws_route_table.public_default.*.id, count.index)
}

# Add internet gateway
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.default.id

  tags = {
    Name         = "${var.environment}-${var.service_name}-public-gateway"
    environment  = var.environment
    service_name = var.service_name
  }
}

# Add a public gateway to public route table
resource "aws_route" "public_gateway_route" {
  count                  = length(var.vpc_public_subnets)
  route_table_id         = element(aws_route_table.public_default.*.id, count.index)
  depends_on             = [aws_route_table.public_default]
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.igw.id
}

# Add internal routes to public route table
resource "aws_route" "public_internal_route" {
  count                  = length(var.vpc_public_subnet_routes)
  route_table_id         = element(aws_route_table.public_default.*.id, count.index)
  destination_cidr_block = var.vpc_public_subnet_routes[count.index]
  transit_gateway_id     = var.transit_gateway_id
  depends_on             = [aws_route_table.public_default]
}

resource "aws_route" "public_internal_route_2" {
  count                  = length(var.vpc_public_subnet_routes)
  route_table_id         = element(aws_route_table.public_default.*.id, count.index + 1)
  destination_cidr_block = var.vpc_public_subnet_routes[count.index]
  transit_gateway_id     = var.transit_gateway_id
  depends_on             = [aws_route_table.public_default]
}

resource "aws_route" "public_prefix_list_route" {
  for_each = {
    for pair in setproduct(var.vpc_public_subnet_prefix_list_routes, range(length(var.vpc_public_subnets))) :
    "${pair[0]}_${pair[1]}" => {
      prefix_list_id = pair[0]
      route_table_id = aws_route_table.public_default[pair[1]].id
    }
  }
  destination_prefix_list_id = each.value["prefix_list_id"]
  route_table_id             = each.value["route_table_id"]
  transit_gateway_id         = var.transit_gateway_id
}

# Add public network acl
resource "aws_network_acl" "public_default_nacl" {
  count      = length(var.vpc_public_subnets)
  vpc_id     = aws_vpc.default.id
  subnet_ids = [element(aws_subnet.public_default.*.id, count.index)]

  egress {
    protocol   = "-1"
    rule_no    = 2
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }

  ingress {
    protocol   = "-1"
    rule_no    = 1
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }

  tags = {
    Name         = "${var.environment}-${var.service_name}-public-network-acl-${count.index}"
    environment  = var.environment
    service_name = var.service_name
  }
}

resource "aws_eip" "nat_eip" {
  count = length(var.vpc_public_subnets)
  vpc   = true
}

# Add private subnets
resource "aws_subnet" "private_default" {
  count             = length(var.vpc_private_subnets)
  vpc_id            = aws_vpc.default.id
  cidr_block        = element(values(var.vpc_private_subnets), count.index)
  availability_zone = element(keys(var.vpc_private_subnets), count.index)

  tags = {
    Name         = "${var.environment}-${var.service_name}-private-subnet-${count.index}"
    environment  = var.environment
    service_name = var.service_name
    tier         = "private"
  }
}

# Add route table
resource "aws_route_table" "private_default" {
  count  = length(var.vpc_private_subnets)
  vpc_id = aws_vpc.default.id

  tags = {
    Name         = "${var.environment}-${var.service_name}-private-route-table-${count.index}"
    environment  = var.environment
    service_name = var.service_name
  }
}

# Associate route table with private subnets
resource "aws_route_table_association" "private_default" {
  count          = length(var.vpc_private_subnets)
  subnet_id      = element(aws_subnet.private_default.*.id, count.index)
  route_table_id = element(aws_route_table.private_default.*.id, count.index)
}

resource "aws_nat_gateway" "nat_gw" {
  count         = length(var.vpc_private_subnets)
  allocation_id = element(aws_eip.nat_eip.*.id, count.index)
  subnet_id     = element(aws_subnet.public_default.*.id, count.index)
  depends_on    = [aws_internet_gateway.igw]

  tags = {
    Name         = "${var.environment}-${var.service_name}-nat-gateway-${count.index}"
    environment  = var.environment
    service_name = var.service_name
  }
}

# Add a nat gateway to private route table
resource "aws_route" "private_nat_gateway_route" {
  count                  = length(var.vpc_private_subnets)
  route_table_id         = element(aws_route_table.private_default.*.id, count.index)
  destination_cidr_block = "0.0.0.0/0"
  depends_on             = [aws_route_table.private_default]
  nat_gateway_id         = element(aws_nat_gateway.nat_gw.*.id, count.index)
}

# Add internal routes to private route table
resource "aws_route" "private_internal_route" {
  count                  = length(var.vpc_private_subnet_routes)
  route_table_id         = element(aws_route_table.private_default.*.id, count.index)
  destination_cidr_block = var.vpc_private_subnet_routes[count.index]
  transit_gateway_id     = var.transit_gateway_id
  depends_on             = [aws_route_table.private_default]
}

resource "aws_route" "private_internal_route_2" {
  count                  = length(var.vpc_private_subnet_routes)
  route_table_id         = element(aws_route_table.private_default.*.id, count.index + 1)
  destination_cidr_block = var.vpc_private_subnet_routes[count.index]
  transit_gateway_id     = var.transit_gateway_id
  depends_on             = [aws_route_table.private_default]
}

resource "aws_route" "private_prefix_list_route" {
  for_each = {
    for pair in setproduct(var.vpc_private_subnet_prefix_list_routes, range(length(var.vpc_private_subnets))) :
    "${pair[0]}_${pair[1]}" => {
      prefix_list_id = pair[0]
      route_table_id = aws_route_table.private_default[pair[1]].id
    }
  }
  destination_prefix_list_id = each.value["prefix_list_id"]
  route_table_id             = each.value["route_table_id"]
  transit_gateway_id         = var.transit_gateway_id
}

# add private network acl
resource "aws_network_acl" "private_default_nacl" {
  count      = length(var.vpc_private_subnets)
  vpc_id     = aws_vpc.default.id
  subnet_ids = [element(aws_subnet.private_default.*.id, count.index)]

  egress {
    protocol   = "-1"
    rule_no    = 2
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }

  ingress {
    protocol   = "-1"
    rule_no    = 1
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }

  tags = {
    Name         = "${var.environment}-${var.service_name}-private-network-acl-${count.index}"
    environment  = var.environment
    service_name = var.service_name
  }
}

# Add existing VPC peering routes to private route table
resource "aws_route" "vpc_peering_routes" {
  count                     = length(var.vpc_peering_routes)
  route_table_id            = element(aws_route_table.private_default.*.id, count.index)
  destination_cidr_block    = element(keys(var.vpc_peering_routes), count.index)
  vpc_peering_connection_id = element(values(var.vpc_peering_routes), count.index)
  depends_on                = [aws_route_table.private_default]
}

resource "aws_route" "vpc_peering_routes_2" {
  count                     = length(var.vpc_peering_routes)
  route_table_id            = element(aws_route_table.private_default.*.id, count.index + 1)
  destination_cidr_block    = element(keys(var.vpc_peering_routes), count.index)
  vpc_peering_connection_id = element(values(var.vpc_peering_routes), count.index)
  depends_on                = [aws_route_table.private_default]
}

