resource "aws_iam_openid_connect_provider" "github" {
  url             = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]

}

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

locals {
  github_repository              = "${var.github_org}/${var.github_repo}"
  github_subject_main            = "repo:${local.github_repository}:ref:refs/heads/main"
  github_subject_production      = "repo:${local.github_repository}:environment:production"
  github_main_ref                = "refs/heads/main"
  image_workflow_ref             = "${local.github_repository}/.github/workflows/image-release.yml@refs/heads/main"
  cli_workflow_name              = "Build & Publish CLI"
  plugin_workflow_name           = "Build & Publish Claude Code Plugin"
  dax_feishu_workflow_name       = "Deploy DAX Feishu Approval"
  dax_bigquery_workflow_name     = "Deploy DAX BigQuery Integration"
  dax_catalog_workflow_name      = "Seed DAX Dataset Catalog"
  production_deploy_workflow_ref = "${local.github_repository}/.github/workflows/deploy-ec2.yml@refs/heads/main"
  parameter_prefix_arn           = "arn:${data.aws_partition.current.partition}:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:parameter/${var.project}/${var.environment}"
  run_shell_document_arn         = "arn:${data.aws_partition.current.partition}:ssm:${data.aws_region.current.name}::document/AWS-RunShellScript"
}

data "aws_iam_policy_document" "trust_image_main" {
  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]
    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.github.arn]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:aud"
      values   = ["sts.amazonaws.com"]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:sub"
      values   = [local.github_subject_main]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:repository"
      values   = [local.github_repository]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:job_workflow_ref"
      values   = [local.image_workflow_ref]
    }
  }
}

data "aws_iam_policy_document" "trust_artifact_main" {
  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]
    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.github.arn]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:aud"
      values   = ["sts.amazonaws.com"]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:sub"
      values   = [local.github_subject_main]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:repository"
      values   = [local.github_repository]
    }
    condition {
      test = "StringEquals"
      # AWS exposes the direct workflow's display name as `workflow`; it does
      # not map GitHub's `workflow_ref` claim into an IAM condition key.
      variable = "token.actions.githubusercontent.com:workflow"
      values = [
        local.cli_workflow_name,
        local.plugin_workflow_name,
      ]
    }
  }
}

data "aws_iam_policy_document" "trust_dax_production" {
  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]
    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.github.arn]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:aud"
      values   = ["sts.amazonaws.com"]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:sub"
      values   = [local.github_subject_production]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:repository"
      values   = [local.github_repository]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:ref"
      values   = [local.github_main_ref]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:workflow"
      values = [
        local.dax_feishu_workflow_name,
        local.dax_bigquery_workflow_name,
        local.dax_catalog_workflow_name,
      ]
    }
  }
}

data "aws_iam_policy_document" "trust_production_deploy" {
  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]
    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.github.arn]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:aud"
      values   = ["sts.amazonaws.com"]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:sub"
      values   = [local.github_subject_production]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:repository"
      values   = [local.github_repository]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:ref"
      values   = [local.github_main_ref]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:job_workflow_ref"
      values   = [local.production_deploy_workflow_ref]
    }
  }
}

# Public frontend build flags live in SSM today. Keep their two-name read in
# its own main-only role so the ECR publisher itself remains ECR-only.
resource "aws_iam_role" "frontend_config" {
  name               = "${var.project}-${var.environment}-github-frontend-config"
  assume_role_policy = data.aws_iam_policy_document.trust_image_main.json
  tags               = { Project = var.project, Environment = var.environment }
}

data "aws_iam_policy_document" "frontend_config_read" {
  statement {
    actions = ["ssm:GetParameter"]
    resources = [
      "${local.parameter_prefix_arn}/GOOGLE_CLIENT_ID",
      "${local.parameter_prefix_arn}/AUTH_PASSWORD_LOGIN_ENABLED",
    ]
  }
}

resource "aws_iam_role_policy" "frontend_config_read" {
  name   = "frontend-public-config-read"
  role   = aws_iam_role.frontend_config.id
  policy = data.aws_iam_policy_document.frontend_config_read.json
}

resource "aws_iam_role" "image_publish" {
  name               = "${var.project}-${var.environment}-github-image-publish"
  assume_role_policy = data.aws_iam_policy_document.trust_image_main.json
  tags               = { Project = var.project, Environment = var.environment }
}

data "aws_iam_policy_document" "image_publish" {
  statement {
    actions   = ["ecr:GetAuthorizationToken"]
    resources = ["*"]
  }
  statement {
    actions = [
      "ecr:BatchCheckLayerAvailability",
      "ecr:BatchGetImage",
      # buildx imagetools retags the server manifest into the worker
      # repository and must read the source layers during the registry-side
      # copy. Keep this read scoped to the configured Helix repositories.
      "ecr:GetDownloadUrlForLayer",
      "ecr:PutImage",
      "ecr:InitiateLayerUpload",
      "ecr:UploadLayerPart",
      "ecr:CompleteLayerUpload",
    ]
    resources = var.ecr_arns
  }
}

