# ============================================================
# OmniCommerce Cloud — Terraform AWS Infrastructure
# EKS, RDS, ElastiCache, OpenSearch, MSK, S3, CloudFront
# ============================================================

terraform {
  required_version = ">= 1.7.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.50"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.30"
    }
    helm = {
      source  = "hashicorp/helm"
      version = "~> 2.13"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }

  backend "s3" {
    bucket         = "omnicommerce-terraform-state"
    key            = "aws/production/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "omnicommerce-terraform-locks"
  }
}

provider "aws" {
  region = var.aws_region
  default_tags {
    tags = {
      Environment = var.environment
      Project     = "omnicommerce"
      ManagedBy   = "terraform"
    }
  }
}

provider "kubernetes" {
  host                   = module.eks.cluster_endpoint
  cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data)
  token                  = data.aws_eks_cluster_auth.cluster.token
}

provider "helm" {
  kubernetes {
    host                   = module.eks.cluster_endpoint
    cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data)
    token                  = data.aws_eks_cluster_auth.cluster.token
  }
}

# ============================================================
# VARIABLES
# ============================================================

variable "aws_region" {
  default     = "us-east-1"
  description = "AWS region"
}

variable "environment" {
  default     = "production"
  description = "Environment name"
}

variable "vpc_cidr" {
  default = "10.0.0.0/16"
}

variable "domain_name" {
  default = "omnicommerce.io"
}

variable "db_instance_class" {
  default = "db.r6g.xlarge"
}

variable "db_multi_az" {
  default = true
}

variable "redis_node_type" {
  default = "cache.r6g.large"
}

variable "eks_cluster_version" {
  default = "1.29"
}

# ============================================================
# DATA SOURCES
# ============================================================

data "aws_availability_zones" "available" {
  state = "available"
}

data "aws_eks_cluster_auth" "cluster" {
  name = module.eks.cluster_name
}

data "aws_caller_identity" "current" {}

data "aws_route53_zone" "main" {
  name = var.domain_name
}

# ============================================================
# VPC
# ============================================================

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.8.0"

  name = "omnicommerce-${var.environment}"
  cidr = var.vpc_cidr

  azs             = slice(data.aws_availability_zones.available.names, 0, 3)
  private_subnets = [for k, v in slice(data.aws_availability_zones.available.names, 0, 3) : cidrsubnet(var.vpc_cidr, 8, k)]
  public_subnets  = [for k, v in slice(data.aws_availability_zones.available.names, 0, 3) : cidrsubnet(var.vpc_cidr, 8, k + 3)]
  intra_subnets   = [for k, v in slice(data.aws_availability_zones.available.names, 0, 3) : cidrsubnet(var.vpc_cidr, 8, k + 6)]

  enable_nat_gateway     = true
  single_nat_gateway     = var.environment != "production"
  enable_dns_hostnames   = true
  enable_dns_support     = true
  enable_flow_log        = true
  flow_log_destination_arn = aws_cloudwatch_log_group.vpc_flow_logs.arn

  tags = {
    "kubernetes.io/cluster/omnicommerce-${var.environment}" = "shared"
  }

  public_subnet_tags = {
    "kubernetes.io/role/elb" = 1
  }

  private_subnet_tags = {
    "kubernetes.io/role/internal-elb" = 1
  }
}

resource "aws_cloudwatch_log_group" "vpc_flow_logs" {
  name              = "/omnicommerce/${var.environment}/vpc-flow-logs"
  retention_in_days = 30
}

