resource "aws_iam_role" "instance" {
  name_prefix = "${var.name_prefix}-"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = {
        Service = "ec2.amazonaws.com"
      }
      Action = "sts:AssumeRole"
    }]
  })

  tags = merge(local.deployment_tags, {
    Name = "${var.name_prefix}-instance"
  })
}

resource "aws_iam_role_policy_attachment" "ssm" {
  role       = aws_iam_role.instance.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_role_policy" "bootstrap_status" {
  name = "bootstrap-status"
  role = aws_iam_role.instance.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Sid      = "TagBootstrapStatus"
      Effect   = "Allow"
      Action   = "ec2:CreateTags"
      Resource = "arn:aws:ec2:${var.aws_region}:${var.target_account_id}:instance/*"
      Condition = {
        StringEquals = {
          "ec2:ResourceTag/Project" = var.name_prefix
        }
      }
    }]
  })
}

resource "aws_iam_instance_profile" "instance" {
  name_prefix = "${var.name_prefix}-"
  role        = aws_iam_role.instance.name
  tags        = local.deployment_tags
}

resource "aws_eip" "public" {
  domain = "vpc"

  tags = merge(local.deployment_tags, {
    Name = "${var.name_prefix}-public"
  })
}

resource "aws_ebs_volume" "data" {
  availability_zone = local.primary_az
  encrypted         = true
  size              = var.data_volume_size_gib
  type              = "gp3"

  tags = merge(local.deployment_tags, {
    Name = "${var.name_prefix}-data"
  })
}

locals {
  secrets_env = <<-EOT
    MYSQL_ROOT_PASSWORD=${local.mysql_root_password}
    MYSQL_USER=${var.mysql_user}
    MYSQL_PASSWORD=${local.mysql_password}
    KAFKA_SASL_USERNAME=${local.kafka_sasl_username}
    KAFKA_SASL_PASSWORD=${local.kafka_sasl_password}
    PULSAR_PROXY_TOKEN=${data.external.pulsar_proxy_token.result.token}
    PULSAR_ADMIN_TOKEN=${data.external.pulsar_admin_token.result.token}
    PULSAR_CLIENT_TOKEN=${data.external.pulsar_client_token.result.token}
    SCHEMA_REGISTRY_USERNAME=${local.schema_registry_username}
    SCHEMA_REGISTRY_PASSWORD=${local.schema_registry_password}
  EOT

  compose_yaml = templatefile("${path.module}/../../templates/compose.yaml.tftpl", {
    mysql_image                       = local.images.mysql
    kafka_image                       = local.images.kafka
    pulsar_image                      = local.images.pulsar
    kafka_proxy_image                 = local.images.kafka_proxy
    schema_registry_image             = local.images.schema_registry
    mysql_port                        = local.ports.mysql
    kafka_public_port                 = local.ports.kafka_public
    kafka_sasl_port                   = local.ports.kafka_sasl
    kafka_pl_public_port              = local.ports.kafka_pl_public
    kafka_pl_sasl_port                = local.ports.kafka_pl_sasl
    pulsar_binary_port                = local.ports.pulsar_binary
    pulsar_admin_http_port            = local.ports.pulsar_admin_http
    schema_registry_port              = local.ports.schema_registry
    public_ip                         = aws_eip.public.public_ip
    kafka_privatelink_advertised_host = local.kafka_privatelink_advertised_host
    kafka_cluster_id                  = local.kafka_cluster_id
    enable_pulsar                     = var.enable_pulsar
    enable_schema_registry            = var.enable_schema_registry
  })

  mysql_config = templatefile("${path.module}/../../templates/mysql.cnf.tftpl", {})
  mysql_init = templatefile("${path.module}/../../templates/mysql-init.sql.tftpl", {
    mysql_user = var.mysql_user
  })
  service_unit = templatefile("${path.module}/../../templates/date-integration.service.tftpl", {})
  seed_script = templatefile("${path.module}/../../templates/seed-services.sh.tftpl", {
    mysql_user             = var.mysql_user
    pulsar_binary_port     = local.ports.pulsar_binary
    pulsar_admin_http_port = local.ports.pulsar_admin_http
    schema_registry_port   = local.ports.schema_registry
    enable_pulsar          = var.enable_pulsar
    enable_schema_registry = var.enable_schema_registry
  })

  schema_registry_jaas  = <<-EOT
    SchemaRegistry-Props {
      org.eclipse.jetty.jaas.spi.PropertyFileLoginModule required
      file="/etc/schema-registry/schema-registry-users.properties";
    };
  EOT
  schema_registry_users = "${local.schema_registry_username}: MD5:${md5(local.schema_registry_password)},user\n"

  install_services = templatefile("${path.module}/../../templates/install-services.sh.tftpl", {
    compose_b64               = base64encode(local.compose_yaml)
    mysql_config_b64          = base64encode(local.mysql_config)
    mysql_init_b64            = base64encode(local.mysql_init)
    pulsar_secret_b64         = local.pulsar_token_secret
    secrets_env_b64           = base64encode(local.secrets_env)
    service_unit_b64          = base64encode(local.service_unit)
    seed_script_b64           = base64encode(local.seed_script)
    schema_registry_jaas_b64  = base64encode(local.schema_registry_jaas)
    schema_registry_users_b64 = base64encode(local.schema_registry_users)
    mysql_credentials_hash    = sha256("${local.mysql_root_password}:${local.mysql_password}")
    pulsar_credentials_hash   = sha256("${local.pulsar_token_secret}:stable-network-v1")
  })

  user_data = templatefile("${path.module}/../../templates/user-data.sh.tftpl", {
    aws_region           = var.aws_region
    data_volume_id       = aws_ebs_volume.data.id
    compose_version      = "v2.39.4"
    compose_sha256       = "49082844b87f03cdcd5f5bbef1ba8c9c897b7a2dfb80cea18d61ec8ca6117e0c"
    install_services_b64 = base64encode(local.install_services)
  })
}

