# ACDL platform infrastructure — contract ingestion Lambda + DynamoDB (D-051)
#
# Deploys:
#   - DynamoDB table acdl-contracts (PK consumerRepo, SK contractId#submittedAt, SSE via CMK, PITR)
#   - KMS customer-managed key for DynamoDB + SSM (shared CMK)
#   - Lambda function acdl-contract-ingestor (Python 3.12, handler contract_ingestor.lambda_handler)
#   - Lambda Function URL (IAM auth — consumers invoke via SigV4)
#   - Secrets Manager secret acdl/github-token (stores the Lambda's GitHub PAT for issue creation)
#   - IAM execution role for the Lambda (DynamoDB write + Secrets Manager read + KMS decrypt)
#
# State: terraform/platform/terraform.tfstate (separate from spike/ and microservice/)

terraform {
  required_version = ">= 1.9, < 1.10"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  backend "s3" {
    bucket = "acdl-tfstate-581513795199-us-east-1"
    key    = "platform/terraform.tfstate"
    region = "us-east-1"
  }
}

provider "aws" {
  region = "us-east-1"
}

# KMS customer-managed key for DynamoDB SSE + SSM Parameter Store encryption
resource "aws_kms_key" "acdl_platform" {
  description             = "ACDL platform KMS key (DynamoDB SSE + SSM + Secrets Manager)"
  enable_key_rotation     = true
  deletion_window_in_days = 30
}

resource "aws_kms_alias" "acdl_platform" {
  name          = "alias/acdl-platform"
  target_key_id = aws_kms_key.acdl_platform.key_id
}

# DynamoDB table for contract ingestion
resource "aws_dynamodb_table" "acdl_contracts" {
  name         = "acdl-contracts"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "consumerRepo"
  range_key    = "contractId#submittedAt"

  attribute {
    name = "consumerRepo"
    type = "S"
  }

  attribute {
    name = "contractId#submittedAt"
    type = "S"
  }

  point_in_time_recovery {
    enabled = true
  }

  server_side_encryption {
    enabled     = true
    kms_key_arn = aws_kms_key.acdl_platform.arn
  }

  tags = {
    "acdl:owner"       = "acdl"
    "acdl:contract"    = "platform"
    "acdl:environment" = "prod"
    "acdl:cost-center" = "acdl-default"
  }
}

# Secrets Manager secret for the Lambda's GitHub token (issue creation)
resource "aws_secretsmanager_secret" "github_token" {
  name        = "acdl/github-token"
  description = "GitHub PAT for the platform Lambda to create issues on the platform repo (D-055)."
  kms_key_id  = aws_kms_key.acdl_platform.arn

  tags = {
    "acdl:owner"       = "acdl"
    "acdl:contract"    = "platform"
    "acdl:environment" = "prod"
    "acdl:cost-center" = "acdl-default"
  }
}

# IAM execution role for the Lambda
resource "aws_iam_role" "lambda_exec" {
  name = "acdl-contract-ingestor-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = { Service = "lambda.amazonaws.com" }
    }]
  })
}

resource "aws_iam_role_policy" "lambda_permissions" {
  name = "acdl-contract-ingestor-policy"
  role = aws_iam_role.lambda_exec.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect   = "Allow"
        Action   = ["dynamodb:PutItem", "dynamodb:GetItem", "dynamodb:Query", "dynamodb:UpdateItem"]
        Resource = aws_dynamodb_table.acdl_contracts.arn
      },
      {
        Effect   = "Allow"
        Action   = ["dynamodb:GetItem", "dynamodb:Query"]
        Resource = aws_dynamodb_table.acdl_change_requests.arn
      },
      {
        Effect   = "Allow"
        Action   = ["secretsmanager:GetSecretValue"]
        Resource = aws_secretsmanager_secret.github_token.arn
      },
      {
        Effect   = "Allow"
        Action   = ["kms:Decrypt"]
        Resource = aws_kms_key.acdl_platform.arn
      },
      {
        Effect   = "Allow"
        Action   = ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"]
        Resource = "arn:aws:logs:*:*:*"
      }
    ]
  })
}

# Lambda function (conditional — only created when contract_ingestor.zip exists)
# The lifecycle pipeline only needs the VPC; the full platform deploy builds the zip first.
locals {
  lambda_zip_exists = fileexists("contract_ingestor.zip")
}

resource "aws_lambda_function" "contract_ingestor" {
  count            = local.lambda_zip_exists ? 1 : 0
  function_name    = "acdl-contract-ingestor"
  handler          = "contract_ingestor.lambda_handler"
  runtime          = "python3.12"
  role             = aws_iam_role.lambda_exec.arn
  filename         = "contract_ingestor.zip"
  source_code_hash = local.lambda_zip_exists ? filebase64sha256("contract_ingestor.zip") : "placeholder"

  environment {
    variables = {
      CONTRACTS_TABLE        = aws_dynamodb_table.acdl_contracts.name
      GITHUB_TOKEN_SECRET_ID = aws_secretsmanager_secret.github_token.name
      PLATFORM_REPO          = "acdl/acdl"
    }
  }

  tags = {
    "acdl:owner"       = "acdl"
    "acdl:contract"    = "platform"
    "acdl:environment" = "prod"
    "acdl:cost-center" = "acdl-default"
  }
}

