terraform {
  required_version = ">= 1.2"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    tls = {
      source  = "hashicorp/tls"
      version = "~> 4.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
  # Credentials from environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
}

# Define common tags for resources
locals {
  common_tags = {
    LEMC_UUID     = var.lemc_uuid
    LEMC_Scope    = var.lemc_scope
    LEMC_Username = var.lemc_username
    LEMC_User_ID  = var.lemc_user_id
    Scope         = var.lemc_scope
    Name          = var.resource_prefix
    Resource_Type = "aws-vm"
    Creation_Date = formatdate("YYYY-MM-DD", timestamp())
    Creation_Time = formatdate("hh:mm:ss", timestamp())
  }

  use_existing_certificate = var.existing_ssl_certificate_arn != ""

  # Use LEMC_UUID suffix instead of timestamp to make key pair names stable across apply/destroy
  # Extract first 8 characters of UUID for uniqueness while keeping names manageable
  uuid_suffix = substr(replace(var.lemc_uuid, "-", ""), 0, 8)

  # Helper function to create valid AWS resource names that don't end with hyphens
  safe_alb_name = substr(
    trimsuffix(
      substr(format("%s-alb", var.resource_prefix), 0, 32),
      "-"
    ),
    0, 32
  )

  safe_target_group_name = substr(
    trimsuffix(
      substr(format("%s-tg", var.resource_prefix), 0, 32),
      "-"
    ),
    0, 32
  )
}

# Generate SSH key pair (used for Linux SSH and Windows password decryption)
resource "tls_private_key" "ssh" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

# Create AWS Key Pair (Linux instances only)
resource "aws_key_pair" "ssh" {
  count      = var.is_windows ? 0 : 1
  key_name   = format("%s-keypair-%s", var.resource_prefix, local.uuid_suffix)
  public_key = tls_private_key.ssh.public_key_openssh
  tags       = local.common_tags
}

# Create AWS Key Pair for Windows password decryption
resource "aws_key_pair" "windows" {
  count      = var.is_windows ? 1 : 0
  key_name   = format("%s-windows-keypair-%s", var.resource_prefix, local.uuid_suffix)
  public_key = tls_private_key.ssh.public_key_openssh
  tags       = local.common_tags
}

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

  tags = merge(local.common_tags, {
    Name = format("%s-vpc", var.resource_prefix)
  })
}

# Create Internet Gateway
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = merge(local.common_tags, {
    Name = format("%s-igw", var.resource_prefix)
  })
}

# Create public subnet
resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  availability_zone       = var.availability_zone
  map_public_ip_on_launch = true

  tags = merge(local.common_tags, {
    Name = format("%s-public-subnet", var.resource_prefix)
  })
}

# Create second public subnet for ALB (ALB requires at least 2 subnets in different AZs)
resource "aws_subnet" "public_secondary" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.2.0/24"
  availability_zone       = var.availability_zone_secondary
  map_public_ip_on_launch = true

  tags = merge(local.common_tags, {
    Name = format("%s-public-subnet-secondary", var.resource_prefix)
  })
}

# Create route table for public subnets
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 = merge(local.common_tags, {
    Name = format("%s-public-rt", var.resource_prefix)
  })
}

# Associate route table with public subnet
resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

# Associate route table with secondary public subnet
resource "aws_route_table_association" "public_secondary" {
  subnet_id      = aws_subnet.public_secondary.id
  route_table_id = aws_route_table.public.id
}

# Security group for EC2 instance
resource "aws_security_group" "instance" {
  name_prefix = format("%s-instance-", var.resource_prefix)
  vpc_id      = aws_vpc.main.id

  # Allow all inbound traffic from anywhere
  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # Allow all outbound traffic
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = merge(local.common_tags, {
    Name = format("%s-instance-sg", var.resource_prefix)
  })
}

# Security group for ALB
resource "aws_security_group" "alb" {
  name_prefix = format("%s-alb-", var.resource_prefix)
  vpc_id      = aws_vpc.main.id

  # HTTP access
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # HTTPS access
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # All outbound traffic
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = merge(local.common_tags, {
    Name = format("%s-alb-sg", var.resource_prefix)
  })
}

# EC2 Instance
resource "aws_instance" "main" {
  ami                    = var.image
  instance_type          = var.instance_type
  key_name               = var.is_windows ? aws_key_pair.windows[0].key_name : aws_key_pair.ssh[0].key_name
  vpc_security_group_ids = [aws_security_group.instance.id]
  subnet_id              = aws_subnet.public.id
  availability_zone      = var.availability_zone

  user_data = var.is_windows ? null : base64encode(file("/lemc/private/configure.sh"))

  # For Windows instances, we need to get the password data
  get_password_data = var.is_windows

  # Enhanced networking (ENA) is enabled by default on supported instance types
  # in AWS provider v5.x, so no explicit configuration is needed

  # Enable nested virtualization support
  dynamic "cpu_options" {
    for_each = var.enable_nested_virtualization ? [1] : []
    content {
      # Enable Intel VT-x/AMD-V for nested virtualization
      # This allows running VMs, Docker, WSL, etc. inside the instance
      core_count       = null # Use instance default
      threads_per_core = 2    # Enable hyperthreading
    }
  }

  # Configure root volume with specified disk size
  root_block_device {
    volume_size           = var.disk_size
    volume_type           = "gp3"
    encrypted             = false
    delete_on_termination = true
  }

  tags = merge(local.common_tags, {
    Name = var.resource_prefix
  })

  lifecycle {
    create_before_destroy = true
  }
}

# Application Load Balancer
resource "aws_lb" "main" {
  name               = local.safe_alb_name
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = [aws_subnet.public.id, aws_subnet.public_secondary.id]

  enable_deletion_protection = false

  tags = local.common_tags
}

# Target Group
resource "aws_lb_target_group" "main" {
  name     = local.safe_target_group_name
  port     = var.port
  protocol = "HTTP"
  vpc_id   = aws_vpc.main.id

  health_check {
    enabled             = true
    healthy_threshold   = 2
    interval            = 30
    matcher             = "200"
    path                = "/"
    port                = "traffic-port"
    protocol            = "HTTP"
    timeout             = 5
    unhealthy_threshold = 2
  }

  tags = local.common_tags
}

# Target Group Attachment
resource "aws_lb_target_group_attachment" "main" {
  target_group_arn = aws_lb_target_group.main.arn
  target_id        = aws_instance.main.id
  port             = var.port
}

# ALB Listener for HTTP (redirect to HTTPS)
resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.main.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type = "redirect"

    redirect {
      port        = "443"
      protocol    = "HTTPS"
      status_code = "HTTP_301"
    }
  }
}

# ALB Listener for HTTPS
resource "aws_lb_listener" "https" {
  count             = local.use_existing_certificate ? 1 : 0
  load_balancer_arn = aws_lb.main.arn
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS-1-2-2017-01"
  certificate_arn   = var.existing_ssl_certificate_arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.main.arn
  }
}

# Route53 record
resource "aws_route53_record" "main" {
  zone_id = var.hosted_zone_id
  name    = var.domain_name
  type    = "A"

  alias {
    name                   = aws_lb.main.dns_name
    zone_id                = aws_lb.main.zone_id
    evaluate_target_health = true
  }
}