resource "aws_iam_role_policy" "image_publish" {
  name   = "ecr-push"
  role   = aws_iam_role.image_publish.id
  policy = data.aws_iam_policy_document.image_publish.json
}

resource "aws_iam_role" "artifact_publish" {
  name               = "${var.project}-${var.environment}-github-artifact-publish"
  assume_role_policy = data.aws_iam_policy_document.trust_artifact_main.json
  tags               = { Project = var.project, Environment = var.environment }
}

data "aws_iam_policy_document" "artifact_publish" {
  count = var.cli_dist_bucket_arn == "" ? 0 : 1
  statement {
    actions = ["s3:PutObject"]
    resources = [
      "${var.cli_dist_bucket_arn}/cli-dist/*",
      "${var.cli_dist_bucket_arn}/plugin-dist/*",
    ]
  }
  statement {
    actions   = ["s3:GetObject"]
    resources = ["${var.cli_dist_bucket_arn}/plugin-dist/latest/manifest.json"]
  }
}

resource "aws_iam_role_policy" "artifact_publish" {
  count  = var.cli_dist_bucket_arn == "" ? 0 : 1
  name   = "artifact-prefix-publish"
  role   = aws_iam_role.artifact_publish.id
  policy = data.aws_iam_policy_document.artifact_publish[0].json
}

# DAX activations are production-environment gated but do not get the general
# deployment role. They may stage only DAX helpers, touch the exact DAX/Feishu
# parameters, and execute the fixed SSM document on the one application host.
resource "aws_iam_role" "dax_deploy" {
  name               = "${var.project}-${var.environment}-github-dax-deploy"
  assume_role_policy = data.aws_iam_policy_document.trust_dax_production.json
  tags               = { Project = var.project, Environment = var.environment }
}

data "aws_iam_policy_document" "dax_deploy" {
  statement {
    actions = ["ssm:GetParameter"]
    resources = [
      "${local.parameter_prefix_arn}/FEISHU_APP_ID",
      "${local.parameter_prefix_arn}/FEISHU_APP_SECRET",
    ]
  }
  statement {
    actions = ["ssm:PutParameter"]
    resources = [
      "${local.parameter_prefix_arn}/FEISHU_APPROVAL_DATA_ACCESS_GROUP_GRANT_CODE",
      "${local.parameter_prefix_arn}/FEISHU_APPROVAL_DATA_ACCESS_SERVICE_ACCOUNT_GRANT_CODE",
      "${local.parameter_prefix_arn}/FEISHU_APPROVAL_DATA_ACCESS_PERSONAL_GRANT_CODE",
    ]
  }
  statement {
    actions = ["s3:PutObject"]
    resources = [
      "${var.state_bucket_arn}/deploy-assets/dax-*",
      "${var.state_bucket_arn}/deploy-assets/dax-*/*",
    ]
  }
  statement {
    actions   = ["ssm:SendCommand"]
    resources = [var.deploy_instance_arn, local.run_shell_document_arn]
  }
  statement {
    actions = ["ssm:GetCommandInvocation"]
    # AWS does not define a resource type for GetCommandInvocation; the
    # Service Authorization Reference requires "*" for this read action.
    resources = ["*"]
  }
}

resource "aws_iam_role_policy" "dax_deploy" {
  name   = "dax-production-activation"
  role   = aws_iam_role.dax_deploy.id
  policy = data.aws_iam_policy_document.dax_deploy.json
}

resource "aws_iam_role" "production_deploy" {
  name               = "${var.project}-${var.environment}-github-production-deploy"
  assume_role_policy = data.aws_iam_policy_document.trust_production_deploy.json
  tags               = { Project = var.project, Environment = var.environment }
}

data "aws_iam_policy_document" "production_deploy" {
  statement {
    actions = ["s3:PutObject", "s3:DeleteObject"]
    resources = [
      "${var.state_bucket_arn}/deploy-assets/*",
    ]
  }
  statement {
    actions   = ["s3:ListBucket"]
    resources = [var.state_bucket_arn]
    condition {
      test     = "StringLike"
      variable = "s3:prefix"
      values   = ["deploy-assets/*"]
    }
  }
  statement {
    actions   = ["ssm:SendCommand"]
    resources = [var.deploy_instance_arn, local.run_shell_document_arn]
  }
  statement {
    actions = ["ssm:GetCommandInvocation"]
    # AWS does not define a resource type for GetCommandInvocation.
    resources = ["*"]
  }
}

resource "aws_iam_role_policy" "production_deploy" {
  name   = "production-deploy"
  role   = aws_iam_role.production_deploy.id
  policy = data.aws_iam_policy_document.production_deploy.json
}
