module "default_tags" {
  source             = "git@github.com:theorchard/terraform-default-tags.git//?ref=2.0.0"
  environment        = var.environment
  application_family = var.application_family
  service_name       = var.service_name
  team_name          = "abacus"
}

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = module.default_tags.tags
  }
}

terraform {
  backend "s3" {
    bucket  = "qa-accounting-terraform-state"
    key     = "qa/statementdb/terraform.tfstate"
    region  = "us-east-1"
    encrypt = "true"
  }
}

data "aws_caller_identity" "current" {}

data "aws_iam_policy" "shared_ami_kms_policy" {
  name = "KMS-shared-base-amis-RO"
}

data "aws_route53_zone" "route53_zone" {
  name         = "qa-accounting.theorchard.io."
  private_zone = false
}

data "aws_ec2_managed_prefix_list" "orchard_prod_private_prefix_list" {
  name = "prod-orcd-private-subnet-prefix-list"
}

data "aws_ec2_managed_prefix_list" "vpn_prefix_list" {
  name = "vpn-sme-internal-primary"
}

# Slaughterhouse instance security group
data "aws_security_group" "slaughterhouse_instance_security_group" {
  name = "${var.environment}-slaughterhouse-instance-sg"
}

# ECS extract-files task security group
data "aws_security_group" "ecs_abacus_extract_sales_task_security_group" {
  name = "${var.environment}-ecs-abacus-extract-sales-task-security-group"
}

module "vpc_info" {
  source = "git@github.com:theorchard/terraform-vpc-info.git?ref=1.0.0"

  environment = var.environment
}

data "aws_subnet" "instance_subnet" {
  filter {
    name   = "vpc-id"
    values = [module.vpc_info.vpc_id]
  }
  filter {
    name = "tag:Name"
    values = [
      "${var.environment}_private_subnet_0",
    ]
  }
}

data "aws_iam_policy_document" "assume_instance_role" {
  statement {
    actions = [
      "sts:AssumeRole",
    ]

    principals {
      type = "Service"
      identifiers = [
        "ec2.amazonaws.com",
      ]
    }
  }
}

# Create KMS key for EBS encryption
resource "aws_kms_key" "kms_key" {
  description             = "${var.environment}-${var.service_name}"
  enable_key_rotation     = true
  deletion_window_in_days = 30

  tags = {
    environment        = var.environment
    service_name       = var.service_name
    application_family = var.application_family
    map-migrated       = var.map_migrated_tag
    terraformed        = true
  }
}

resource "aws_kms_alias" "kms_alias" {
  name          = "alias/${var.environment}-${var.service_name}"
  target_key_id = aws_kms_key.kms_key.key_id
}

data "aws_iam_policy_document" "kms_usage_policy" {
  statement {
    actions = [
      "kms:CreateGrant",
      "kms:Decrypt",
      "kms:DescribeKey",
      "kms:Encrypt",
      "kms:GenerateDataKey*",
      "kms:ReEncrypt*",
      "kms:RetireGrant",
    ]

    resources = [
      aws_kms_key.kms_key.arn,
    ]
  }
}

# Instance role
resource "aws_iam_role" "instance_iam_role" {
  name               = "${var.environment}-${var.service_name}-instance-role"
  assume_role_policy = data.aws_iam_policy_document.assume_instance_role.json
}

resource "aws_iam_instance_profile" "iam_instance_profile" {
  name = "${var.environment}-${var.service_name}-instance-profile"
  role = aws_iam_role.instance_iam_role.name
}

# Write access to royalties sales files bucket
module "s3_read_write_policy" {
  source = "git@github.com:theorchard/terraform-iam-policy-templates.git//documents/s3/buckets_with_prefixes?ref=0.3.0"

  buckets = [
    { bucket = "${var.environment}-royalties-sales-files" },
  ]
  action_types = ["read", "write"]
}

resource "aws_iam_policy" "s3_royalties_sales_file_rw_policy" {
  name   = "S3-${var.environment}-royalties-sales-files-RW"
  policy = module.s3_read_write_policy.policy.json
}

# Create KMS policy
resource "aws_iam_policy" "kms_policy" {
  name   = "KMS-${var.environment}-${var.service_name}-key-usage-policy"
  policy = data.aws_iam_policy_document.kms_usage_policy.json
}

# Attach KMS policy to role
resource "aws_iam_role_policy_attachment" "kms_policy_attachment" {
  role       = aws_iam_role.instance_iam_role.name
  policy_arn = aws_iam_policy.kms_policy.arn
}

