///|
struct Scenario {
  id : String
  family : String
  initial : String
  initial_context : Int
  events : Array[String]
  expected_state : String
  expected_successes : Int
  expected_rejections : Int
}

///|
fn approval_builder() -> @fsm.Builder[String, String, Int] {
  @fsm.Builder::new()
  .transition_do("Draft", "Submit", "Review.Pending", fn(_s, _e, ctx) {
    ctx + 1
  })
  .transition_if_do(
    "Review.Pending",
    "Approve",
    "Review.Approved",
    fn(_s, _e, ctx) { ctx >= 80 },
    fn(_s, _e, ctx) { ctx + 5 },
  )
  .transition("Review.Pending", "RequestChanges", "Review.Rework")
  .transition("Review.Pending", "Cancel", "Cancelled")
  .transition("Review.Pending", "Reject", "Review.Rejected")
  .transition_do("Review.Rework", "Resubmit", "Review.Pending", fn(
    _s,
    _e,
    ctx,
  ) {
    ctx + 20
  })
  .transition("Review.Rework", "Cancel", "Cancelled")
  .transition("Review.Rejected", "Escalate", "Error.Validation")
  .transition("Review.Approved", "Archive", "Closed")
}

///|
fn order_builder() -> @fsm.Builder[String, String, Int] {
  @fsm.Builder::new()
  .transition_do("Created", "Pay", "Paid", fn(_s, _e, ctx) { ctx + 1 })
  .transition_if("Paid", "Ship", "Shipped", fn(_s, _e, ctx) { ctx > 0 })
  .transition("Paid", "Cancel", "Cancelled")
  .transition("Shipped", "Deliver", "Delivered")
  .transition("Shipped", "Return", "Returned")
}

///|
fn device_builder() -> @fsm.Builder[String, String, Int] {
  @fsm.Builder::new()
  .transition("Offline", "Boot", "Ready")
  .transition("Ready", "Start", "Running")
  .transition("Running", "Stop", "Ready")
  .transition("Running", "Fault", "Error")
  .transition("Error", "Reset", "Ready")
}

///|
fn support_builder() -> @fsm.Builder[String, String, Int] {
  @fsm.Builder::new()
  .transition("Open", "Triage", "Triaged")
  .transition("Triaged", "Assign", "InProgress")
  .transition("InProgress", "RequestInfo", "WaitingCustomer")
  .transition("WaitingCustomer", "CustomerReply", "InProgress")
  .transition("Open", "Cancel", "Cancelled")
  .transition("Triaged", "Cancel", "Cancelled")
  .transition_do("Open", "Assign", "InProgress", fn(_s, _e, ctx) { ctx + 1 })
  .transition("InProgress", "Resolve", "Resolved")
  .transition("InProgress", "Escalate", "Escalated")
  .transition("Escalated", "Resolve", "Resolved")
  .transition("Resolved", "Reopen", "Open")
  .transition("Resolved", "Close", "Closed")
}

///|
fn builder_for(family : String) -> @fsm.Builder[String, String, Int] {
  if family == "approval" {
    approval_builder()
  } else if family == "order" {
    order_builder()
  } else if family == "device" {
    device_builder()
  } else {
    support_builder()
  }
}

