/** Behaviour the message list should apply when new messages arrive. */
export type AutoScrollBehavior = 'smooth' | 'instant' | 'none';

export interface AutoScrollDecisionInput {
  /** Whether the message count grew since the last render. */
  readonly newMessages: boolean;
  /** Forced jump for the local user's own just-sent message. */
  readonly forceLocalSend: boolean;
  /** Pixels from the bottom of the scroll container. */
  readonly distanceFromBottom: number;
  /**
   * True while the conversation is still settling after a channel/server
   * switch (initial scroll + async embed/image height changes). During this
   * window we jump instantly so we never animate to the wrong position.
   */
  readonly withinInitialGrace: boolean;
  /** Distance threshold under which the list is considered stuck to bottom. */
  readonly stickyThreshold?: number;
}

/**
 * Decides how the message list should react when the message count changes.
 *
 * - No new messages -> `none`.
 * - Local user's own send -> `instant` (never animate your own message).
 * - Near bottom during the post-switch settle window -> `instant` (so loading
 *   a channel always lands at the bottom without a mid-chat smooth animation).
 * - Near bottom afterwards -> `smooth` (live messages animate into view).
 * - Far from bottom -> `none` (show the new-messages indicator instead).
 */
export function resolveAutoScrollBehavior(input: AutoScrollDecisionInput): AutoScrollBehavior {
  if (!input.newMessages)
    return 'none';

  if (input.forceLocalSend)
    return 'instant';

  const threshold = input.stickyThreshold ?? 300;

  if (input.distanceFromBottom > threshold)
    return 'none';

  return input.withinInitialGrace ? 'instant' : 'smooth';
}

/** Default pixel distance under which the list is considered stuck to bottom. */
export const STICKY_BOTTOM_THRESHOLD = 300;

/**
 * Whether the scroll position is close enough to the bottom that the list
 * should keep auto-pinning to the latest message. Negative distances (rubber
 * band / overscroll) count as stuck.
 *
 * This is the predicate the message list uses to decide whether a content
 * height change (late image/embed/plugin render) should re-pin to bottom.
 */
export function isStuckToBottom(distanceFromBottom: number, threshold: number = STICKY_BOTTOM_THRESHOLD): boolean {
  return distanceFromBottom <= threshold;
}
