variable "vpc_id" {
  description = "ID of the VPC in which to create security groups"
  type        = string
}

variable "name_prefix" {
  description = "Prefix prepended to every security group name created by this module"
  type        = string

  validation {
    condition     = length(trimspace(var.name_prefix)) > 0
    error_message = "name_prefix must not be empty."
  }
}

variable "security_groups" {
  description = "Map of security group definitions"
  type = map(object({
    name_suffix = string
    description = string
    ingress_rules = optional(list(object({
      description      = optional(string, "")
      protocol         = string
      from_port        = number
      to_port          = number
      ipv4_cidr_blocks = optional(list(string), [])
      ipv6_cidr_blocks = optional(list(string), [])
      source_sg_key    = optional(string, null)
      source_sg_id     = optional(string, null)
      self             = optional(bool, false)
    })), [])
    egress_rules = optional(list(object({
      description      = optional(string, "")
      protocol         = string
      from_port        = number
      to_port          = number
      ipv4_cidr_blocks = optional(list(string), [])
      ipv6_cidr_blocks = optional(list(string), [])
      source_sg_key    = optional(string, null)
      source_sg_id     = optional(string, null)
      self             = optional(bool, false)
    })), [])
  }))
  default = {}

  validation {
    condition = alltrue([
      for sg_key, sg in var.security_groups :
      length(sg.ingress_rules) > 0 || length(sg.egress_rules) > 0
    ])
    error_message = <<-EOT
      One or more security groups have no ingress or egress rules.

      Each security group must define at least one rule in either
      ingress_rules or egress_rules. A security group with neither
      does not allow any traffic movement. Fix by either removing
      the empty security group from security_groups, or adding at
      least one rule to it.
    EOT
  }

  validation {
    condition = alltrue(flatten([
      for sg_key, sg in var.security_groups : [
        for rule in concat(sg.ingress_rules, sg.egress_rules) :
        length(rule.ipv4_cidr_blocks) > 0
        || length(rule.ipv6_cidr_blocks) > 0
        || rule.source_sg_key != null
        || rule.source_sg_id != null
        || rule.self
      ]
    ]))
    error_message = <<-EOT
      One or more rules have no source configured.

      Every ingress or egress rule must set at least one of:
      ipv4_cidr_blocks, ipv6_cidr_blocks, source_sg_key, source_sg_id,
      or self = true. A rule with none of these produces no AWS
      resource at all, which is almost always a mistake.
    EOT
  }

  validation {
    condition = alltrue(flatten([
      for sg_key, sg in var.security_groups : [
        for rule in concat(sg.ingress_rules, sg.egress_rules) :
        rule.source_sg_id == null || can(regex("^sg-[a-z0-9]+$", rule.source_sg_id))
      ]
    ]))
    error_message = <<-EOT
      One or more source_sg_id values are not a valid format. It must 
      be a security group ID (e.g. "sg-0123abcd").
    EOT
  }

  validation {
    condition = alltrue(flatten([
      for sg_key, sg in var.security_groups : [
        for rule in concat(sg.ingress_rules, sg.egress_rules) :
        rule.source_sg_key == null || contains(keys(var.security_groups), rule.source_sg_key)
      ]
    ]))
    error_message = <<-EOT
      One or more source_sg_key values reference a security group
      that is not defined in the security_groups map. It must match one of
      the keys in the security_groups map.
    EOT
  }
}

variable "tags" {
  description = "Resource tags to apply to all resources"
  type        = map(string)
  default     = {}
}
