{
  "Comment": "Phase 2 (suspend): Processes approved suspend tickets using DynamoDB snapshots",
  "StartAt": "CheckForDirectTickets",
  "States": {
    "CheckForDirectTickets": {
      "Comment": "If input contains a 'tickets' array, skip the Jira query and process them directly.",
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.tickets[0]",
          "IsPresent": true,
          "Next": "PrepareDirectTickets"
        }
      ],
      "Default": "JiraQueryApprovedTickets"
    },
    "PrepareDirectTickets": {
      "Type": "Pass",
      "Parameters": {
        "tickets.$": "$.tickets"
      },
      "Next": "ForEachApprovedTicket"
    },
    "JiraQueryApprovedTickets": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "${lambda_jira_cli}",
        "Payload": {
          "action": "query-approved-suspend-tickets"
        }
      },
      "ResultSelector": {
        "tickets.$": "$.Payload.tickets"
      },
      "Retry": [
        {
          "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
          "IntervalSeconds": 5,
          "MaxAttempts": 3,
          "BackoffRate": 2
        }
      ],
      "Next": "ForEachApprovedTicket"
    },
    "ForEachApprovedTicket": {
      "Type": "Map",
      "MaxConcurrency": 1,
      "ItemsPath": "$.tickets",
      "ItemSelector": {
        "ticket_id.$": "$$.Map.Item.Value.id",
        "email.$": "$$.Map.Item.Value.email",
        "full_name.$": "$$.Map.Item.Value.full_name",
        "last_working_day.$": "$$.Map.Item.Value.last_working_day"
      },
      "Iterator": {
        "StartAt": "DynamoGetSnapshot",
        "States": {
          "DynamoGetSnapshot": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_dynamo_cli}",
              "Payload": {
                "action": "get-snapshot",
                "ticket_id.$": "$.ticket_id"
              }
            },
            "ResultSelector": {
              "found.$": "$.Payload.found",
              "snapshot.$": "$.Payload.snapshot"
            },
            "ResultPath": "$.dynamo",
            "Retry": [
              {
                "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                "IntervalSeconds": 5,
                "MaxAttempts": 3,
                "BackoffRate": 2
              }
            ],
            "Catch": [
              {
                "ErrorEquals": ["States.ALL"],
                "ResultPath": "$.error",
                "Next": "HandleExecutionError"
              }
            ],
            "Next": "CheckSnapshotFound"
          },
          "CheckSnapshotFound": {
            "Type": "Choice",
            "Choices": [
              {
                "Variable": "$.dynamo.found",
                "BooleanEquals": true,
                "Next": "Phase2ToolsInParallel"
              }
            ],
            "Default": "SnapshotNotFoundComment"
          },
          "SnapshotNotFoundComment": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "add-comment",
                "ticket_id.$": "$.ticket_id",
                "comment": "Execution skipped: no discovery snapshot found for this ticket. The discovery phase may not have run or the snapshot may have expired. Please re-run discovery or investigate manually."
              }
            },
            "ResultPath": null,
            "Retry": [
              {
                "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                "IntervalSeconds": 5,
                "MaxAttempts": 3,
                "BackoffRate": 2
              }
            ],
            "Catch": [
              {
                "ErrorEquals": ["States.ALL"],
                "ResultPath": null,
                "Next": "TicketSkipped"
              }
            ],
            "Next": "TicketSkipped"
          },
          "TicketSkipped": {
            "Type": "Succeed"
          },
          "Phase2ToolsInParallel": {
            "Comment": "Branch ordering matters: phase2_results[0] = Auth0, phase2_results[1] = GitHub. Uses snapshot data instead of re-searching.",
            "Type": "Parallel",
            "Branches": [
              {
                "StartAt": "HasAuth0MatchesFromSnapshot",
                "States": {
                  "HasAuth0MatchesFromSnapshot": {
                    "Type": "Choice",
                    "Choices": [
                      {
                        "Variable": "$.dynamo.snapshot.auth0_matches[0]",
                        "IsPresent": true,
                        "Next": "Auth0SuspendUsersForEachMatch"
                      }
                    ],
                    "Default": "Auth0AlreadyAbsent"
                  },
                  "Auth0SuspendUsersForEachMatch": {
                    "Type": "Map",
                    "MaxConcurrency": 1,
                    "ItemsPath": "$.dynamo.snapshot.auth0_matches",
                    "ItemSelector": {
                      "tenant.$": "$$.Map.Item.Value.tenant",
                      "user_id.$": "$$.Map.Item.Value.user_id",
                      "ticket_id.$": "$.ticket_id"
                    },
                    "Iterator": {
                      "StartAt": "Auth0SuspendUserById",
                      "States": {
                        "Auth0SuspendUserById": {
                          "Type": "Task",
                          "Resource": "arn:aws:states:::lambda:invoke",
                          "Parameters": {
                            "FunctionName": "${lambda_auth0_cli}",
                            "Payload": {
                              "action": "suspend-user-by-id",
                              "tenant.$": "$.tenant",
                              "user_id.$": "$.user_id",
                              "ticket_id.$": "$.ticket_id"
                            }
                          },
                          "ResultSelector": {
                            "tenant.$": "$.Payload.tenant",
                            "user_id.$": "$.Payload.user_id"
                          },
                          "Retry": [
                            {
                              "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                              "IntervalSeconds": 5,
                              "MaxAttempts": 3,
                              "BackoffRate": 2
                            }
                          ],
                          "Catch": [
                            {
                              "ErrorEquals": ["States.ALL"],
                              "ResultPath": "$.error",
                              "Next": "Auth0SuspendFailed"
                            }
                          ],
                          "End": true
                        },
                        "Auth0SuspendFailed": {
                          "Type": "Pass",
                          "Parameters": {
                            "tenant.$": "$.tenant",
                            "user_id.$": "$.user_id",
                            "error.$": "$.error"
                          },
                          "End": true
                        }
                      }
                    },
                    "ResultPath": "$.suspension_results",
                    "Next": "Auth0SuspendedComment"
                  },
                  "Auth0SuspendedComment": {
                    "Type": "Task",
                    "Resource": "arn:aws:states:::lambda:invoke",
                    "Parameters": {
                      "FunctionName": "${lambda_jira_cli}",
                      "Payload": {
                        "action": "add-comment",
                        "ticket_id.$": "$.ticket_id",
                        "comment.$": "States.Format('Auth0: user suspension complete. Results: {}', States.JsonToString($.suspension_results))"
                      }
                    },
                    "ResultPath": null,
                    "Retry": [
                      {
                        "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                        "IntervalSeconds": 5,
                        "MaxAttempts": 3,
                        "BackoffRate": 2
                      }
                    ],
                    "Catch": [
                      {
                        "ErrorEquals": ["States.ALL"],
                        "ResultPath": null,
                        "Next": "Auth0BranchDone"
                      }
                    ],
                    "End": true
                  },
                  "Auth0AlreadyAbsent": {
                    "Type": "Task",
                    "Resource": "arn:aws:states:::lambda:invoke",
                    "Parameters": {
                      "FunctionName": "${lambda_jira_cli}",
                      "Payload": {
                        "action": "add-comment",
                        "ticket_id.$": "$.ticket_id",
                        "comment": "Auth0: no matches found in discovery snapshot. No suspensions required."
                      }
                    },
                    "ResultPath": null,
                    "Retry": [
                      {
                        "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                        "IntervalSeconds": 5,
                        "MaxAttempts": 3,
                        "BackoffRate": 2
                      }
                    ],
                    "Catch": [
                      {
                        "ErrorEquals": ["States.ALL"],
                        "ResultPath": null,
                        "Next": "Auth0BranchDone"
                      }
                    ],
                    "Next": "Auth0BranchDone"
                  },
                  "Auth0BranchDone": {
                    "Type": "Pass",
                    "Result": "done",
                    "End": true
                  }
                }
              },
              {
                "StartAt": "HasGitHubHitsFromSnapshot",
                "States": {
                  "HasGitHubHitsFromSnapshot": {
                    "Type": "Choice",
                    "Choices": [
                      {
                        "Variable": "$.dynamo.snapshot.terraform_hits[0]",
                        "IsPresent": true,
                        "Next": "GitHubSuspendCleanupUser"
                      }
                    ],
                    "Default": "GitHubNoHitsPhase2"
                  },
                  "GitHubSuspendCleanupUser": {
                    "Type": "Task",
                    "Resource": "arn:aws:states:::lambda:invoke",
                    "Parameters": {
                      "FunctionName": "${lambda_github_cli}",
                      "Payload": {
                        "action": "offboard-user-with-copilot",
                        "operation": "suspend",
                        "org": "theorchard",
                        "base_branch": "master",
                        "email.$": "$.dynamo.snapshot.email",
                        "full_name.$": "$.dynamo.snapshot.full_name",
                        "last_working_day.$": "$.dynamo.snapshot.last_working_day",
                        "ticket_id.$": "$.ticket_id",
                        "terraform_hits.$": "$.dynamo.snapshot.terraform_hits"
                      }
                    },
                    "ResultSelector": {
                      "ticket_id.$": "$.Payload.ticket_id",
                      "issue_urls.$": "$.Payload.issue_urls",
                      "issue_numbers.$": "$.Payload.issue_numbers"
                    },
                    "Retry": [
                      {
                        "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                        "IntervalSeconds": 5,
                        "MaxAttempts": 3,
                        "BackoffRate": 2
                      }
                    ],
                    "Catch": [
                      {
                        "ErrorEquals": ["States.ALL"],
                        "ResultPath": "$.error",
                        "Next": "GitHubSuspendCleanupFailed"
                      }
                    ],
                    "Next": "GitHubIssueCreatedComment"
                  },
                  "GitHubIssueCreatedComment": {
                    "Type": "Task",
                    "Resource": "arn:aws:states:::lambda:invoke",
                    "Parameters": {
                      "FunctionName": "${lambda_jira_cli}",
                      "Payload": {
                        "action": "add-comment",
                        "ticket_id.$": "$.ticket_id",
                        "comment.$": "States.Format('GitHub: suspension cleanup issue(s) created: {}', States.JsonToString($.issue_urls))"
                      }
                    },
                    "ResultPath": null,
                    "Retry": [
                      {
                        "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                        "IntervalSeconds": 5,
                        "MaxAttempts": 3,
                        "BackoffRate": 2
                      }
                    ],
                    "Catch": [
                      {
                        "ErrorEquals": ["States.ALL"],
                        "ResultPath": null,
                        "Next": "GitHubBranchDone"
                      }
                    ],
                    "End": true
                  },
                  "GitHubNoHitsPhase2": {
                    "Type": "Pass",
                    "Result": null,
                    "End": true
                  },
                  "GitHubSuspendCleanupFailed": {
                    "Type": "Task",
                    "Resource": "arn:aws:states:::lambda:invoke",
                    "Parameters": {
                      "FunctionName": "${lambda_jira_cli}",
                      "Payload": {
                        "action": "add-comment",
                        "ticket_id.$": "$.ticket_id",
                        "comment.$": "States.Format('GitHub suspension cleanup error: {}', States.JsonToString($.error))"
                      }
                    },
                    "ResultPath": null,
                    "Retry": [
                      {
                        "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                        "IntervalSeconds": 5,
                        "MaxAttempts": 3,
                        "BackoffRate": 2
                      }
                    ],
                    "Next": "GitHubBranchDone"
                  },
                  "GitHubBranchDone": {
                    "Type": "Pass",
                    "Result": "done-with-errors",
                    "End": true
                  }
                }
              }
            ],
            "ResultPath": "$.phase2_results",
            "Catch": [
              {
                "ErrorEquals": ["States.ALL"],
                "ResultPath": "$.error",
                "Next": "HandleExecutionError"
              }
            ],
            "Next": "ActionsTaken_AddLabelAutomationComplete"
          },
          "ActionsTaken_AddLabelAutomationComplete": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "add-label",
                "ticket_id.$": "$.ticket_id",
                "label": "suspension-complete"
              }
            },
            "ResultPath": null,
            "Retry": [
              {
                "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                "IntervalSeconds": 5,
                "MaxAttempts": 3,
                "BackoffRate": 2
              }
            ],
            "Catch": [
              {
                "ErrorEquals": ["States.ALL"],
                "ResultPath": "$.error",
                "Next": "HandleExecutionError"
              }
            ],
            "Next": "ActionsTaken_AddComment"
          },
          "ActionsTaken_AddComment": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "add-comment",
                "ticket_id.$": "$.ticket_id",
                "comment.$": "States.Format('Suspension automation complete (Phase 2). Auth0 result: {}. GitHub result: {}.', States.JsonToString($.phase2_results[0]), States.JsonToString($.phase2_results[1]))"
              }
            },
            "ResultPath": null,
            "Retry": [
              {
                "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                "IntervalSeconds": 5,
                "MaxAttempts": 3,
                "BackoffRate": 2
              }
            ],
            "Catch": [
              {
                "ErrorEquals": ["States.ALL"],
                "ResultPath": null,
                "Next": "DynamoDeleteSnapshot"
              }
            ],
            "Next": "DynamoDeleteSnapshot"
          },
          "DynamoDeleteSnapshot": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_dynamo_cli}",
              "Payload": {
                "action": "delete-snapshot",
                "ticket_id.$": "$.ticket_id"
              }
            },
            "ResultPath": null,
            "Retry": [
              {
                "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                "IntervalSeconds": 5,
                "MaxAttempts": 3,
                "BackoffRate": 2
              }
            ],
            "Catch": [
              {
                "ErrorEquals": ["States.ALL"],
                "ResultPath": null,
                "Next": "ExecutionComplete"
              }
            ],
            "Next": "ExecutionComplete"
          },
          "ExecutionComplete": {
            "Type": "Succeed"
          },
          "HandleExecutionError": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "add-comment",
                "ticket_id.$": "$.ticket_id",
                "comment.$": "States.Format('Execution automation error: {}. Manual investigation required.', States.JsonToString($.error))"
              }
            },
            "ResultPath": null,
            "Retry": [
              {
                "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                "IntervalSeconds": 5,
                "MaxAttempts": 3,
                "BackoffRate": 2
              }
            ],
            "Next": "ExecutionFailed"
          },
          "ExecutionFailed": {
            "Type": "Fail",
            "Error": "ExecutionError",
            "Cause": "An error occurred during the execution phase for this ticket. See Jira comment for details."
          }
        }
      },
      "ResultPath": null,
      "End": true
    }
  }
}