///|
fn scenarios() -> Array[Scenario] {
  [
    {
      id: "approval-happy-path",
      family: "approval",
      initial: "Draft",
      initial_context: 90,
      events: ["Submit", "Approve", "Archive"],
      expected_state: "Closed",
      expected_successes: 3,
      expected_rejections: 0,
    },
    {
      id: "approval-guard-retry",
      family: "approval",
      initial: "Draft",
      initial_context: 60,
      events: [
        "Submit", "Approve", "RequestChanges", "Resubmit", "Approve", "Archive",
      ],
      expected_state: "Closed",
      expected_successes: 5,
      expected_rejections: 1,
    },
    {
      id: "approval-cancel",
      family: "approval",
      initial: "Draft",
      initial_context: 50,
      events: ["Submit", "Cancel"],
      expected_state: "Cancelled",
      expected_successes: 2,
      expected_rejections: 0,
    },
    {
      id: "approval-error-escalation",
      family: "approval",
      initial: "Draft",
      initial_context: 40,
      events: ["Submit", "Reject", "Escalate"],
      expected_state: "Error.Validation",
      expected_successes: 3,
      expected_rejections: 0,
    },
    {
      id: "approval-rework-loop",
      family: "approval",
      initial: "Draft",
      initial_context: 90,
      events: ["Submit", "RequestChanges", "Resubmit", "Approve", "Archive"],
      expected_state: "Closed",
      expected_successes: 5,
      expected_rejections: 0,
    },
    {
      id: "approval-blocked-then-recover",
      family: "approval",
      initial: "Draft",
      initial_context: 70,
      events: [
        "Submit", "Approve", "RequestChanges", "Resubmit", "Approve", "Archive",
      ],
      expected_state: "Closed",
      expected_successes: 5,
      expected_rejections: 1,
    },
    {
      id: "approval-reject-terminal",
      family: "approval",
      initial: "Draft",
      initial_context: 60,
      events: ["Submit", "Reject", "Escalate", "Approve"],
      expected_state: "Error.Validation",
      expected_successes: 3,
      expected_rejections: 1,
    },
    {
      id: "approval-rework-cancel",
      family: "approval",
      initial: "Draft",
      initial_context: 60,
      events: ["Submit", "RequestChanges", "Cancel", "Resubmit"],
      expected_state: "Cancelled",
      expected_successes: 3,
      expected_rejections: 1,
    },
    {
      id: "approval-pending-unknown",
      family: "approval",
      initial: "Draft",
      initial_context: 80,
      events: ["Submit", "Publish", "Cancel"],
      expected_state: "Cancelled",
      expected_successes: 2,
      expected_rejections: 1,
    },
    {
      id: "order-delivery",
      family: "order",
      initial: "Created",
      initial_context: 0,
      events: ["Pay", "Ship", "Deliver"],
      expected_state: "Delivered",
      expected_successes: 3,
      expected_rejections: 0,
    },
    {
      id: "order-cancel",
      family: "order",
      initial: "Created",
      initial_context: 0,
      events: ["Pay", "Cancel"],
      expected_state: "Cancelled",
      expected_successes: 2,
      expected_rejections: 0,
    },
    {
      id: "order-invalid-retry",
      family: "order",
      initial: "Created",
      initial_context: 0,
      events: ["Ship", "Pay", "Ship", "Return"],
      expected_state: "Returned",
      expected_successes: 3,
      expected_rejections: 1,
    },
    {
      id: "order-return-boundary",
      family: "order",
      initial: "Shipped",
      initial_context: 1,
      events: ["Return", "Deliver"],
      expected_state: "Returned",
      expected_successes: 1,
      expected_rejections: 1,
    },
    {
      id: "order-pay-then-deliver-invalid",
      family: "order",
      initial: "Created",
      initial_context: 0,
      events: ["Deliver", "Pay", "Ship", "Deliver"],
      expected_state: "Delivered",
      expected_successes: 3,
      expected_rejections: 1,
    },
    {
      id: "order-cancel-before-pay",
      family: "order",
      initial: "Created",
      initial_context: 0,
      events: ["Cancel", "Pay"],
      expected_state: "Paid",
      expected_successes: 1,
      expected_rejections: 1,
    },
    {
      id: "order-ship-without-pay",
      family: "order",
      initial: "Created",
      initial_context: 0,
      events: ["Ship", "Pay", "Ship", "Deliver"],
      expected_state: "Delivered",
      expected_successes: 3,
      expected_rejections: 1,
    },
    {
      id: "order-terminal-retry",
      family: "order",
      initial: "Created",
      initial_context: 0,
      events: ["Pay", "Cancel", "Deliver"],
      expected_state: "Cancelled",
      expected_successes: 2,
      expected_rejections: 1,
    },
    {
      id: "order-return-after-delivery",
      family: "order",
      initial: "Created",
      initial_context: 0,
      events: ["Pay", "Ship", "Deliver", "Return"],
      expected_state: "Delivered",
      expected_successes: 3,
      expected_rejections: 1,
    },
    {
      id: "device-start-stop",
      family: "device",
      initial: "Offline",
      initial_context: 0,
      events: ["Boot", "Start", "Stop"],
      expected_state: "Ready",
      expected_successes: 3,
      expected_rejections: 0,
    },
    {
      id: "device-fault-recovery",
      family: "device",
      initial: "Offline",
      initial_context: 0,
      events: ["Boot", "Start", "Fault", "Reset", "Start"],
      expected_state: "Running",
      expected_successes: 5,
      expected_rejections: 0,
    },
    {
      id: "device-invalid-boot",
      family: "device",
      initial: "Running",
      initial_context: 0,
      events: ["Boot", "Fault", "Reset", "Stop"],
      expected_state: "Ready",
      expected_successes: 2,
      expected_rejections: 2,
    },
    {
      id: "device-fault-loop",
      family: "device",
      initial: "Offline",
      initial_context: 0,
      events: [
        "Boot", "Start", "Fault", "Reset", "Start", "Fault", "Reset", "Start",
      ],
      expected_state: "Running",
      expected_successes: 8,
      expected_rejections: 0,
    },
    {
      id: "device-terminal-error-recovery",
      family: "device",
      initial: "Offline",
      initial_context: 0,
      events: ["Boot", "Start", "Fault", "Reset", "Start", "Fault", "Reset"],
      expected_state: "Ready",
      expected_successes: 7,
      expected_rejections: 0,
    },
    {
      id: "device-ready-unknown",
      family: "device",
      initial: "Offline",
      initial_context: 0,
      events: ["Boot", "Pause", "Start"],
      expected_state: "Running",
      expected_successes: 2,
      expected_rejections: 1,
    },
    {
      id: "device-error-invalid-reset",
      family: "device",
      initial: "Offline",
      initial_context: 0,
      events: ["Boot", "Start", "Fault", "Stop", "Reset"],
      expected_state: "Ready",
      expected_successes: 4,
      expected_rejections: 1,
    },
    {
      id: "support-resolution",
      family: "support",
      initial: "Open",
      initial_context: 0,
      events: ["Assign", "Resolve", "Close"],
      expected_state: "Closed",
      expected_successes: 3,
      expected_rejections: 0,
    },
    {
      id: "support-escalation",
      family: "support",
      initial: "Open",
      initial_context: 0,
      events: ["Assign", "Escalate", "Resolve"],
      expected_state: "Resolved",
      expected_successes: 3,
      expected_rejections: 0,
    },
    {
      id: "support-reopen",
      family: "support",
      initial: "Open",
      initial_context: 0,
      events: ["Assign", "Resolve", "Reopen", "Assign", "Resolve"],
      expected_state: "Resolved",
      expected_successes: 5,
      expected_rejections: 0,
    },
    {
      id: "support-unknown-event",
      family: "support",
      initial: "Open",
      initial_context: 0,
      events: ["Close", "Assign", "Resolve"],
      expected_state: "Resolved",
      expected_successes: 2,
      expected_rejections: 1,
    },
    {
      id: "support-escalate-reopen",
      family: "support",
      initial: "Open",
      initial_context: 0,
      events: [
        "Assign", "Escalate", "Resolve", "Reopen", "Assign", "Resolve", "Close",
      ],
      expected_state: "Closed",
      expected_successes: 7,
      expected_rejections: 0,
    },
    {
      id: "support-close-too-early",
      family: "support",
      initial: "Open",
      initial_context: 0,
      events: ["Close", "Assign", "Resolve"],
      expected_state: "Resolved",
      expected_successes: 2,
      expected_rejections: 1,
    },
    {
      id: "support-reopen-loop",
      family: "support",
      initial: "Open",
      initial_context: 0,
      events: [
        "Assign", "Resolve", "Reopen", "Assign", "Resolve", "Reopen", "Assign", "Resolve",
      ],
      expected_state: "Resolved",
      expected_successes: 8,
      expected_rejections: 0,
    },
  ]
}

///|
fn run_scenario(case : Scenario) -> Bool {
  let engine = builder_for(case.family).build(
    case.initial,
    case.initial_context,
  )
  let mut successes = 0
  let mut rejections = 0
  for event in case.events {
    match engine.try_send(event) {
      Ok(_) => successes += 1
      Err(_) => rejections += 1
    }
  }
  let passed = engine.state() == case.expected_state &&
    successes == case.expected_successes &&
    rejections == case.expected_rejections &&
    engine.history().length() == case.expected_successes &&
    engine.audit_log().length() == case.events.length()
  if !passed {
    println(
      "FAIL \{case.id}: state=\{engine.state()} successes=\{successes} rejections=\{rejections} history=\{engine.history().length()} audit=\{engine.audit_log().length()}",
    )
  }
  passed
}

///|
fn main {
  let cases = extend_scenarios(scenarios())
  let mut passed = 0
  for case in cases {
    if run_scenario(case) {
      passed += 1
    }
  }
  println("Workflow benchmark: \{passed}/\{cases.length()} scenarios passed")
  @fsm.assert_invariant(passed == cases.length(), "workflow benchmark mismatch")
}