{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://bpa.local/schemas/workflow/v1alpha3",
  "title": "BPA Workflow v1alpha3",
  "type": "object",
  "additionalProperties": false,
  "required": ["apiVersion", "kind", "metadata", "spec"],
  "properties": {
    "apiVersion": { "const": "bpa/v1alpha3" },
    "kind": { "const": "Workflow" },
    "metadata": { "$ref": "#/$defs/metadata" },
    "spec": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "riskLevel",
        "inputSchema",
        "outputSchema",
        "limits",
        "root"
      ],
      "properties": {
        "riskLevel": { "$ref": "#/$defs/riskLevel" },
        "inputSchema": { "$ref": "#/$defs/jsonSchema" },
        "outputSchema": { "$ref": "#/$defs/jsonSchema" },
        "limits": {
          "type": "object",
          "additionalProperties": false,
          "required": ["maxDepth", "maxStepExecutions"],
          "properties": {
            "maxDepth": {
              "type": "integer",
              "minimum": 1,
              "maximum": 3
            },
            "maxStepExecutions": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000
            }
          }
        },
        "resourceSlots": {
          "type": "object",
          "minProperties": 1,
          "maxProperties": 16,
          "propertyNames": { "$ref": "#/$defs/resourceName" },
          "additionalProperties": {
            "$ref": "#/$defs/browserResourceSlot"
          }
        },
        "root": { "$ref": "#/$defs/block" }
      }
    }
  },
  "$defs": {
    "metadata": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "version", "title"],
      "properties": {
        "id": { "$ref": "#/$defs/assetId" },
        "version": { "$ref": "#/$defs/semver" },
        "title": { "type": "string", "minLength": 1, "maxLength": 200 },
        "description": { "type": "string", "maxLength": 4000 }
      }
    },
    "assetId": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$",
      "maxLength": 160
    },
    "stepKey": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9_]*$",
      "maxLength": 100
    },
    "identifier": {
      "type": "string",
      "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
      "maxLength": 100
    },
    "resourceName": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9_]*$",
      "maxLength": 64
    },
    "capability": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9]*(?:[.:_-][a-z0-9]+)*$",
      "maxLength": 160
    },
    "httpsOrigin": {
      "type": "string",
      "format": "uri",
      "pattern": "^https://[^/?#@:]+(?::[1-9][0-9]{0,4})?$",
      "maxLength": 2048
    },
    "browserResourceSlot": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "kind",
        "capabilities",
        "allowedOrigins",
        "authentication",
        "purpose"
      ],
      "properties": {
        "kind": { "const": "browser" },
        "capabilities": {
          "type": "array",
          "minItems": 1,
          "maxItems": 32,
          "uniqueItems": true,
          "items": { "$ref": "#/$defs/capability" }
        },
        "allowedOrigins": {
          "type": "array",
          "minItems": 1,
          "maxItems": 16,
          "uniqueItems": true,
          "items": { "$ref": "#/$defs/httpsOrigin" }
        },
        "authentication": {
          "enum": ["anonymous", "optional", "authenticated", "membership"]
        },
        "purpose": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        }
      }
    },
    "resourceMappings": {
      "type": "object",
      "minProperties": 1,
      "maxProperties": 16,
      "propertyNames": { "$ref": "#/$defs/resourceName" },
      "additionalProperties": { "$ref": "#/$defs/resourceName" }
    },
    "semver": {
      "type": "string",
      "maxLength": 100,
      "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?$"
    },
    "nodeRef": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*@(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?$",
      "maxLength": 200
    },
    "duration": {
      "type": "string",
      "pattern": "^[1-9][0-9]*(?:ms|s|m|h)$"
    },
    "riskLevel": { "enum": ["R0", "R1", "R2", "R3", "R4"] },
    "jsonSchema": { "type": "object" },
    "binding": {
      "type": "string",
      "pattern": "^\\$\\{(?:input|item|index|steps\\.[a-z][a-z0-9_]*\\.output)(?:\\.[A-Za-z_][A-Za-z0-9_]*)*\\}$",
      "maxLength": 1000
    },
    "operand": {
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "binding"],
          "properties": {
            "kind": { "const": "binding" },
            "binding": { "$ref": "#/$defs/binding" }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "value"],
          "properties": {
            "kind": { "const": "literal" },
            "value": { "$ref": "#/$defs/safeValue" }
          }
        }
      ]
    },
    "condition": {
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "operator", "left"],
          "allOf": [
            {
              "if": {
                "properties": { "operator": { "const": "exists" } },
                "required": ["operator"]
              },
              "then": {
                "not": {
                  "properties": { "right": true },
                  "required": ["right"]
                }
              },
              "else": {
                "properties": { "right": true },
                "required": ["right"]
              }
            }
          ],
          "properties": {
            "kind": { "const": "compare" },
            "operator": {
              "enum": [
                "equals",
                "not_equals",
                "greater_than",
                "greater_than_or_equal",
                "less_than",
                "less_than_or_equal",
                "contains",
                "exists"
              ]
            },
            "left": { "$ref": "#/$defs/operand" },
            "right": { "$ref": "#/$defs/operand" }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "conditions"],
          "properties": {
            "kind": { "enum": ["all", "any"] },
            "conditions": {
              "type": "array",
              "minItems": 1,
              "maxItems": 50,
              "items": { "$ref": "#/$defs/condition" }
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "condition"],
          "properties": {
            "kind": { "const": "not" },
            "condition": { "$ref": "#/$defs/condition" }
          }
        }
      ]
    },
    "safeValue": {
      "oneOf": [
        { "type": "null" },
        { "type": "boolean" },
        { "type": "number" },
        { "type": "string", "maxLength": 100000 },
        {
          "type": "array",
          "maxItems": 10000,
          "items": { "$ref": "#/$defs/safeValue" }
        },
        {
          "type": "object",
          "maxProperties": 1000,
          "propertyNames": {
            "not": {
              "pattern": "^(?:selector|xpath|coordinates|javascript|script|cssSelector|screenX|screenY)$"
            }
          },
          "additionalProperties": { "$ref": "#/$defs/safeValue" }
        }
      ]
    },
    "retry": {
      "type": "object",
      "additionalProperties": false,
      "required": ["maxAttempts"],
      "properties": {
        "maxAttempts": { "type": "integer", "minimum": 1, "maximum": 10 },
        "backoff": { "$ref": "#/$defs/duration" },
        "retryableErrors": {
          "type": "array",
          "uniqueItems": true,
          "maxItems": 50,
          "items": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]+$" }
        }
      }
    },
    "handlers": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "failure": { "$ref": "#/$defs/block" },
        "timeout": { "$ref": "#/$defs/block" },
        "rejected": { "$ref": "#/$defs/block" },
        "cancelled": { "$ref": "#/$defs/block" },
        "uncertain": { "$ref": "#/$defs/block" }
      }
    },
    "block": {
      "type": "object",
      "additionalProperties": false,
      "required": ["kind", "steps"],
      "properties": {
        "kind": { "const": "sequence" },
        "steps": {
          "type": "array",
          "minItems": 1,
          "maxItems": 500,
          "items": { "$ref": "#/$defs/step" }
        }
      }
    },
    "step": {
      "oneOf": [
        { "$ref": "#/$defs/callStep" },
        { "$ref": "#/$defs/decisionStep" },
        { "$ref": "#/$defs/foreachStep" },
        { "$ref": "#/$defs/assistanceStep" },
        { "$ref": "#/$defs/terminalStep" }
      ]
    },
    "callStep": {
      "type": "object",
      "additionalProperties": false,
      "required": ["key", "kind", "use"],
      "properties": {
        "key": { "$ref": "#/$defs/stepKey" },
        "kind": { "const": "call" },
        "use": { "$ref": "#/$defs/nodeRef" },
        "with": { "$ref": "#/$defs/safeValue" },
        "timeout": { "$ref": "#/$defs/duration" },
        "retry": { "$ref": "#/$defs/retry" },
        "timing": {
          "$ref": "https://bpa.local/schemas/timing-policy/v1"
        },
        "resourceMappings": { "$ref": "#/$defs/resourceMappings" },
        "handlers": { "$ref": "#/$defs/handlers" },
        "description": { "type": "string", "maxLength": 1000 }
      }
    },
    "decisionStep": {
      "type": "object",
      "additionalProperties": false,
      "required": ["key", "kind", "condition", "then", "else"],
      "properties": {
        "key": { "$ref": "#/$defs/stepKey" },
        "kind": { "const": "decision" },
        "condition": { "$ref": "#/$defs/condition" },
        "then": { "$ref": "#/$defs/block" },
        "else": { "$ref": "#/$defs/block" },
        "description": { "type": "string", "maxLength": 1000 }
      }
    },
    "foreachStep": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "key",
        "kind",
        "items",
        "itemName",
        "indexName",
        "itemKey",
        "maxItems",
        "maxDuration",
        "onItemError",
        "body"
      ],
      "properties": {
        "key": { "$ref": "#/$defs/stepKey" },
        "kind": { "const": "foreach" },
        "items": { "$ref": "#/$defs/binding" },
        "itemName": { "$ref": "#/$defs/identifier" },
        "indexName": { "$ref": "#/$defs/identifier" },
        "itemKey": { "$ref": "#/$defs/binding" },
        "maxItems": { "type": "integer", "minimum": 1, "maximum": 500 },
        "maxDuration": { "$ref": "#/$defs/duration" },
        "onItemError": { "enum": ["stop", "collect"] },
        "body": { "$ref": "#/$defs/block" },
        "description": { "type": "string", "maxLength": 1000 }
      }
    },
    "assistanceStep": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "key",
        "kind",
        "use",
        "blocking",
        "onUnavailable"
      ],
      "properties": {
        "key": { "$ref": "#/$defs/stepKey" },
        "kind": { "const": "wait.assistance" },
        "use": { "$ref": "#/$defs/nodeRef" },
        "with": { "$ref": "#/$defs/safeValue" },
        "blocking": { "type": "boolean" },
        "deadline": { "$ref": "#/$defs/duration" },
        "onUnavailable": {
          "enum": ["continue_unresolved", "human_action", "fail"]
        },
        "handlers": { "$ref": "#/$defs/handlers" },
        "description": { "type": "string", "maxLength": 1000 }
      }
    },
    "terminalStep": {
      "type": "object",
      "additionalProperties": false,
      "required": ["key", "kind", "status"],
      "properties": {
        "key": { "$ref": "#/$defs/stepKey" },
        "kind": { "const": "terminal" },
        "status": {
          "enum": ["succeeded", "rejected", "failed", "cancelled", "uncertain"]
        },
        "output": { "$ref": "#/$defs/safeValue" },
        "error": {
          "type": "object",
          "additionalProperties": false,
          "required": ["code", "message"],
          "properties": {
            "code": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]+$" },
            "message": { "type": "string", "minLength": 1, "maxLength": 4000 }
          }
        },
        "description": { "type": "string", "maxLength": 1000 }
      }
    }
  }
}
