# EC2 Nodes ====================================================================

# Key pairs --------------------------------------------------------------------

resource "aws_key_pair" "this" {
  for_each = var.key_pairs

  key_name   = "${var.name_prefix}-${local.region_abbr}-keypair-${each.value.name}"
  public_key = each.value.public_key

  tags = merge(var.tags, local.module_tags, {
    resource-type = "ec2-key-pair"
  })
}

# Instances --------------------------------------------------------------------

resource "aws_instance" "this" {
  for_each = var.instances

  ami                         = each.value.ami_id
  instance_type               = each.value.instance_type
  subnet_id                   = each.value.subnet_id
  vpc_security_group_ids      = each.value.security_group_ids
  iam_instance_profile        = each.value.iam_instance_profile
  key_name                    = try(aws_key_pair.this[each.value.key_name].key_name, each.value.key_name)
  associate_public_ip_address = each.value.associate_public_ip
  ebs_optimized               = each.value.ebs_optimized
  monitoring                  = each.value.monitoring
  user_data                   = each.value.user_data_base64 == null ? each.value.user_data : null
  user_data_base64            = each.value.user_data_base64
  placement_group             = each.value.placement_group
  tenancy                     = each.value.tenancy
  private_ip                  = each.value.primary_private_ip
  secondary_private_ips       = each.value.secondary_private_ips

  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = each.value.metadata_http_tokens
    http_put_response_hop_limit = each.value.metadata_hop_limit
  }

  root_block_device {
    volume_size           = each.value.root_volume.size
    volume_type           = each.value.root_volume.type
    encrypted             = true
    kms_key_id            = each.value.root_volume.kms_key_id
    delete_on_termination = each.value.root_volume.delete_on_termination
    tags = merge(var.tags, each.value.instance_tags, each.value.root_volume.tags, local.module_tags, {
      Name          = "${local.instance_names[each.key]}-root-vol"
      resource-type = "ebs-volume"
    })
  }

  tags = merge(var.tags, each.value.instance_tags, local.module_tags, {
    Name          = local.instance_names[each.key]
    resource-type = "ec2-instance"
  })

  lifecycle {
    ignore_changes = [
      user_data,
      user_data_base64,
    ]

    postcondition {
      condition     = self.instance_state == "running"
      error_message = "Instance ${self.tags.Name} did not reach running state after apply."
    }

    postcondition {
      condition     = self.primary_network_interface_id != null
      error_message = "Instance ${self.tags.Name} has no primary network interface — check subnet and security group config."
    }

    postcondition {
      condition = length(each.value.secondary_private_ips) == 0 || alltrue([
        for ip in each.value.secondary_private_ips : contains(self.secondary_private_ips, ip)
      ])
      error_message = "Instance ${self.tags.Name} did not receive all requested secondary private IPs."
    }

    postcondition {
      condition     = self.metadata_options[0].http_tokens == "required"
      error_message = "Instance ${self.tags.Name} is not enforcing IMDSv2. metadata_http_tokens must be 'required'."
    }
  }
}

# Data volumes -----------------------------------------------------------------

resource "aws_ebs_volume" "data" {
  for_each = local.data_volume_map

  availability_zone = aws_instance.this[each.value.instance_key].availability_zone
  size              = each.value.vol.volume_size
  type              = each.value.vol.volume_type
  encrypted         = true
  kms_key_id        = each.value.vol.kms_key_id
  iops              = each.value.vol.iops
  throughput        = each.value.vol.throughput

  tags = merge(var.tags, each.value.vol.tags, local.module_tags, {
    Name          = "${local.instance_names[each.value.instance_key]}-data-vol-${format("%02d", each.value.idx + 1)}"
    resource-type = "ebs-volume"
  })
}

resource "aws_volume_attachment" "data" {
  for_each = local.data_volume_map

  device_name = each.value.vol.device_name
  volume_id   = aws_ebs_volume.data[each.key].id
  instance_id = aws_instance.this[each.value.instance_key].id
}
