// Slack's rate-limit tiers.
//
// https://docs.slack.dev/apis/web-api/rate-limits
//
// Every method belongs to a tier, and the tier is the only public statement
// Slack makes about how fast you may call it. The mapping is data, not
// knowledge: it lives in ext/metadata/rate_limit_tiers.json and is compiled
// into generated_methods.mbt by ext/tools/gen_methods. This file holds only the
// part that is a judgement -- what each tier means in requests per minute.

///|
pub(all) enum RateLimitTier {
  /// 1+ per minute. "Access tier 1 methods infrequently."
  Tier1
  /// 20+ per minute. The default, and by far the most common.
  Tier2
  /// 50+ per minute. Usually the paginating collection methods.
  Tier3
  /// 100+ per minute.
  Tier4
  /// `auth.test`: hundreds per minute.
  SpecialAuthTest
  /// `chat.getPermalink`: hundreds per minute.
  SpecialChatGetPermalink
  /// `chat.postMessage`: roughly one message per second **per channel**, with
  /// a separate workspace-wide ceiling above that. The per-channel part is why
  /// a throttling key for this method has to include the channel id.
  SpecialChatPostMessage
  /// `assistant.threads.setStatus`: like `chat.postMessage`, but per DM.
  SpecialAssistantThreadsSetStatus
} derive(Eq, Hash, Debug)

///|
/// The tier's documented floor, in requests per minute.
///
/// A floor, not a quota: Slack says "20+" and tolerates bursts. Treating it as
/// a hard ceiling is the conservative reading and the one @ratectl uses.
pub fn RateLimitTier::allowed_requests_per_minute(self : Self) -> Int {
  match self {
    Tier1 => 1
    Tier2 => 20
    Tier3 => 50
    Tier4 => 100
    SpecialAuthTest => 600
    SpecialChatGetPermalink => 600
    SpecialChatPostMessage => 60
    SpecialAssistantThreadsSetStatus => 60
  }
}

///|
/// The name this tier has in Slack's own metadata, which is the spelling
/// java-slack-sdk's enum uses and the one in `rate_limit_tiers.json`.
pub fn RateLimitTier::to_metadata_name(self : Self) -> String {
  match self {
    Tier1 => "Tier1"
    Tier2 => "Tier2"
    Tier3 => "Tier3"
    Tier4 => "Tier4"
    SpecialAuthTest => "SpecialTier_auth_test"
    SpecialChatGetPermalink => "SpecialTier_chat_getPermalink"
    SpecialChatPostMessage => "SpecialTier_chat_postMessage"
    SpecialAssistantThreadsSetStatus =>
      "SpecialTier_assistant_threads_setStatus"
  }
}

///|
pub fn RateLimitTier::parse(name : String) -> RateLimitTier? {
  match name {
    "Tier1" => Some(Tier1)
    "Tier2" => Some(Tier2)
    "Tier3" => Some(Tier3)
    "Tier4" => Some(Tier4)
    "SpecialTier_auth_test" => Some(SpecialAuthTest)
    "SpecialTier_chat_getPermalink" => Some(SpecialChatGetPermalink)
    "SpecialTier_chat_postMessage" => Some(SpecialChatPostMessage)
    "SpecialTier_assistant_threads_setStatus" =>
      Some(SpecialAssistantThreadsSetStatus)
    _ => None
  }
}

///|
/// Every tier, for tests and for anyone enumerating them.
pub let all_tiers : Array[RateLimitTier] = [
  Tier1,
  Tier2,
  Tier3,
  Tier4,
  SpecialAuthTest,
  SpecialChatGetPermalink,
  SpecialChatPostMessage,
  SpecialAssistantThreadsSetStatus,
]

///|
/// The tier for a method, or `None` if Slack's metadata does not list it.
pub fn tier_of(method_name : String) -> RateLimitTier? {
  method_tiers.get(method_name)
}

///|
/// The rate limit for a method, defaulting to Tier2 for anything unknown.
///
/// java-slack-sdk does the same: an unrecognised method is throttled as Tier2
/// rather than crashing or being left unthrottled. Tier2 is not necessarily
/// right -- but a wrong number is recoverable and no number is not.
pub fn allowed_requests_per_minute(method_name : String) -> Int {
  match tier_of(method_name) {
    Some(t) => t.allowed_requests_per_minute()
    None => RateLimitTier::Tier2.allowed_requests_per_minute()
  }
}

///|
/// Whether Slack's metadata knows this method at all. Useful for validating a
/// name before it reaches the network.
pub fn is_known(method_name : String) -> Bool {
  method_tiers.contains(method_name)
}