///|
/// WebDriver BiDi session subscription state.
///
/// This keeps event subscriptions and buffered event delivery state outside of
/// the protocol command dispatcher.
priv struct BidiSubscriptionState {
  subscriptions : Map[String, Bool] // module-level subscriptions (legacy)
  context_subscriptions : Map[String, Array[String]] // context -> events
  user_context_subscriptions : Map[String, Array[String]] // user context -> events
  mut global_subscriptions : Array[String] // events subscribed globally
  mut global_unsubscribed_events : Array[String] // event-level overrides for module subscriptions
  subscription_events : Map[String, Array[String]] // subscription id -> events
  subscription_contexts : Map[String, Array[String]] // subscription id -> contexts
  subscription_user_contexts : Map[String, Array[String]] // subscription id -> user contexts
  mut next_subscription_id : Int
  pending_log_entries : Map[String, Array[Json]] // context -> buffered log.entryAdded params
}

///|
fn BidiSubscriptionState::new() -> BidiSubscriptionState {
  {
    subscriptions: {},
    context_subscriptions: {},
    user_context_subscriptions: {},
    global_subscriptions: [],
    global_unsubscribed_events: [],
    subscription_events: {},
    subscription_contexts: {},
    subscription_user_contexts: {},
    next_subscription_id: 1,
    pending_log_entries: {},
  }
}

///|
fn BidiSubscriptionState::reset(self : BidiSubscriptionState) -> Unit {
  let legacy_keys : Array[String] = []
  for key, _ in self.subscriptions {
    legacy_keys.push(key)
  }
  for key in legacy_keys {
    self.subscriptions.remove(key)
  }

  let context_keys : Array[String] = []
  for key, _ in self.context_subscriptions {
    context_keys.push(key)
  }
  for key in context_keys {
    self.context_subscriptions.remove(key)
  }

  let user_context_keys : Array[String] = []
  for key, _ in self.user_context_subscriptions {
    user_context_keys.push(key)
  }
  for key in user_context_keys {
    self.user_context_subscriptions.remove(key)
  }

  let subscription_keys : Array[String] = []
  for key, _ in self.subscription_events {
    subscription_keys.push(key)
  }
  for key in subscription_keys {
    self.subscription_events.remove(key)
    self.subscription_contexts.remove(key)
    self.subscription_user_contexts.remove(key)
  }

  let pending_log_keys : Array[String] = []
  for key, _ in self.pending_log_entries {
    pending_log_keys.push(key)
  }
  for key in pending_log_keys {
    self.pending_log_entries.remove(key)
  }

  self.global_subscriptions = []
  self.global_unsubscribed_events = []
  self.next_subscription_id = 1
}

///|
fn BidiSubscriptionState::has_subscription_id(
  self : BidiSubscriptionState,
  subscription_id : String,
) -> Bool {
  self.subscription_events.contains(subscription_id)
}

///|
fn BidiSubscriptionState::buffer_log_entry(
  self : BidiSubscriptionState,
  ctx_id : String,
  params : Json,
) -> Unit {
  let pending = self.pending_log_entries.get(ctx_id).unwrap_or([])
  pending.push(params)
  self.pending_log_entries[ctx_id] = pending
}

///|
fn BidiSubscriptionState::remove_pending_log_entries(
  self : BidiSubscriptionState,
  ctx_id : String,
) -> Unit {
  self.pending_log_entries.remove(ctx_id)
}

///|
fn BidiSubscriptionState::has_any_log_subscription(
  self : BidiSubscriptionState,
) -> Bool {
  if self.subscriptions.get("log").unwrap_or(false) {
    return true
  }
  if has_log_subscription(self.global_subscriptions) {
    return true
  }
  for _, events in self.context_subscriptions {
    if has_log_subscription(events) {
      return true
    }
  }
  for _, events in self.user_context_subscriptions {
    if has_log_subscription(events) {
      return true
    }
  }
  false
}

///|
fn BidiSubscriptionState::update_legacy_module_subscription(
  self : BidiSubscriptionState,
  module_key : String,
) -> Unit {
  let mut has_same_module = false
  for sub in self.global_subscriptions {
    if sub == module_key {
      has_same_module = true
      break
    }
  }
  if has_same_module {
    self.subscriptions[module_key] = true
  } else {
    self.subscriptions.remove(module_key)
  }
}

///|
fn BidiSubscriptionState::generate_subscription_id(
  self : BidiSubscriptionState,
) -> String {
  let id_num = self.next_subscription_id
  self.next_subscription_id += 1
  left_pad_with_zero(id_num.to_string(), 32)
}