resource "aws_iam_role_policy_attachment" "shared_ami_kms_policy_attachment" {
  role       = aws_iam_role.instance_iam_role.name
  policy_arn = data.aws_iam_policy.shared_ami_kms_policy.arn
}

resource "aws_iam_role_policy_attachment" "s3_royalties_sales_file_rw_policy_attachment" {
  role       = aws_iam_role.instance_iam_role.name
  policy_arn = aws_iam_policy.s3_royalties_sales_file_rw_policy.arn
}

# Security group for instances
resource "aws_security_group" "instance_security_group" {
  name        = "${var.environment}-${var.service_name}-instance-security-group"
  description = "Instance security group for ${var.environment}-${var.service_name}"
  vpc_id      = module.vpc_info.vpc_id

  tags = {
    environment        = var.environment
    application_family = var.application_family
    service_name       = var.service_name
    map-migrated       = var.map_migrated_tag
    eiso-exception     = "aws.08.30"
    terraformed        = true
  }
}

resource "aws_security_group_rule" "allow_ssh" {
  type              = "ingress"
  from_port         = 22
  to_port           = 22
  protocol          = "TCP"
  cidr_blocks       = var.ssh_ingress_cidr_blocks
  security_group_id = aws_security_group.instance_security_group.id
}

resource "aws_security_group_rule" "allow_mysql" {
  type              = "ingress"
  from_port         = 3306
  to_port           = 3306
  protocol          = "TCP"
  cidr_blocks       = var.mysql_ingress_cidr_blocks
  security_group_id = aws_security_group.instance_security_group.id
}

resource "aws_security_group_rule" "allow_instance_egress" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.instance_security_group.id
}

resource "aws_security_group_rule" "allow_prefix_lists" {
  type              = "ingress"
  from_port         = 3306
  to_port           = 3306
  protocol          = "TCP"
  prefix_list_ids   = [
    data.aws_ec2_managed_prefix_list.orchard_prod_private_prefix_list.id,
    data.aws_ec2_managed_prefix_list.vpn_prefix_list.id,
  ]
  security_group_id = aws_security_group.instance_security_group.id
}

# Slaughterhouse access to statementdb
resource "aws_security_group_rule" "qa_slaughterhouse" {
  type                     = "ingress"
  from_port                = 3306
  to_port                  = 3306
  protocol                 = "TCP"
  source_security_group_id = data.aws_security_group.slaughterhouse_instance_security_group.id
  security_group_id        = aws_security_group.instance_security_group.id
}

# ECS extract-files task access to statementdb
resource "aws_security_group_rule" "qa_ecs_abacus_extract_sales_task" {
  type                     = "ingress"
  from_port                = 22
  to_port                  = 22
  protocol                 = "TCP"
  source_security_group_id = data.aws_security_group.ecs_abacus_extract_sales_task_security_group.id
  security_group_id        = aws_security_group.instance_security_group.id
}

# Assume role policy for Data Lifecycle Manager
data "aws_iam_policy_document" "assume_role_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type = "Service"

      identifiers = [
        "dlm.amazonaws.com",
      ]
    }
  }
}

resource "aws_route53_record" "statementdb_dns_record" {
  zone_id = data.aws_route53_zone.route53_zone.zone_id
  name    = "${var.environment}-statementdb"
  type    = "A"
  ttl     = "60"
  records = [aws_instance.mysql.private_ip]
}

# Data Lifecycle Manager role
resource "aws_iam_role" "data_lifecycle_manager_role" {
  name               = "${var.environment}-${var.service_name}-dlm-role"
  assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
}

data "aws_iam_policy_document" "data_lifecycle_manager_policy_document" {
  # checkov:skip=CKV_AWS_111:DLM does not work with ec2 resource constraints because some actions do not support them
  statement {
    actions = [
      "ec2:CreateSnapshot",
      "ec2:CreateSnapshots",
      "ec2:DeleteSnapshot",
      "ec2:DescribeVolumes",
      "ec2:DescribeInstances",
      "ec2:DescribeSnapshots",
    ]

    resources = [
      "*",
    ]
  }

  statement {
    actions = [
      "ec2:CreateTags",
    ]

    resources = [
      "arn:aws:ec2:*::snapshot/*",
    ]
  }
}