# ============================================================
# EKS CLUSTER
# ============================================================

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "20.8.0"

  cluster_name    = "omnicommerce-${var.environment}"
  cluster_version = var.eks_cluster_version

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnets

  cluster_endpoint_public_access = true
  cluster_endpoint_private_access = true

  cluster_addons = {
    coredns                = { most_recent = true }
    kube-proxy             = { most_recent = true }
    vpc-cni                = { most_recent = true }
    aws-ebs-csi-driver     = { most_recent = true }
    amazon-cloudwatch-observability = { most_recent = true }
  }

  eks_managed_node_groups = {
    # System nodes (core infrastructure)
    system = {
      name            = "system"
      instance_types  = ["t3a.medium"]
      min_size        = 2
      max_size        = 5
      desired_size    = 2
      capacity_type   = "ON_DEMAND"
      taints = [{
        key    = "CriticalAddonsOnly"
        value  = "true"
        effect = "NO_SCHEDULE"
      }]
      labels = {
        nodegroup = "system"
      }
      block_device_mappings = {
        xvda = {
          device_name = "/dev/xvda"
          ebs = {
            volume_size = 50
            volume_type = "gp3"
            encrypted   = true
          }
        }
      }
    }

    # Application nodes
    application = {
      name            = "application"
      instance_types  = ["c6a.xlarge", "c6i.xlarge", "c7a.xlarge"]
      min_size        = 3
      max_size        = 30
      desired_size    = 5
      capacity_type   = "ON_DEMAND"
      labels = {
        nodegroup = "application"
      }
      block_device_mappings = {
        xvda = {
          device_name = "/dev/xvda"
          ebs = {
            volume_size = 100
            volume_type = "gp3"
            encrypted   = true
          }
        }
      }
    }

    # Database nodes (PostgreSQL, Elasticsearch)
    database = {
      name            = "database"
      instance_types  = ["r6a.xlarge"]
      min_size        = 3
      max_size        = 6
      desired_size    = 3
      capacity_type   = "ON_DEMAND"
      labels = {
        nodegroup = "database"
      }
      taints = [{
        key    = "DatabaseOnly"
        value  = "true"
        effect = "NO_SCHEDULE"
      }]
      block_device_mappings = {
        xvda = {
          device_name = "/dev/xvda"
          ebs = {
            volume_size = 200
            volume_type = "gp3"
            iops        = 10000
            throughput  = 500
            encrypted   = true
          }
        }
      }
    }

    # Spot nodes for batch jobs and burst capacity
    spot = {
      name            = "spot"
      instance_types  = ["c6a.xlarge", "c6i.xlarge", "c7a.xlarge", "m6a.xlarge"]
      min_size        = 0
      max_size        = 20
      desired_size    = 2
      capacity_type   = "SPOT"
      labels = {
        nodegroup = "spot"
        lifecycle = "spot"
      }
      block_device_mappings = {
        xvda = {
          device_name = "/dev/xvda"
          ebs = {
            volume_size = 100
            volume_type = "gp3"
            encrypted   = true
          }
        }
      }
    }
  }

  enable_irsa = true

  tags = {
    Environment = var.environment
    Project     = "omnicommerce"
  }
}

# ============================================================
# RDS PostgreSQL
# ============================================================

module "rds" {
  source  = "terraform-aws-modules/rds/aws"
  version = "6.6.0"

  identifier = "omnicommerce-${var.environment}"

  engine         = "postgres"
  engine_version = "16.3"
  family         = "postgres16"

  instance_class    = var.db_instance_class
  allocated_storage = 500
  storage_encrypted = true
  storage_type      = "gp3"
  iops              = 10000

  db_name  = "omnicommerce"
  username = "omni_admin"
  port     = 5432

  multi_az               = var.db_multi_az
  create_db_subnet_group = true
  subnet_ids             = module.vpc.database_subnets

  vpc_security_group_ids = [aws_security_group.rds.id]

  maintenance_window = "sun:04:00-sun:06:00"
  backup_window      = "03:00-05:00"
  backup_retention_period = 30
  deletion_protection = true
  skip_final_snapshot  = false
  final_snapshot_identifier = "omnicommerce-${var.environment}-final"

  enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]

  parameters = [
    { name = "shared_preload_libraries", value = "pg_stat_statements,pg_cron", apply_method = "pending-reboot" },
    { name = "shared_buffers", value = "{DBInstanceClassMemory/4}" },
    { name = "effective_cache_size", value = "{DBInstanceClassMemory*3/4}" },
    { name = "work_mem", value = "26214" },
    { name = "maintenance_work_mem", value = "2097152" },
    { name = "max_connections", value = "500" },
    { name = "log_min_duration_statement", value = "1000" },
    { name = "log_connections", value = "1" },
    { name = "log_disconnections", value = "1" },
    { name = "log_lock_waits", value = "1" },
    { name = "log_temp_files", value = "0" },
    { name = "log_autovacuum_min_duration", value = "0" },
    { name = "ssl", value = "1" },
    { name = "ssl_min_protocol_version", value = "TLSv1.3" },
  ]

  create_db_option_group    = false
  create_db_parameter_group = true

  tags = {
    Environment = var.environment
    Project     = "omnicommerce"
  }
}

