################################################################################
# Environment: aws
# Main — SentryAgent.ai AgentIdP on AWS
#
# Architecture:
#   Internet → Route 53 → ALB (public subnets, HTTPS/443) →
#   ECS Fargate tasks (private subnets) →
#   RDS PostgreSQL 14 (private subnets, Multi-AZ) +
#   ElastiCache Redis 7 (private subnets, primary + replica)
#
# All secrets stored in AWS Secrets Manager — ECS tasks pull at launch time.
# No sensitive values in state (except where Terraform internals require it).
################################################################################

terraform {
  required_version = ">= 1.6.0"

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

  # Remote state — configure your backend here.
  # Example using S3 + DynamoDB state locking:
  #
  # backend "s3" {
  #   bucket         = "sentryagent-terraform-state"
  #   key            = "agentidp/aws/production/terraform.tfstate"
  #   region         = "us-east-1"
  #   encrypt        = true
  #   dynamodb_table = "sentryagent-terraform-locks"
  # }
}

provider "aws" {
  region = var.region

  default_tags {
    tags = {
      environment = var.environment
      project     = var.project
      managed_by  = "terraform"
    }
  }
}

################################################################################
# Data sources
################################################################################

data "aws_caller_identity" "current" {}
data "aws_region" "current" {}

################################################################################
# VPC
################################################################################

resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "${var.project}-${var.environment}-vpc"
  }
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "${var.project}-${var.environment}-igw"
  }
}

################################################################################
# Subnets
################################################################################

resource "aws_subnet" "public" {
  count = length(var.availability_zones)

  vpc_id                  = aws_vpc.main.id
  cidr_block              = var.public_subnet_cidrs[count.index]
  availability_zone       = var.availability_zones[count.index]
  map_public_ip_on_launch = false

  tags = {
    Name = "${var.project}-${var.environment}-public-${var.availability_zones[count.index]}"
    tier = "public"
  }
}

resource "aws_subnet" "private" {
  count = length(var.availability_zones)

  vpc_id            = aws_vpc.main.id
  cidr_block        = var.private_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  tags = {
    Name = "${var.project}-${var.environment}-private-${var.availability_zones[count.index]}"
    tier = "private"
  }
}

################################################################################
# NAT Gateways — one per AZ for HA outbound from private subnets
# ECS tasks need outbound internet to pull ECR images and reach Secrets Manager.
################################################################################

resource "aws_eip" "nat" {
  count  = length(var.availability_zones)
  domain = "vpc"

  tags = {
    Name = "${var.project}-${var.environment}-nat-eip-${var.availability_zones[count.index]}"
  }

  depends_on = [aws_internet_gateway.main]
}

resource "aws_nat_gateway" "main" {
  count = length(var.availability_zones)

  allocation_id = aws_eip.nat[count.index].id
  subnet_id     = aws_subnet.public[count.index].id

  tags = {
    Name = "${var.project}-${var.environment}-nat-${var.availability_zones[count.index]}"
  }

  depends_on = [aws_internet_gateway.main]
}

################################################################################
# Route Tables
################################################################################

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 = {
    Name = "${var.project}-${var.environment}-public-rt"
  }
}

resource "aws_route_table_association" "public" {
  count = length(aws_subnet.public)

  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table" "private" {
  count  = length(var.availability_zones)
  vpc_id = aws_vpc.main.id

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.main[count.index].id
  }

  tags = {
    Name = "${var.project}-${var.environment}-private-rt-${var.availability_zones[count.index]}"
  }
}

resource "aws_route_table_association" "private" {
  count = length(aws_subnet.private)

  subnet_id      = aws_subnet.private[count.index].id
  route_table_id = aws_route_table.private[count.index].id
}

################################################################################
# VPC Endpoints — allow ECS tasks to reach AWS services without NAT
################################################################################

resource "aws_vpc_endpoint" "secretsmanager" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.region}.secretsmanager"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  private_dns_enabled = true

  tags = {
    Name = "${var.project}-${var.environment}-secretsmanager-endpoint"
  }
}

resource "aws_vpc_endpoint" "ecr_api" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.region}.ecr.api"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  private_dns_enabled = true

  tags = {
    Name = "${var.project}-${var.environment}-ecr-api-endpoint"
  }
}

