///|
/// Something the App or its executor handled on its own but an operator should
/// know about. Delivered to `AppBuilder::on_warn`; without a hook the App
/// prints it. `Show` renders a one-line message for every variant, so a hook
/// that only logs can call `to_string()`.
#non_exhaustive
pub(all) enum AppWarning {
  /// The watchdog tried to defer a slow deferring route and the defer failed.
  AutoDeferFailed(target~ : String, error~ : Error)
  /// An immediate or raw route's response deadline passed before its handler
  /// could start (usually while waiting for handler capacity), so the
  /// interaction was not answered.
  InteractionDropped(target~ : String)
  /// A deferred interaction's token expired before its handler could start
  /// (usually while waiting for handler capacity), so its handler never ran.
  DeferredInteractionExpired(target~ : String)
  /// The executor was stopping (its `HandlerIntake` closed) before the
  /// handler could start, so its handler never ran.
  InteractionDroppedWhileStopping(target~ : String)
  /// An error policy tried to respond to a failure that has no interaction
  /// (an event or a service). `summary` describes the discarded response.
  ErrorResponseWithoutInteraction(summary~ : String)
  /// An error policy tried to respond after the interaction's window closed.
  ErrorResponseExpired(summary~ : String)
  /// The default policy met `HandlerError::Malformed`; `reason` is for logs.
  MalformedInteraction(origin~ : FailureOrigin, reason~ : String)
  /// The default policy met an error it does not render for users.
  UnhandledFailure(origin~ : FailureOrigin, error~ : Error)
  /// The error policy itself raised.
  ErrorPolicyFailed(error~ : Error)
  /// An autocomplete handler returned more than Discord's 25 choices; the
  /// first 25 were sent.
  AutocompleteTruncated(
    command_path~ : String,
    option~ : String,
    count~ : Int32
  )
  /// A handler registered directly on the framework raised.
  HandlerFailed(target~ : String, error~ : Error)
  /// `wait_for_component` ran on an executor without a gateway connection.
  WaiterUnavailable
  /// The executor could not process an interaction at all.
  InteractionProcessingFailed(error~ : Error)
  /// A warning raised by an executor about its own transport (for example the
  /// gateway `Bot`), identified by `source`.
  Executor(source~ : String, message~ : String)
  /// `max_pending` deferred interactions already wait for a handler, so more
  /// are refused with `HandlerError::Busy` until one starts. Reported once
  /// per overload.
  DeferredInteractionsRefused(max_pending~ : Int32)
  /// The cooldown store stopped answering; `outage` is what commands with a
  /// cooldown do until it answers again. Reported once per outage.
  CooldownUnavailable(reason~ : String, outage~ : CooldownOutage)
} derive(Debug)

///|
/// The one-line message, as the default hook prints it.
pub extend AppWarning with Show::{to_string}

///|
pub impl Show for AppWarning with fn output(self, logger) {
  match self {
    AutoDeferFailed(target~, error~) =>
      logger.write_string("automatic defer failed (\{target}): \{Repr(error)}")
    InteractionDropped(target~) =>
      logger.write_string(
        "interaction dropped (\{target}): the response deadline passed before the handler could start",
      )
    DeferredInteractionExpired(target~) =>
      logger.write_string(
        "deferred interaction expired (\{target}): the interaction token expired before the handler could start",
      )
    InteractionDroppedWhileStopping(target~) =>
      logger.write_string(
        "interaction dropped (\{target}): the executor was stopping before the handler could start",
      )
    ErrorResponseWithoutInteraction(summary~) =>
      logger.write_string(
        "cannot respond to a non-interaction failure: \{summary}",
      )
    ErrorResponseExpired(summary~) =>
      logger.write_string(
        "cannot respond to an expired interaction: \{summary}",
      )
    MalformedInteraction(origin~, reason~) =>
      logger.write_string("malformed interaction (\{Repr(origin)}): \{reason}")
    UnhandledFailure(origin~, error~) =>
      logger.write_string(
        "unhandled app failure (\{Repr(origin)}): \{Repr(error)}",
      )
    ErrorPolicyFailed(error~) =>
      logger.write_string("error policy failed: \{Repr(error)}")
    AutocompleteTruncated(command_path~, option~, count~) =>
      logger.write_string(
        "autocomplete choices truncated for command path \{command_path}, option \{option}: \{count} choices exceeds the limit of 25",
      )
    HandlerFailed(target~, error~) =>
      logger.write_string(
        "framework handler failed (\{target}): \{Repr(error)}",
      )
    WaiterUnavailable =>
      logger.write_string(
        "component waiting is unavailable without a gateway connection",
      )
    InteractionProcessingFailed(error~) =>
      logger.write_string("interaction processing failed: \{Repr(error)}")
    Executor(message~, ..) => logger.write_string(message)
    DeferredInteractionsRefused(max_pending~) =>
      logger.write_string(
        "\{max_pending} deferred interactions are waiting for a handler; refusing more as busy until one starts",
      )
    CooldownUnavailable(reason~, outage~) => {
      let effect = match outage {
        Allow => "commands run without cooldowns"
        Refuse => "commands with a cooldown are refused"
      }
      logger.write_string(
        "cooldown store unavailable (\{reason}): \{effect} until it answers again",
      )
    }
  }
}