resource "terraform_data" "bootstrap_revision" {
  input = sha256(local.user_data)
}

resource "aws_instance" "this" {
  ami                         = local.ami_id
  instance_type               = var.instance_type
  subnet_id                   = aws_subnet.public["0"].id
  associate_public_ip_address = true
  iam_instance_profile        = aws_iam_instance_profile.instance.name
  vpc_security_group_ids      = [aws_security_group.instance.id]

  user_data_base64            = base64gzip(local.user_data)
  user_data_replace_on_change = true

  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = "required"
    http_put_response_hop_limit = 1
    instance_metadata_tags      = "disabled"
  }

  root_block_device {
    encrypted             = true
    delete_on_termination = true
    volume_size           = var.root_volume_size_gib
    volume_type           = "gp3"
    tags = merge(local.common_tags, {
      Name = "${var.name_prefix}-root"
    })
  }

  tags = merge(local.deployment_tags, {
    Name = "${var.name_prefix}-services"
  })

  depends_on = [
    aws_iam_role_policy.bootstrap_status,
    aws_iam_role_policy_attachment.ssm,
  ]

  lifecycle {
    ignore_changes       = [tags["BootstrapStatus"]]
    replace_triggered_by = [terraform_data.bootstrap_revision]

    precondition {
      condition     = data.aws_caller_identity.current.account_id == var.target_account_id
      error_message = "The active AWS identity does not match target_account_id."
    }

    precondition {
      condition     = contains(data.aws_ec2_instance_type.services.supported_architectures, "arm64")
      error_message = "instance_type must support arm64 because the stack uses an ARM64 AMI and bootstrap binary."
    }
  }
}

resource "aws_volume_attachment" "data" {
  device_name                    = "/dev/sdf"
  volume_id                      = aws_ebs_volume.data.id
  instance_id                    = aws_instance.this.id
  stop_instance_before_detaching = true
}

resource "aws_eip_association" "public" {
  allocation_id = aws_eip.public.id
  instance_id   = aws_instance.this.id
}

resource "terraform_data" "wait_for_bootstrap" {
  triggers_replace = [aws_instance.this.id]

  provisioner "local-exec" {
    command     = "${path.module}/../../scripts/wait-for-bootstrap.sh"
    interpreter = ["/usr/bin/env", "bash", "-c"]
    environment = {
      AWS_PROFILE       = var.aws_profile
      AWS_REGION        = var.aws_region
      INSTANCE_ID       = aws_instance.this.id
      BOOTSTRAP_TIMEOUT = "1800"
    }
  }

  depends_on = [
    aws_eip_association.public,
    aws_volume_attachment.data,
  ]
}

resource "aws_ec2_instance_state" "this" {
  instance_id = aws_instance.this.id
  state       = var.desired_state

  depends_on = [terraform_data.wait_for_bootstrap]
}

resource "terraform_data" "wait_after_state_change" {
  triggers_replace = [var.desired_state, aws_instance.this.id]

  provisioner "local-exec" {
    command     = var.desired_state == "running" ? "${path.module}/../../scripts/wait-for-aws-services.sh" : "true"
    interpreter = ["/usr/bin/env", "bash", "-c"]
    environment = {
      AWS_PROFILE       = var.aws_profile
      AWS_REGION        = var.aws_region
      INSTANCE_ID       = aws_instance.this.id
      BOOTSTRAP_TIMEOUT = "1800"
    }
  }

  depends_on = [aws_ec2_instance_state.this]
}
