################################################################################
# Module: redis
# Main — AWS ElastiCache Redis 7
#
# - Single shard (cluster mode disabled): one primary + one replica
# - Encryption at rest and in transit (TLS)
# - AUTH token required when transit encryption is enabled
# - VPC-internal only — no public access
# - Access restricted to explicitly allowed security groups (app only)
# - Slow log + engine log delivery to CloudWatch
################################################################################

terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.40.0"
    }
  }
}

locals {
  identifier = "${var.project}-${var.environment}"

  common_tags = {
    environment = var.environment
    project     = var.project
    managed_by  = "terraform"
  }
}

################################################################################
# CloudWatch Log Group for Redis logs
################################################################################

resource "aws_cloudwatch_log_group" "redis" {
  count = var.log_delivery_enabled ? 1 : 0

  name              = var.log_group_name
  retention_in_days = 30

  tags = local.common_tags
}

################################################################################
# Security Group — only the app SGs may connect on 6379
################################################################################

resource "aws_security_group" "redis" {
  name        = "${local.identifier}-redis-sg"
  description = "Controls inbound access to ElastiCache Redis — allow only app SG on 6379"
  vpc_id      = var.vpc_id

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

  tags = merge(local.common_tags, {
    Name = "${local.identifier}-redis-sg"
  })
}

resource "aws_security_group_rule" "redis_ingress_from_app" {
  for_each = toset(var.allowed_security_group_ids)

  type                     = "ingress"
  description              = "Redis from app security group"
  from_port                = 6379
  to_port                  = 6379
  protocol                 = "tcp"
  source_security_group_id = each.value
  security_group_id        = aws_security_group.redis.id
}

################################################################################
# ElastiCache Subnet Group
################################################################################

resource "aws_elasticache_subnet_group" "main" {
  name        = "${local.identifier}-redis-subnet-group"
  description = "Private subnets for AgentIdP ElastiCache Redis"
  subnet_ids  = var.subnet_ids

  tags = local.common_tags
}

################################################################################
# ElastiCache Parameter Group — Redis 7.x defaults are fine; custom group
# allows future tuning without recreating the replication group.
################################################################################

resource "aws_elasticache_parameter_group" "main" {
  name        = "${local.identifier}-redis7-params"
  family      = "redis7"
  description = "AgentIdP Redis 7 parameter group"

  # Disable dangerous commands that could truncate data in production
  parameter {
    name  = "lazyfree-lazy-eviction"
    value = "yes"
  }

  parameter {
    name  = "lazyfree-lazy-expire"
    value = "yes"
  }

  tags = local.common_tags
}

################################################################################
# ElastiCache Replication Group (cluster mode disabled)
#
# cluster_mode = 0 (disabled) gives a single-shard setup:
#   - 1 primary node
#   - num_cache_clusters - 1 replica nodes
# This matches the application usage: token revocation (SET/GET/DEL),
# rate limiting (INCR/EXPIRE), and monthly counters (INCR) — no sharding needed.
################################################################################

resource "aws_elasticache_replication_group" "main" {
  replication_group_id = local.identifier
  description          = "AgentIdP Redis 7 — token revocation, rate limiting, counters"

  # Engine
  engine               = "redis"
  engine_version       = var.engine_version
  node_type            = var.node_type
  parameter_group_name = aws_elasticache_parameter_group.main.name
  port                 = 6379

  # Topology — single shard, primary + replica
  num_cache_clusters         = var.num_cache_clusters
  automatic_failover_enabled = var.automatic_failover_enabled
  multi_az_enabled           = var.multi_az_enabled

  # Network — VPC-internal, no public endpoints
  subnet_group_name  = aws_elasticache_subnet_group.main.name
  security_group_ids = [aws_security_group.redis.id]

  # Security
  at_rest_encryption_enabled = var.at_rest_encryption_enabled
  transit_encryption_enabled = var.transit_encryption_enabled
  auth_token                 = var.transit_encryption_enabled && var.auth_token != "" ? var.auth_token : null

  # Maintenance and snapshots
  maintenance_window       = var.maintenance_window
  snapshot_retention_limit = var.snapshot_retention_limit
  snapshot_window          = var.snapshot_window
  apply_immediately        = var.apply_immediately

  # Log delivery to CloudWatch
  dynamic "log_delivery_configuration" {
    for_each = var.log_delivery_enabled ? [
      { log_type = "slow-log", log_format = "json" },
      { log_type = "engine-log", log_format = "json" }
    ] : []

    content {
      destination      = var.log_delivery_enabled ? aws_cloudwatch_log_group.redis[0].name : ""
      destination_type = "cloudwatch-logs"
      log_format       = log_delivery_configuration.value.log_format
      log_type         = log_delivery_configuration.value.log_type
    }
  }

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