resource "aws_security_group" "rds" {
  name        = "omnicommerce-rds-${var.environment}"
  description = "RDS PostgreSQL security group"
  vpc_id      = module.vpc.vpc_id

  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [module.eks.cluster_security_group_id]
    description     = "Allow from EKS"
  }

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

# ============================================================
# ELASTICACHE (Redis)
# ============================================================

module "elasticache" {
  source  = "terraform-aws-modules/elasticache/aws"
  version = "3.3.0"

  cluster_id               = "omnicommerce-${var.environment}"
  engine                   = "redis"
  engine_version           = "7.1"
  node_type                = var.redis_node_type
  num_cache_clusters       = var.environment == "production" ? 3 : 1
  automatic_failover_enabled = var.environment == "production"

  port                     = 6379
  subnet_ids               = module.vpc.private_subnets
  security_group_ids       = [aws_security_group.redis.id]
  maintenance_window       = "sun:05:00-sun:08:00"
  snapshot_window          = "04:00-06:00"
  snapshot_retention_limit = 7

  at_rest_encryption_enabled  = true
  transit_encryption_enabled  = true
  auth_token                  = random_password.redis_auth.result

  parameter_group_name = aws_elasticache_parameter_group.redis.name
}

resource "aws_elasticache_parameter_group" "redis" {
  name   = "omnicommerce-redis-${var.environment}"
  family = "redis7"

  parameter {
    name  = "maxmemory-policy"
    value = "allkeys-lru"
  }

  parameter {
    name  = "timeout"
    value = "300"
  }

  parameter {
    name  = "tcp-keepalive"
    value = "300"
  }

  parameter {
    name  = "maxclients"
    value = "65000"
  }
}

resource "random_password" "redis_auth" {
  length  = 32
  special = false
}

resource "aws_security_group" "redis" {
  name        = "omnicommerce-redis-${var.environment}"
  description = "ElastiCache Redis security group"
  vpc_id      = module.vpc.vpc_id

  ingress {
    from_port       = 6379
    to_port         = 6379
    protocol        = "tcp"
    security_groups = [module.eks.cluster_security_group_id]
  }
}

# ============================================================
# OPENSEARCH (Elasticsearch)
# ============================================================

resource "aws_opensearch_domain" "search" {
  domain_name    = "omnicommerce-${var.environment}"
  engine_version = "OpenSearch_2.13"

  cluster_config {
    instance_type            = var.environment == "production" ? "r6g.xlarge.search" : "t3.medium.search"
    instance_count           = var.environment == "production" ? 3 : 1
    dedicated_master_enabled = var.environment == "production"
    dedicated_master_count   = 3
    dedicated_master_type    = "r6g.large.search"
    zone_awareness_enabled   = true
    warm_enabled             = var.environment == "production"
    warm_count               = 3
    warm_type                = "ultrawarm1.medium.search"
  }

  ebs_options {
    ebs_enabled = true
    volume_size = var.environment == "production" ? 500 : 100
    volume_type = "gp3"
    iops        = 10000
  }

  vpc_options {
    subnet_ids         = module.vpc.private_subnets
    security_group_ids = [aws_security_group.opensearch.id]
  }

  encrypt_at_rest {
    enabled = true
  }

  node_to_node_encryption {
    enabled = true
  }

  domain_endpoint_options {
    enforce_https       = true
    tls_security_policy = "Policy-Min-TLS-1-2-2019-07"
  }

  advanced_security_options {
    enabled                        = true
    internal_user_database_enabled = true
    master_user_options {
      master_user_name     = "admin"
      master_user_password = random_password.opensearch.result
    }
  }

  advanced_options = {
    "rest.action.multi.allow_explicit_index" = "true"
    "indices.fielddata.cache.size"           = "25"
  }

  access_policies = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = { AWS = "*" }
        Action   = "es:*"
        Resource = "arn:aws:es:${var.aws_region}:${data.aws_caller_identity.current.account_id}:domain/omnicommerce-${var.environment}/*"
      }
    ]
  })

  log_publishing_options {
    cloudwatch_log_group_arn = aws_cloudwatch_log_group.opensearch.arn
    log_type                 = "INDEX_SLOW_LOGS"
  }

  log_publishing_options {
    cloudwatch_log_group_arn = aws_cloudwatch_log_group.opensearch.arn
    log_type                 = "SEARCH_SLOW_LOGS"
  }

  log_publishing_options {
    cloudwatch_log_group_arn = aws_cloudwatch_log_group.opensearch.arn
    log_type                 = "ES_APPLICATION_LOGS"
  }
}