# Lambda Function URL (IAM auth — consumers invoke via SigV4)
resource "aws_lambda_function_url" "contract_ingestor" {
  count              = local.lambda_zip_exists ? 1 : 0
  function_name      = aws_lambda_function.contract_ingestor[0].function_name
  authorization_type = "AWS_IAM"
}

# P1-6: Render the consumer invoke policy with the live AWS account ID.
# The JSON template (consumer_invoke_policy.json) uses ${account_id} and
# ${region} placeholders. Terraform renders them at apply time using the
# caller's live account ID — no hardcoded placeholder account IDs.
data "aws_caller_identity" "current" {}

data "aws_region" "current" {}

locals {
  invoke_policy_template = file("${path.module}/consumer_invoke_policy.json")
  rendered_invoke_policy = replace(
    replace(local.invoke_policy_template, "$${account_id}", data.aws_caller_identity.current.account_id),
    "$${region}", data.aws_region.current.name
  )
}

output "consumer_invoke_policy_rendered" {
  value       = local.rendered_invoke_policy
  description = "The consumer invoke policy JSON with the live account ID rendered. Distribute this to consumer accounts during onboarding."
}

# REQ-93: DynamoDB table for change requests (CMDB for decommission validation)
resource "aws_dynamodb_table" "acdl_change_requests" {
  name         = "acdl-change-requests"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "changeRequestId"
  range_key    = "submittedAt"

  attribute {
    name = "changeRequestId"
    type = "S"
  }

  attribute {
    name = "submittedAt"
    type = "S"
  }

  point_in_time_recovery {
    enabled = true
  }

  server_side_encryption {
    enabled     = true
    kms_key_arn = aws_kms_key.acdl_platform.arn
  }

  tags = {
    "acdl:owner"       = "acdl"
    "acdl:contract"    = "platform"
    "acdl:environment" = "prod"
    "acdl:cost-center" = "acdl-default"
  }
}
# REQ-107: SNS topic for separation-of-duties halt artifacts.
# route_halt_artifact publishes here when ACDL_SOD_HALT_TOPIC_ARN is set.
resource "aws_sns_topic" "acdl_sod_halt" {
  name              = "acdl-sod-halt"
  kms_master_key_id = aws_kms_key.acdl_platform.id
  tags = {
    "acdl:owner"       = "acdl"
    "acdl:contract"    = "platform"
    "acdl:environment" = "prod"
    "acdl:cost-center" = "acdl-default"
  }
}

output "acdl_sod_halt_topic_arn" {
  value = aws_sns_topic.acdl_sod_halt.arn
}

# ---------------------------------------------------------------------------
# P58: Single shared platform VPC — all consumer stacks reference this VPC
# via terraform_remote_state (data source). No per-contract VPC ever again.
# ---------------------------------------------------------------------------

resource "aws_vpc" "acdl_shared" {
  cidr_block = "10.0.0.0/16"
  tags = {
    Name               = "acdl-shared"
    "acdl:owner"       = "acdl"
    "acdl:contract"    = "platform"
    "acdl:environment" = "shared"
    "acdl:cost-center" = "acdl-default"
  }
}

resource "aws_subnet" "acdl_shared" {
  count             = 2
  vpc_id            = aws_vpc.acdl_shared.id
  cidr_block        = cidrsubnet(aws_vpc.acdl_shared.cidr_block, 8, count.index + 1)
  availability_zone = data.aws_availability_zones.available.names[count.index]
  tags = {
    Name               = "acdl-shared-subnet-${count.index}"
    "acdl:owner"       = "acdl"
    "acdl:contract"    = "platform"
    "acdl:environment" = "shared"
    "acdl:cost-center" = "acdl-default"
  }
}

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

resource "aws_internet_gateway" "acdl_shared" {
  vpc_id = aws_vpc.acdl_shared.id
  tags = {
    Name               = "acdl-shared-igw"
    "acdl:owner"       = "acdl"
    "acdl:contract"    = "platform"
    "acdl:environment" = "shared"
    "acdl:cost-center" = "acdl-default"
  }
}

resource "aws_route_table" "acdl_shared" {
  vpc_id = aws_vpc.acdl_shared.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.acdl_shared.id
  }
  tags = {
    Name               = "acdl-shared-rt"
    "acdl:owner"       = "acdl"
    "acdl:contract"    = "platform"
    "acdl:environment" = "shared"
    "acdl:cost-center" = "acdl-default"
  }
}

resource "aws_route_table_association" "acdl_shared" {
  count          = 2
  subnet_id      = aws_subnet.acdl_shared[count.index].id
  route_table_id = aws_route_table.acdl_shared.id
}

resource "aws_security_group" "ecs" {
  name        = "acdl-ecs-sg"
  description = "Security group for ECS Fargate services (platform VPC)"
  vpc_id      = aws_vpc.acdl_shared.id

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

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

  tags = {
    Name               = "acdl-ecs-sg"
    "acdl:owner"       = "acdl"
    "acdl:contract"    = "platform"
    "acdl:environment" = "shared"
    "acdl:cost-center" = "acdl-default"
  }
}

output "vpc_id" {
  value       = aws_vpc.acdl_shared.id
  description = "The shared platform VPC ID. Consumer stacks reference this via terraform_remote_state."
}

output "subnet_ids" {
  value       = join(",", aws_subnet.acdl_shared[*].id)
  description = "Comma-separated subnet IDs in the shared platform VPC."
}

output "ecs_security_group_id" {
  value       = aws_security_group.ecs.id
  description = "Security group ID for ECS Fargate services in the platform VPC."
}
