(*
   --------------------------------------------------------------------------
   title: API Rate Limiting
   description: Rate limiting based on subscription and usage
   order: 7
   --------------------------------------------------------------------------

   Real-world example: API Rate Limiting

   Use case: Determine if an API request should be allowed based on
   rate limiting rules and user subscription level.

   Practical implications:
   - Ensures fair resource allocation
   - Identifies edge cases in throttling logic
   - Provides test cases for rate limiter implementation
   - Helps verify SLA compliance
   - Critical for system stability and abuse prevention
*)

type subscription = Free | Starter | Professional | Enterprise
type rate_limit_result = Allowed | RateLimited | Blocked

(* Get rate limit per hour for subscription *)
let requests_per_hour sub =
  match sub with
  | Free -> 100
  | Starter -> 1000
  | Professional -> 10000
  | Enterprise -> 100000

(* Calculate cooldown period in seconds *)
let cooldown_period sub =
  match sub with
  | Free -> 60        (* 1 minute *)
  | Starter -> 30     (* 30 seconds *)
  | Professional -> 10 (* 10 seconds *)
  | Enterprise -> 0   (* No cooldown *)

(* Check if request should be allowed *)
let check_rate_limit requests_in_last_hour subscription is_abuse_detected seconds_since_last =
  if is_abuse_detected then
    (* Abuse detected: block regardless *)
    Blocked
  else
    let limit = requests_per_hour subscription in
    let cooldown = cooldown_period subscription in
    if requests_in_last_hour >= limit then
      (* Over rate limit *)
      RateLimited
    else if seconds_since_last < cooldown then
      (* Within cooldown period *)
      RateLimited
    else
      (* Allow request *)
      Allowed
[@@decomp top ~basis:[[%id requests_per_hour]; [%id cooldown_period]] ()]

(* Variation: Simplified version without abuse checking *)
let check_rate_limit_legitimate requests_in_last_hour subscription seconds_since_last =
  let limit = requests_per_hour subscription in
  let cooldown = cooldown_period subscription in
  if requests_in_last_hour >= limit then
    RateLimited
  else if seconds_since_last < cooldown then
    RateLimited
  else
    Allowed
[@@decomp top ~ctx_simp:true ~basis:[[%id requests_per_hour]; [%id cooldown_period]] ()]

(* Variation: Focus on the decision logic with pruning *)
let check_rate_limit_pruned requests_in_last_hour subscription is_abuse_detected seconds_since_last =
  if is_abuse_detected then
    Blocked
  else
    let limit = requests_per_hour subscription in
    let cooldown = cooldown_period subscription in
    if requests_in_last_hour >= limit then
      RateLimited
    else if seconds_since_last < cooldown then
      RateLimited
    else
      Allowed
[@@decomp top ~basis:[[%id requests_per_hour]; [%id cooldown_period]] ~prune:true ~ctx_simp:true ()]