resource "random_password" "opensearch" {
  length  = 24
  special = true
}

resource "aws_cloudwatch_log_group" "opensearch" {
  name              = "/aws/opensearch/omnicommerce-${var.environment}"
  retention_in_days = 30
}

resource "aws_security_group" "opensearch" {
  name        = "omnicommerce-opensearch-${var.environment}"
  description = "OpenSearch security group"
  vpc_id      = module.vpc.vpc_id

  ingress {
    from_port       = 443
    to_port         = 443
    protocol        = "tcp"
    security_groups = [module.eks.cluster_security_group_id]
  }
}

# ============================================================
# MSK (Kafka) — OPTIONAL: Use Strimzi on K8s instead for cost
# ============================================================

# Uncomment for managed Kafka — expensive, use Strimzi on EKS for most cases
# module "msk" {
#   source  = "terraform-aws-modules/msk-kafka-cluster/aws"
#   version = "2.1.0"
#   
#   cluster_name = "omnicommerce-${var.environment}"
#   kafka_version = "3.6.0"
#   number_of_broker_nodes = 3
#   
#   broker_node_client_subnets  = module.vpc.private_subnets
#   broker_node_security_groups = [aws_security_group.msk.id]
#   broker_node_instance_type   = "kafka.m5.xlarge"
#   broker_node_storage_info = {
#     ebs_storage_info = {
#       volume_size = 500
#     }
#   }
#   
#   encryption_in_transit_client_broker = "TLS"
#   encryption_in_transit_in_cluster    = true
#   
#   configuration_server_properties = {
#     "auto.create.topics.enable" = false
#     "default.replication.factor" = 3
#     "min.insync.replicas" = 2
#     "num.partitions" = 12
#   }
# }

# ============================================================
# S3 — Media Storage + Static Assets
# ============================================================

module "s3_media" {
  source  = "terraform-aws-modules/s3-bucket/aws"
  version = "4.1.0"

  bucket = "omnicommerce-media-${var.environment}"
  acl    = "private"

  versioning = {
    enabled = true
  }

  server_side_encryption_configuration = {
    rule = {
      apply_server_side_encryption_by_default = {
        sse_algorithm = "AES256"
      }
    }
  }

  lifecycle_rule = [
    {
      id      = "intelligent-tiering"
      enabled = true
      transition = [
        {
          days          = 90
          storage_class = "INTELLIGENT_TIERING"
        }
      ]
    }
  ]

  cors_rule = [
    {
      allowed_methods = ["GET", "PUT", "POST", "DELETE"]
      allowed_origins = ["https://*.${var.domain_name}"]
      allowed_headers = ["*"]
      max_age_seconds = 3600
    }
  ]
}

# ============================================================
# CLOUDFRONT CDN
# ============================================================

resource "aws_cloudfront_distribution" "cdn" {
  enabled         = true
  is_ipv6_enabled = true
  comment         = "OmniCommerce CDN - ${var.environment}"
  price_class     = "PriceClass_All"
  aliases         = ["cdn.${var.domain_name}"]

  origin {
    domain_name = module.s3_media.s3_bucket_bucket_regional_domain_name
    origin_id   = "S3-media"
    s3_origin_config {
      origin_access_identity = aws_cloudfront_origin_access_identity.cdn.cloudfront_access_identity_path
    }
  }

  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD", "OPTIONS"]
    target_origin_id = "S3-media"
    compress         = true
    viewer_protocol_policy = "redirect-to-https"

    forwarded_values {
      query_string = true
      headers      = ["Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"]
      cookies {
        forward = "none"
      }
    }

    min_ttl     = 0
    default_ttl = 86400
    max_ttl     = 31536000
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    acm_certificate_arn      = aws_acm_certificate.cdn.arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }

  web_acl_id = aws_wafv2_web_acl.cdn.arn
}