resource "aws_iam_policy" "data_lifecycle_manager_policy" {
  # checkov:skip=CKV_AWS_109:DLM does not work with ec2 resource constraints because some actions do not support them
  # checkov:skip=CKV_AWS_111:DLM does not work with ec2 resource constraints because some actions do not support them
  name   = "${var.environment}-${var.service_name}-ebs-lifecycle-policy"
  policy = data.aws_iam_policy_document.data_lifecycle_manager_policy_document.json
}

resource "aws_iam_role_policy_attachment" "data_lifecycle_manager_policy_attachment" {
  role       = aws_iam_role.data_lifecycle_manager_role.id
  policy_arn = aws_iam_policy.data_lifecycle_manager_policy.arn
}

resource "aws_dlm_lifecycle_policy" "data_lifecycle_policy" {
  description        = "${var.environment}-${var.service_name}-ebs-lifecycle-policy"
  execution_role_arn = aws_iam_role.data_lifecycle_manager_role.arn
  state              = "ENABLED"

  policy_details {
    resource_types = ["VOLUME"]

    schedule {
      name = "${var.environment}-${var.service_name}-ebs-backup-schedule"

      create_rule {
        interval      = 24
        interval_unit = "HOURS"
        times         = [var.time_to_evaluate_snapshot_schedule]
      }

      retain_rule {
        count = var.number_of_snapshots_to_retain
      }

      copy_tags = true
    }

    target_tags = {
      Name = "${var.environment}-mysql-${var.service_name}"
    }
  }

  tags = {
    Name               = "${var.environment}-mysql-${var.service_name}"
    environment        = var.environment
    application_family = var.application_family
    service_name       = var.service_name
    map-migrated       = var.map_migrated_tag
    terraformed        = true
  }
}

# Maximum gp3 settings; using the EBS resource allows
# in-place changes to be detected by Terraform
resource "aws_ebs_volume" "volume" {
  availability_zone = data.aws_subnet.instance_subnet.availability_zone
  encrypted         = true
  type              = "gp3"
  size              = 15000
  iops              = 3000
  kms_key_id        = aws_kms_key.kms_key.arn
  throughput        = 125

  tags = {
    Name               = "${var.environment}-mysql-${var.service_name}"
    environment        = var.environment
    application_family = var.application_family
    service_name       = var.service_name
    map-migrated       = var.map_migrated_tag
    terraformed        = true
  }
}

resource "aws_volume_attachment" "attachment" {
  device_name = "/dev/sdb"
  volume_id   = aws_ebs_volume.volume.id
  instance_id = aws_instance.mysql.id
}

# Get the latest PDE Debian 12 AMI id
data "aws_ami" "debian" {
  most_recent = true

  owners = ["086679231553"]

  filter {
    name   = "name"
    values = ["debian-12-amd64-*"]
  }

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }
}

resource "aws_instance" "mysql" {
  # checkov:skip=CKV_AWS_79:Debian AMI need metadata endpoint v1 enabled or SSH does not work.
  ami                                  = data.aws_ami.debian.id
  key_name                             = "${var.environment}-mysql-${var.service_name}01"
  monitoring                           = true
  subnet_id                            = data.aws_subnet.instance_subnet.id
  vpc_security_group_ids               = [aws_security_group.instance_security_group.id]
  associate_public_ip_address          = false
  iam_instance_profile                 = aws_iam_instance_profile.iam_instance_profile.name
  instance_type                        = "r5.2xlarge"
  ebs_optimized                        = true
  instance_initiated_shutdown_behavior = "stop"
  disable_api_termination              = true
  user_data                            = module.statementdb_bootstrap.user_data_output

  root_block_device {
    volume_type           = "gp3"
    volume_size           = 250
    delete_on_termination = false
    encrypted             = true
    kms_key_id            = aws_kms_key.kms_key.arn
  }

  volume_tags = merge(module.default_tags.tags, {
    Name = "${var.environment}-mysql-${var.service_name}",
    map-migrated = var.map_migrated_tag
  })

  tags = merge(module.default_tags.tags, {
    Name = "${var.environment}-mysql-${var.service_name}",
    map-migrated = var.map_migrated_tag
  })

  lifecycle {
    ignore_changes = [
      ami,
    ]
  }
}

module "statementdb_bootstrap" {
  source = "git@github.com:theorchard/terraform-chef-bootstrap.git?ref=3.1.2"

  chef_role         = "${var.environment}_mysql_statementdb"
  aws_instance_name = "${var.environment}-${var.service_name}"
  aws_iam_role_id   = aws_iam_role.instance_iam_role.id

  node_attributes = {
    datadog = {
      tags = {
        environment  = var.environment
        service_name = var.service_name
      }
    }
  }
}
