{
  "Comment": "Phase 1: Discovers offboarding candidates and stores snapshots in DynamoDB",
  "StartAt": "JiraQueryOffboardingTickets",
  "States": {
    "JiraQueryOffboardingTickets": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "${lambda_jira_cli}",
        "Payload": {
          "action": "query-offboarding-tickets"
        }
      },
      "ResultSelector": {
        "tickets.$": "$.Payload.tickets"
      },
      "Retry": [
        {
          "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
          "IntervalSeconds": 5,
          "MaxAttempts": 3,
          "BackoffRate": 2
        }
      ],
      "Next": "ForEachNewTicket"
    },
    "ForEachNewTicket": {
      "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": "JiraValidateTicket",
        "States": {
          "JiraValidateTicket": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "validate-ticket",
                "ticket_id.$": "$.ticket_id"
              }
            },
            "ResultSelector": {
              "ticket_id.$": "$.Payload.ticket_id",
              "valid.$": "$.Payload.valid",
              "warnings.$": "$.Payload.warnings"
            },
            "ResultPath": "$.validation",
            "Retry": [
              {
                "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                "IntervalSeconds": 5,
                "MaxAttempts": 3,
                "BackoffRate": 2
              }
            ],
            "Catch": [
              {
                "ErrorEquals": ["States.ALL"],
                "ResultPath": "$.error",
                "Next": "HandleDiscoveryError"
              }
            ],
            "Next": "HasValidationWarnings"
          },
          "HasValidationWarnings": {
            "Type": "Choice",
            "Choices": [
              {
                "Variable": "$.validation.warnings[0]",
                "IsPresent": true,
                "Next": "JiraValidationWarningComment"
              }
            ],
            "Default": "JiraAddDueDate"
          },
          "JiraValidationWarningComment": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "add-comment",
                "ticket_id.$": "$.ticket_id",
                "comment.$": "States.Format('Warning: ticket source could not be fully verified. {}', States.JsonToString($.validation.warnings))"
              }
            },
            "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": "JiraAddDueDate"
              }
            ],
            "Next": "JiraAddDueDate"
          },
          "JiraAddDueDate": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "add-due-date",
                "ticket_id.$": "$.ticket_id",
                "due_date.$": "$.last_working_day"
              }
            },
            "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": "HandleDiscoveryError"
              }
            ],
            "Next": "Phase1ToolsInParallel"
          },
          "Phase1ToolsInParallel": {
            "Comment": "Branch ordering matters: phase1_results[0] = Auth0, phase1_results[1] = GitHub. Do not reorder without updating RequiresApproval and DynamoPutSnapshot.",
            "Type": "Parallel",
            "Branches": [
              {
                "StartAt": "Auth0SearchUserByEmail",
                "States": {
                  "Auth0SearchUserByEmail": {
                    "Type": "Task",
                    "Resource": "arn:aws:states:::lambda:invoke",
                    "Parameters": {
                      "FunctionName": "${lambda_auth0_cli}",
                      "Payload": {
                        "action": "search-user-by-email",
                        "email.$": "$.email",
                        "ticket_id.$": "$.ticket_id"
                      }
                    },
                    "ResultSelector": {
                      "email.$": "$.Payload.email",
                      "ticket_id.$": "$.Payload.ticket_id",
                      "matches.$": "$.Payload.matches"
                    },
                    "Retry": [
                      {
                        "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                        "IntervalSeconds": 5,
                        "MaxAttempts": 3,
                        "BackoffRate": 2
                      }
                    ],
                    "Catch": [
                      {
                        "ErrorEquals": ["States.ALL"],
                        "ResultPath": "$.error",
                        "Next": "Auth0SearchFailed"
                      }
                    ],
                    "Next": "HasAuth0Matches"
                  },
                  "HasAuth0Matches": {
                    "Type": "Choice",
                    "Choices": [
                      {
                        "Variable": "$.matches[0]",
                        "IsPresent": true,
                        "Next": "Auth0FoundComment"
                      }
                    ],
                    "Default": "Auth0NotFoundComment"
                  },
                  "Auth0FoundComment": {
                    "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 found in the following tenant(s): {}. Pending human approval before deletion.', States.JsonToString($.matches))"
                      }
                    },
                    "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": "Auth0ReturnMatches"
                      }
                    ],
                    "Next": "Auth0ReturnMatches"
                  },
                  "Auth0ReturnMatches": {
                    "Type": "Pass",
                    "Parameters": {
                      "has_matches": true,
                      "matches.$": "$.matches"
                    },
                    "End": true
                  },
                  "Auth0NotFoundComment": {
                    "Type": "Task",
                    "Resource": "arn:aws:states:::lambda:invoke",
                    "Parameters": {
                      "FunctionName": "${lambda_jira_cli}",
                      "Payload": {
                        "action": "add-comment",
                        "ticket_id.$": "$.ticket_id",
                        "comment": "Auth0: user not found in any tenant."
                      }
                    },
                    "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": "Auth0ReturnNoMatches"
                      }
                    ],
                    "Next": "Auth0ReturnNoMatches"
                  },
                  "Auth0ReturnNoMatches": {
                    "Type": "Pass",
                    "Result": {
                      "has_matches": false,
                      "matches": []
                    },
                    "End": true
                  },
                  "Auth0SearchFailed": {
                    "Type": "Pass",
                    "Result": {
                      "has_matches": false,
                      "matches": [],
                      "error": true
                    },
                    "End": true
                  }
                }
              },
              {
                "StartAt": "GitHubSearchUser",
                "States": {
                  "GitHubSearchUser": {
                    "Type": "Task",
                    "Resource": "arn:aws:states:::lambda:invoke",
                    "Parameters": {
                      "FunctionName": "${lambda_github_cli}",
                      "Payload": {
                        "action": "search-text-in-org",
                        "org": "theorchard",
                        "email.$": "$.email",
                        "full_name.$": "$.full_name",
                        "ticket_id.$": "$.ticket_id"
                      }
                    },
                    "ResultSelector": {
                      "ticket_id.$": "$.Payload.ticket_id",
                      "email.$": "$.Payload.email",
                      "full_name.$": "$.Payload.full_name",
                      "terraform_hits.$": "$.Payload.terraform_hits"
                    },
                    "ResultPath": "$.github_search",
                    "Retry": [
                      {
                        "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"],
                        "IntervalSeconds": 5,
                        "MaxAttempts": 3,
                        "BackoffRate": 2
                      }
                    ],
                    "Catch": [
                      {
                        "ErrorEquals": ["States.ALL"],
                        "ResultPath": "$.error",
                        "Next": "GitHubSearchFailed"
                      }
                    ],
                    "Next": "HasGitHubHits"
                  },
                  "HasGitHubHits": {
                    "Type": "Choice",
                    "Choices": [
                      {
                        "Variable": "$.github_search.terraform_hits[0]",
                        "IsPresent": true,
                        "Next": "GitHubSearchResultsComment"
                      }
                    ],
                    "Default": "GitHubNoResultsComment"
                  },
                  "GitHubSearchResultsComment": {
                    "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: Terraform references found: {}. Offboarding will run after approval.', States.JsonToString($.github_search.terraform_hits))"
                      }
                    },
                    "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": "GitHubReturnHits"
                      }
                    ],
                    "Next": "GitHubReturnHits"
                  },
                  "GitHubReturnHits": {
                    "Type": "Pass",
                    "Parameters": {
                      "has_hits": true,
                      "terraform_hits.$": "$.github_search.terraform_hits"
                    },
                    "End": true
                  },
                  "GitHubNoResultsComment": {
                    "Type": "Task",
                    "Resource": "arn:aws:states:::lambda:invoke",
                    "Parameters": {
                      "FunctionName": "${lambda_jira_cli}",
                      "Payload": {
                        "action": "add-comment",
                        "ticket_id.$": "$.ticket_id",
                        "comment": "GitHub: no Terraform references found."
                      }
                    },
                    "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": "GitHubReturnNoHits"
                      }
                    ],
                    "Next": "GitHubReturnNoHits"
                  },
                  "GitHubReturnNoHits": {
                    "Type": "Pass",
                    "Result": {
                      "has_hits": false,
                      "terraform_hits": []
                    },
                    "End": true
                  },
                  "GitHubSearchFailed": {
                    "Type": "Pass",
                    "Result": {
                      "has_hits": false,
                      "terraform_hits": [],
                      "error": true
                    },
                    "End": true
                  }
                }
              }
            ],
            "ResultPath": "$.phase1_results",
            "Catch": [
              {
                "ErrorEquals": ["States.ALL"],
                "ResultPath": "$.error",
                "Next": "HandleDiscoveryError"
              }
            ],
            "Next": "DynamoPutSnapshot"
          },
          "DynamoPutSnapshot": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_dynamo_cli}",
              "Payload": {
                "action": "put-snapshot",
                "ticket_id.$": "$.ticket_id",
                "email.$": "$.email",
                "full_name.$": "$.full_name",
                "last_working_day.$": "$.last_working_day",
                "auth0_matches.$": "$.phase1_results[0].matches",
                "terraform_hits.$": "$.phase1_results[1].terraform_hits"
              }
            },
            "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": "HandleDiscoveryError"
              }
            ],
            "Next": "RequiresApproval"
          },
          "RequiresApproval": {
            "Type": "Choice",
            "Choices": [
              {
                "Or": [
                  {
                    "Variable": "$.phase1_results[0].has_matches",
                    "BooleanEquals": true
                  },
                  {
                    "Variable": "$.phase1_results[1].has_hits",
                    "BooleanEquals": true
                  }
                ],
                "Next": "JiraAddPendingApprovalLabel"
              }
            ],
            "Default": "JiraAddNoActionsRequiredLabel"
          },
          "JiraAddPendingApprovalLabel": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "add-label",
                "ticket_id.$": "$.ticket_id",
                "label": "pending-approval"
              }
            },
            "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": "HandleDiscoveryError"
              }
            ],
            "Next": "JiraPendingApprovalComment"
          },
          "JiraPendingApprovalComment": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "add-comment",
                "ticket_id.$": "$.ticket_id",
                "comment": "Automation complete (Phase 1). Auth0 and/or GitHub results require human review before proceeding. Add the label 'approved-for-offboarding' to this ticket to allow Auth0 deletion and GitHub offboarding to proceed."
              }
            },
            "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": "DiscoveryComplete"
              }
            ],
            "Next": "DiscoveryComplete"
          },
          "JiraAddNoActionsRequiredLabel": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "add-label",
                "ticket_id.$": "$.ticket_id",
                "label": "no-actions-required"
              }
            },
            "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": "HandleDiscoveryError"
              }
            ],
            "Next": "JiraNoActionsComment"
          },
          "JiraNoActionsComment": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "add-comment",
                "ticket_id.$": "$.ticket_id",
                "comment": "Offboarding automation complete. No Auth0 users found and no GitHub Terraform references detected. Ticket is ready to close."
              }
            },
            "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": "DiscoveryComplete"
              }
            ],
            "Next": "DiscoveryComplete"
          },
          "DiscoveryComplete": {
            "Type": "Succeed"
          },
          "HandleDiscoveryError": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
              "FunctionName": "${lambda_jira_cli}",
              "Payload": {
                "action": "add-comment",
                "ticket_id.$": "$.ticket_id",
                "comment.$": "States.Format('Discovery 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": "DiscoveryFailed"
          },
          "DiscoveryFailed": {
            "Type": "Fail",
            "Error": "DiscoveryError",
            "Cause": "An error occurred during the discovery phase for this ticket. See Jira comment for details."
          }
        }
      },
      "ResultPath": null,
      "End": true
    }
  }
}