resource "aws_cloudfront_origin_access_identity" "cdn" {
  comment = "OmniCommerce CDN OAI"
}

resource "aws_acm_certificate" "cdn" {
  domain_name       = "cdn.${var.domain_name}"
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_wafv2_web_acl" "cdn" {
  name        = "omnicommerce-cdn-${var.environment}"
  description = "WAF for CloudFront CDN"
  scope       = "CLOUDFRONT"

  default_action {
    allow {}
  }

  # Rate limiting rule
  rule {
    name     = "rate-limit"
    priority = 1

    action {
      block {}
    }

    statement {
      rate_based_statement {
        limit              = 2000
        aggregate_key_type = "IP"
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name               = "cdn-rate-limit"
      sampled_requests_enabled  = true
    }
  }

  # AWS managed rule groups
  rule {
    name     = "aws-managed-common"
    priority = 10
    override_action {
      none {}
    }
    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesCommonRuleSet"
        vendor_name = "AWS"
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name               = "aws-managed-common"
      sampled_requests_enabled  = true
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name               = "omnicommerce-cdn-waf"
    sampled_requests_enabled  = true
  }
}

# ============================================================
# ROUTE53 DNS
# ============================================================

resource "aws_route53_record" "api" {
  zone_id = data.aws_route53_zone.main.zone_id
  name    = "api.${var.domain_name}"
  type    = "CNAME"
  ttl     = 300
  records = [module.eks.cluster_endpoint]
}

resource "aws_route53_record" "cdn" {
  zone_id = data.aws_route53_zone.main.zone_id
  name    = "cdn.${var.domain_name}"
  type    = "CNAME"
  ttl     = 300
  records = [aws_cloudfront_distribution.cdn.domain_name]
}

# ============================================================
# ECR REPOSITORIES
# ============================================================

resource "aws_ecr_repository" "repos" {
  for_each = toset(["backend", "web", "admin", "vendor-dashboard"])

  name                 = "omnicommerce/${each.key}"
  image_tag_mutability = "IMMUTABLE"
  force_delete         = false

  image_scanning_configuration {
    scan_on_push = true
  }

  encryption_configuration {
    encryption_type = "AES256"
  }
}

resource "aws_ecr_lifecycle_policy" "repos" {
  for_each = aws_ecr_repository.repos

  repository = each.value.name
  policy = jsonencode({
    rules = [
      {
        rulePriority = 1
        description  = "Keep last 50 images"
        selection = {
          tagStatus   = "any"
          countType   = "imageCountMoreThan"
          countNumber = 50
        }
        action = { type = "expire" }
      }
    ]
  })
}

# ============================================================
# SECRETS MANAGER
# ============================================================

resource "aws_secretsmanager_secret" "db" {
  name        = "omnicommerce/${var.environment}/database"
  description = "RDS credentials for OmniCommerce"
}

resource "aws_secretsmanager_secret_version" "db" {
  secret_id = aws_secretsmanager_secret.db.id
  secret_string = jsonencode({
    username = module.rds.db_instance_username
    password = random_password.db_master.result
    host     = module.rds.db_instance_address
    port     = 5432
    dbname   = "omnicommerce"
  })
}

resource "random_password" "db_master" {
  length  = 32
  special = false
}

# ============================================================
# OUTPUTS
# ============================================================

output "eks_cluster_name" {
  value = module.eks.cluster_name
}

output "eks_cluster_endpoint" {
  value = module.eks.cluster_endpoint
}

output "rds_endpoint" {
  value     = module.rds.db_instance_address
  sensitive = true
}

output "elasticache_endpoint" {
  value     = module.elasticache.cluster_address
  sensitive = true
}

output "opensearch_endpoint" {
  value = aws_opensearch_domain.search.endpoint
}

output "s3_media_bucket" {
  value = module.s3_media.s3_bucket_id
}

output "cloudfront_domain" {
  value = aws_cloudfront_distribution.cdn.domain_name
}

output "ecr_repository_urls" {
  value = { for k, v in aws_ecr_repository.repos : k => v.repository_url }
}

output "configure_kubectl" {
  value = "aws eks update-kubeconfig --region ${var.aws_region} --name ${module.eks.cluster_name}"
}