resource "aws_vpc_endpoint" "ecr_dkr" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.region}.ecr.dkr"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  private_dns_enabled = true

  tags = {
    Name = "${var.project}-${var.environment}-ecr-dkr-endpoint"
  }
}

resource "aws_vpc_endpoint" "s3" {
  vpc_id            = aws_vpc.main.id
  service_name      = "com.amazonaws.${var.region}.s3"
  vpc_endpoint_type = "Gateway"
  route_table_ids   = aws_route_table.private[*].id

  tags = {
    Name = "${var.project}-${var.environment}-s3-endpoint"
  }
}

resource "aws_vpc_endpoint" "cloudwatch_logs" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.region}.logs"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  private_dns_enabled = true

  tags = {
    Name = "${var.project}-${var.environment}-logs-endpoint"
  }
}

################################################################################
# IAM — ECS Task Execution Role
# Allows ECS to pull images from ECR, write logs, and fetch secrets.
################################################################################

data "aws_iam_policy_document" "ecs_task_execution_assume" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["ecs-tasks.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "ecs_task_execution" {
  name               = "${var.project}-${var.environment}-ecs-execution-role"
  assume_role_policy = data.aws_iam_policy_document.ecs_task_execution_assume.json

  tags = {
    environment = var.environment
    project     = var.project
  }
}

