///|
/// Captures the work needed to register one new normalized shortcut.
struct RegisterPlan {
  id : Int
  parsed : ParsedGlobalHotkey
} derive(Debug, Eq)

///|
/// Captures the work needed to unregister one previously normalized shortcut.
struct UnregisterPlan {
  id : Int
  normalized : String
} derive(Debug, Eq)

///|
/// Stores the MoonBit-side registration bookkeeping that mirrors the native backend.
struct RegistrationStore {
  registrations : Map[String, Int]
  registration_names : Map[Int, String]
  mut next_id : Int
  mut destroyed : Bool
}

///|
/// Creates an empty registration store ready for a fresh manager.
fn new_registration_store() -> RegistrationStore {
  { registrations: {}, registration_names: {}, next_id: 1, destroyed: false }
}

///|
/// Rejects mutations after the manager has been destroyed.
fn RegistrationStore::ensure_alive(
  self : RegistrationStore,
) -> Result[Unit, String] {
  if self.destroyed {
    Err(destroyed_error_message())
  } else {
    Ok(())
  }
}

///|
/// Parses an accelerator only when the store is still active.
fn RegistrationStore::parse_active_accelerator(
  self : RegistrationStore,
  accelerator : String,
) -> Result[ParsedGlobalHotkey, String] {
  match self.ensure_alive() {
    Err(error) => Err(error)
    Ok(_) => parse_accelerator(accelerator)
  }
}

///|
/// Reserves and returns the next registration id.
fn RegistrationStore::take_next_id(self : RegistrationStore) -> Int {
  let id = self.next_id
  self.next_id += 1
  id
}

///|
/// Validates a registration request and reserves the next id for it.
fn RegistrationStore::plan_register(
  self : RegistrationStore,
  accelerator : String,
) -> Result[RegisterPlan, String] {
  match self.parse_active_accelerator(accelerator) {
    Err(error) => Err(error)
    Ok(parsed) =>
      match self.registrations.get(parsed.normalized) {
        Some(_) => Err("global_hotkey already registered: " + parsed.normalized)
        None => {
          let id = self.take_next_id()
          Ok({ id, parsed })
        }
      }
  }
}

///|
/// Records a native registration that has already succeeded.
fn RegistrationStore::commit_register(
  self : RegistrationStore,
  plan : RegisterPlan,
) -> Unit {
  self.registrations.set(plan.parsed.normalized, plan.id)
  self.registration_names.set(plan.id, plan.parsed.normalized)
}

///|
/// Validates an unregistration request and resolves it to a native id.
fn RegistrationStore::plan_unregister(
  self : RegistrationStore,
  accelerator : String,
) -> Result[UnregisterPlan?, String] {
  match self.parse_active_accelerator(accelerator) {
    Err(error) => Err(error)
    Ok(parsed) =>
      match self.registrations.get(parsed.normalized) {
        Some(id) => Ok(Some({ id, normalized: parsed.normalized }))
        None => Ok(None)
      }
  }
}

///|
/// Removes the MoonBit-side bookkeeping for a successful unregistration.
fn RegistrationStore::commit_unregister(
  self : RegistrationStore,
  plan : UnregisterPlan,
) -> Unit {
  self.registrations.remove(plan.normalized)
  self.registration_names.remove(plan.id)
}

///|
/// Returns the normalized registrations in insertion order.
fn RegistrationStore::list(self : RegistrationStore) -> Array[String] {
  let accelerators : Array[String] = []
  for accelerator, _ in self.registrations {
    accelerators.push(accelerator)
  }
  accelerators
}

///|
/// Resolves a triggered id back to the normalized accelerator string.
fn RegistrationStore::lookup_trigger(
  self : RegistrationStore,
  id : Int,
) -> String? {
  self.registration_names.get(id)
}

///|
/// Clears all bookkeeping and marks the store as destroyed.
fn RegistrationStore::destroy(self : RegistrationStore) -> Unit {
  self.destroyed = true
  self.registrations.clear()
  self.registration_names.clear()
}