///|
pub struct RouteRule {
  name : String
  provider : String
  event_type : String
  path : String
  http_method : HttpMethod
  handler : String
  enabled : Bool
} derive(Eq, Debug)

///|
pub fn RouteRule::new(
  name : String,
  provider : String,
  event_type : String,
  path : String,
  http_method : HttpMethod,
  handler : String,
  enabled? : Bool = true,
) -> RouteRule {
  { name, provider, event_type, path, http_method, handler, enabled }
}

///|
pub fn RouteRule::matches(
  self : RouteRule,
  request : WebhookRequest,
  profile : ProviderProfile,
) -> Bool {
  self.enabled &&
  self.provider == profile.name &&
  self.event_type == provider_event_type(request, profile) &&
  self.path == request.path &&
  self.http_method == request.http_method
}

///|
pub struct RouteDecision {
  matched : Bool
  rule_name : String
  handler : String
  reason : String
} derive(Eq, Debug)

///|
pub fn RouteDecision::matched(rule : RouteRule) -> RouteDecision {
  {
    matched: true,
    rule_name: rule.name,
    handler: rule.handler,
    reason: "matched",
  }
}

///|
pub fn RouteDecision::miss(reason : String) -> RouteDecision {
  { matched: false, rule_name: "", handler: "", reason }
}

///|
pub struct EventRouter {
  rules : Array[RouteRule]
} derive(Eq, Debug)

///|
pub fn EventRouter::empty() -> EventRouter {
  { rules: [] }
}

///|
pub fn EventRouter::add(self : EventRouter, rule : RouteRule) -> EventRouter {
  let old = self.rules
  let rules = Array::makei(old.length() + 1, i => {
    if i < old.length() {
      old[i]
    } else {
      rule
    }
  })
  { rules, }
}

///|
pub fn EventRouter::route(
  self : EventRouter,
  request : WebhookRequest,
  profile : ProviderProfile,
) -> RouteDecision {
  for i in 0.. Int {
  let mut count = 0
  for i in 0.. Array[String] {
  let mut count = 0
  for i in 0.. {
    while offset < self.rules.length() {
      let handler = self.rules[offset].handler
      let mut seen = false
      for j in 0.. EventRouter {
  EventRouter::empty()
  .add(
    RouteRule::new(
      "github_issues",
      "github",
      "issues",
      "/webhook/github",
      Post,
      "handle_github_issues",
    ),
  )
  .add(
    RouteRule::new(
      "github_push",
      "github",
      "push",
      "/webhook/github",
      Post,
      "handle_github_push",
    ),
  )
  .add(
    RouteRule::new(
      "feishu_message",
      "feishu",
      "im.message.receive_v1",
      "/webhook/feishu",
      Post,
      "handle_feishu_message",
    ),
  )
  .add(
    RouteRule::new(
      "stripe_invoice",
      "stripe",
      "invoice.paid",
      "/webhook/stripe",
      Post,
      "handle_stripe_invoice",
    ),
  )
}

///|
pub struct DispatchOutcome {
  verification : VerificationResult
  route : RouteDecision
} derive(Eq, Debug)

///|
pub fn dispatch_request(
  request : WebhookRequest,
  policy : VerificationPolicy,
  router : EventRouter,
) -> DispatchOutcome {
  let verification = verify_request(request, policy)
  let route = if verification.is_ok() {
    router.route(request, policy.provider)
  } else {
    RouteDecision::miss("verification failed")
  }
  { verification, route }
}

///|
pub fn dispatch_text(outcome : DispatchOutcome) -> String {
  let builder = StringBuilder::new()
  builder.write_string(outcome.verification.to_text())
  builder.write_string("\nroute=")
  if outcome.route.matched {
    builder.write_string(outcome.route.rule_name)
    builder.write_string(" handler=")
    builder.write_string(outcome.route.handler)
  } else {
    builder.write_string("miss reason=")
    builder.write_string(outcome.route.reason)
  }
  builder.to_string()
}

///|
pub fn route_demo_events() -> String {
  let outcome = dispatch_request(
    example_github_request(),
    VerificationPolicy::default(),
    default_router(),
  )
  dispatch_text(outcome)
}