resource "aws_iam_role_policy_attachment" "ecs_task_execution_managed" {
  role       = aws_iam_role.ecs_task_execution.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

# Allow the execution role to fetch the specific secrets it needs
data "aws_iam_policy_document" "ecs_task_execution_secrets" {
  statement {
    sid    = "GetAppSecrets"
    effect = "Allow"
    actions = [
      "secretsmanager:GetSecretValue",
      "secretsmanager:DescribeSecret"
    ]
    resources = concat(
      [
        aws_secretsmanager_secret.database_url.arn,
        aws_secretsmanager_secret.redis_url.arn,
        aws_secretsmanager_secret.jwt_private_key.arn,
        aws_secretsmanager_secret.jwt_public_key.arn,
      ],
      var.vault_token != "" ? [aws_secretsmanager_secret.vault_token[0].arn] : []
    )
  }
}

resource "aws_iam_role_policy" "ecs_task_execution_secrets" {
  name   = "${var.project}-${var.environment}-secrets-policy"
  role   = aws_iam_role.ecs_task_execution.id
  policy = data.aws_iam_policy_document.ecs_task_execution_secrets.json
}

################################################################################
# IAM — ECS Task Role
# Permissions granted to the running application container.
################################################################################

resource "aws_iam_role" "ecs_task" {
  name               = "${var.project}-${var.environment}-ecs-task-role"
  assume_role_policy = data.aws_iam_policy_document.ecs_task_execution_assume.json

  tags = {
    environment = var.environment
    project     = var.project
  }
}

# ECS task role policy — extend as needed for other AWS service calls.
data "aws_iam_policy_document" "ecs_task" {
  statement {
    sid    = "AllowCloudWatchMetrics"
    effect = "Allow"
    actions = [
      "cloudwatch:PutMetricData"
    ]
    resources = ["*"]
  }
}

resource "aws_iam_role_policy" "ecs_task" {
  name   = "${var.project}-${var.environment}-task-policy"
  role   = aws_iam_role.ecs_task.id
  policy = data.aws_iam_policy_document.ecs_task.json
}

################################################################################
# IAM — RDS Enhanced Monitoring Role
################################################################################

data "aws_iam_policy_document" "rds_monitoring_assume" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["monitoring.rds.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "rds_monitoring" {
  name               = "${var.project}-${var.environment}-rds-monitoring-role"
  assume_role_policy = data.aws_iam_policy_document.rds_monitoring_assume.json

  tags = {
    environment = var.environment
    project     = var.project
  }
}

resource "aws_iam_role_policy_attachment" "rds_monitoring" {
  role       = aws_iam_role.rds_monitoring.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"
}

################################################################################
# AWS Secrets Manager — store all sensitive values
################################################################################

resource "aws_secretsmanager_secret" "database_url" {
  name                    = "/${var.project}/${var.environment}/database-url"
  description             = "PostgreSQL DATABASE_URL for AgentIdP"
  recovery_window_in_days = 7

  tags = {
    environment = var.environment
    project     = var.project
  }
}

resource "aws_secretsmanager_secret_version" "database_url" {
  secret_id = aws_secretsmanager_secret.database_url.id
  # Build the DATABASE_URL using the RDS endpoint output.
  # The password is passed in as var.db_password so it never appears in plaintext
  # in any .tf file — only in this encrypted secret version.
  secret_string = "postgresql://${var.project}:${var.db_password}@${module.rds.endpoint}:${module.rds.port}/${module.rds.db_name}?sslmode=require"

  depends_on = [module.rds]
}

resource "aws_secretsmanager_secret" "redis_url" {
  name                    = "/${var.project}/${var.environment}/redis-url"
  description             = "Redis REDIS_URL for AgentIdP"
  recovery_window_in_days = 7

  tags = {
    environment = var.environment
    project     = var.project
  }
}

resource "aws_secretsmanager_secret_version" "redis_url" {
  secret_id = aws_secretsmanager_secret.redis_url.id
  # ElastiCache Redis with TLS uses the rediss:// scheme and requires an AUTH token.
  secret_string = "rediss://:${var.redis_auth_token}@${module.redis.primary_endpoint}:${module.redis.port}"

  depends_on = [module.redis]
}

resource "aws_secretsmanager_secret" "jwt_private_key" {
  name                    = "/${var.project}/${var.environment}/jwt-private-key"
  description             = "RSA-2048 private key for signing AgentIdP JWTs"
  recovery_window_in_days = 7

  tags = {
    environment = var.environment
    project     = var.project
  }
}

resource "aws_secretsmanager_secret_version" "jwt_private_key" {
  secret_id     = aws_secretsmanager_secret.jwt_private_key.id
  secret_string = var.jwt_private_key
}

resource "aws_secretsmanager_secret" "jwt_public_key" {
  name                    = "/${var.project}/${var.environment}/jwt-public-key"
  description             = "RSA-2048 public key for verifying AgentIdP JWTs"
  recovery_window_in_days = 7

  tags = {
    environment = var.environment
    project     = var.project
  }
}

resource "aws_secretsmanager_secret_version" "jwt_public_key" {
  secret_id     = aws_secretsmanager_secret.jwt_public_key.id
  secret_string = var.jwt_public_key
}

resource "aws_secretsmanager_secret" "vault_token" {
  count = var.vault_token != "" ? 1 : 0

  name                    = "/${var.project}/${var.environment}/vault-token"
  description             = "HashiCorp Vault token for AgentIdP"
  recovery_window_in_days = 7

  tags = {
    environment = var.environment
    project     = var.project
  }
}

resource "aws_secretsmanager_secret_version" "vault_token" {
  count = var.vault_token != "" ? 1 : 0

  secret_id     = aws_secretsmanager_secret.vault_token[0].id
  secret_string = var.vault_token
}

################################################################################
# Module: Load Balancer
################################################################################

module "lb" {
  source = "../../modules/lb"

  environment     = var.environment
  project         = var.project
  vpc_id          = aws_vpc.main.id
  subnet_ids      = aws_subnet.public[*].id
  certificate_arn = var.certificate_arn

  target_group_port          = 3000
  enable_deletion_protection = true
  access_logs_bucket         = var.alb_access_logs_bucket
}

################################################################################
# Module: RDS PostgreSQL
################################################################################

module "rds" {
  source = "../../modules/rds"

  environment = var.environment
  project     = var.project
  vpc_id      = aws_vpc.main.id
  subnet_ids  = aws_subnet.private[*].id

  # The app SG is created by the agentidp module; we wire it after both modules
  # are instantiated using a separate security group rule (see below).
  allowed_security_group_ids = []

  db_name     = "sentryagent_idp"
  db_username = var.project
  db_password = var.db_password

  instance_class             = var.rds_instance_class
  allocated_storage          = 50
  max_allocated_storage      = 500
  multi_az                   = true
  backup_retention_days      = var.rds_backup_retention_days
  deletion_protection        = var.rds_deletion_protection
  skip_final_snapshot        = var.rds_skip_final_snapshot
  monitoring_role_arn        = aws_iam_role.rds_monitoring.arn
  monitoring_interval        = 60
  performance_insights_enabled = true
}

################################################################################
# Module: Redis
################################################################################

module "redis" {
  source = "../../modules/redis"

  environment = var.environment
  project     = var.project
  vpc_id      = aws_vpc.main.id
  subnet_ids  = aws_subnet.private[*].id

  # Same pattern as RDS — app SG wired after agentidp module creates it.
  allowed_security_group_ids = []

  node_type                  = var.redis_node_type
  num_cache_clusters         = 2
  automatic_failover_enabled = true
  multi_az_enabled           = true
  at_rest_encryption_enabled = true
  transit_encryption_enabled = true
  auth_token                 = var.redis_auth_token
  snapshot_retention_limit   = 7
}

################################################################################
# Module: AgentIdP (ECS Fargate)
################################################################################

module "agentidp" {
  source = "../../modules/agentidp"

  provider_type = "aws"
  environment   = var.environment
  project       = var.project
  app_image     = "sentryagent/agentidp:${var.app_image_tag}"
  app_port      = 3000

  aws_region             = var.region
  aws_vpc_id             = aws_vpc.main.id
  aws_subnet_ids         = aws_subnet.private[*].id
  aws_target_group_arn   = module.lb.target_group_arn
  aws_execution_role_arn = aws_iam_role.ecs_task_execution.arn
  aws_task_role_arn      = aws_iam_role.ecs_task.arn
  aws_log_group_name     = "/ecs/${var.project}-${var.environment}"
  aws_desired_count      = var.ecs_desired_count
  aws_cpu                = 512
  aws_memory             = 1024
  aws_cors_origin        = var.cors_origin
  aws_policy_dir         = "/app/policies"
  aws_vault_addr         = var.vault_addr
  aws_vault_mount        = var.vault_mount

  aws_secret_database_url_arn   = aws_secretsmanager_secret.database_url.arn
  aws_secret_redis_url_arn      = aws_secretsmanager_secret.redis_url.arn
  aws_secret_jwt_private_key_arn = aws_secretsmanager_secret.jwt_private_key.arn
  aws_secret_jwt_public_key_arn = aws_secretsmanager_secret.jwt_public_key.arn
  aws_secret_vault_token_arn    = var.vault_token != "" ? aws_secretsmanager_secret.vault_token[0].arn : ""

  depends_on = [
    aws_secretsmanager_secret_version.database_url,
    aws_secretsmanager_secret_version.redis_url,
    aws_secretsmanager_secret_version.jwt_private_key,
    aws_secretsmanager_secret_version.jwt_public_key,
  ]
}

################################################################################
# Cross-module security group wiring
#
# The app SG (from agentidp module) must be allowed into RDS and Redis.
# These rules are created after both modules are fully instantiated to avoid
# circular references in the module dependency graph.
################################################################################

resource "aws_security_group_rule" "rds_from_app" {
  type                     = "ingress"
  description              = "PostgreSQL from ECS app tasks"
  from_port                = 5432
  to_port                  = 5432
  protocol                 = "tcp"
  source_security_group_id = module.agentidp.aws_app_security_group_id
  security_group_id        = module.rds.security_group_id
}

resource "aws_security_group_rule" "redis_from_app" {
  type                     = "ingress"
  description              = "Redis from ECS app tasks"
  from_port                = 6379
  to_port                  = 6379
  protocol                 = "tcp"
  source_security_group_id = module.agentidp.aws_app_security_group_id
  security_group_id        = module.redis.security_group_id
}

# Allow the ALB to reach ECS tasks on the app port
resource "aws_security_group_rule" "app_from_alb" {
  type                     = "ingress"
  description              = "App port from ALB"
  from_port                = 3000
  to_port                  = 3000
  protocol                 = "tcp"
  source_security_group_id = module.lb.alb_security_group_id
  security_group_id        = module.agentidp.aws_app_security_group_id
}

################################################################################
# Route 53 — alias record pointing the domain to the ALB
################################################################################

data "aws_route53_zone" "main" {
  name         = join(".", slice(split(".", var.domain_name), 1, length(split(".", var.domain_name))))
  private_zone = false
}

resource "aws_route53_record" "app" {
  zone_id = data.aws_route53_zone.main.zone_id
  name    = var.domain_name
  type    = "A"

  alias {
    name                   = module.lb.alb_dns_name
    zone_id                = module.lb.alb_zone_id
    evaluate_target_health = true
  }
}
