// 负责聊天区域滚动控制的通用方法，解耦 App.vue 中的 DOM 操作

type ScrollContext = {
  getMessagesAreaElement?: () => HTMLElement | null;
  getThinkingContentElement?: (blockId: string) => HTMLElement | null;
  chatToggleScrollLockState?: () => boolean;
  chatSetScrollState?: (payload: { autoScrollEnabled?: boolean; userScrolling?: boolean }) => void;
  thinkingScrollLocks?: Map<string, boolean>;
  _setScrollingFlag?: (value: boolean) => void;
  autoScrollEnabled?: boolean;
  userScrolling?: boolean;
  isOutputActive?: () => boolean;
  streamingMessage?: boolean;
  hasPendingToolActions?: () => boolean;
};

type ScrollOptions = {
  ignoreUserScrolling?: boolean;
  /**
   * When true, force userScrolling=false after the programmatic scroll so that
   * later自动滚动不会被“用户滚动中”状态卡住。
   */
  resetUserScrolling?: boolean;
  /**
   * Control scroll behavior; 'smooth' 用于点击滚动锁按钮时的动画滚动。
   */
  behavior?: ScrollBehavior;
};

export function scrollToBottom(ctx: ScrollContext, options?: ScrollOptions) {
  const messagesArea = ctx.getMessagesAreaElement?.();
  if (!messagesArea) {
    return;
  }

  const attempts = [0, 16, 60, 150, 320, 520]; // 多次尝试覆盖布局抖动/异步伸缩
  let cancelled = false;
  const useSmooth =
    options?.behavior === 'smooth' &&
    typeof (messagesArea as HTMLElement).scrollTo === 'function';

  const perform = (idx: number) => {
    if (cancelled) return;
    if (ctx.userScrolling && !options?.ignoreUserScrolling) {
      cancelled = true;
      if (typeof ctx._setScrollingFlag === 'function') {
        ctx._setScrollingFlag(false);
      }
      return;
    }

    if (idx === 0 && typeof ctx._setScrollingFlag === 'function') {
      ctx._setScrollingFlag(true);
    }

    if (useSmooth) {
      (messagesArea as HTMLElement).scrollTo({
        top: messagesArea.scrollHeight,
        behavior: 'smooth'
      });
    } else {
      messagesArea.scrollTop = messagesArea.scrollHeight;
    }

    if (idx < attempts.length - 1) {
      setTimeout(() => perform(idx + 1), attempts[idx + 1] - attempts[idx]);
    } else if (typeof ctx._setScrollingFlag === 'function') {
      setTimeout(() => ctx._setScrollingFlag && ctx._setScrollingFlag(false), 40);
      if (options?.resetUserScrolling) {
        if (typeof ctx.chatSetScrollState === 'function') {
          ctx.chatSetScrollState({ userScrolling: false });
        } else if ('userScrolling' in ctx) {
          ctx.userScrolling = false;
        }
      }
    } else if (options?.resetUserScrolling) {
      if (typeof ctx.chatSetScrollState === 'function') {
        ctx.chatSetScrollState({ userScrolling: false });
      } else if ('userScrolling' in ctx) {
        ctx.userScrolling = false;
      }
    }
  };

  perform(0);
}

export function conditionalScrollToBottom(ctx: ScrollContext) {
  const active = typeof ctx.isOutputActive === 'function' ? ctx.isOutputActive() : true;
  if (ctx.autoScrollEnabled === true && ctx.userScrolling === false && active) {
    scrollToBottom(ctx);
  }
}

export function toggleScrollLock(ctx: ScrollContext) {
  const active = typeof ctx.isOutputActive === 'function' ? ctx.isOutputActive() : true;

  // 没有模型输出时：允许点击，但不切换锁定，仅单次滚动到底部
  if (!active) {
    scrollToBottom(ctx, { ignoreUserScrolling: true, resetUserScrolling: true, behavior: 'smooth' });
    return ctx.autoScrollEnabled ?? false;
  }

  const nextState = ctx.chatToggleScrollLockState?.() ?? false;
  if (nextState) {
    scrollToBottom(ctx);
  }
  return nextState;
}

/**
 * 在页面初始化/刷新后同步滚动锁定的默认状态：
 * 仅当存在输出（流式中或有未完成工具）时才保持锁定，否则自动解锁。
 */
export function normalizeScrollLock(ctx: ScrollContext) {
  const active =
    (typeof ctx.isOutputActive === 'function' ? ctx.isOutputActive() : false) ||
    !!ctx.streamingMessage ||
    (typeof ctx.hasPendingToolActions === 'function' ? ctx.hasPendingToolActions() : false);

  if (!active && ctx.autoScrollEnabled) {
    ctx.chatSetScrollState?.({ autoScrollEnabled: false, userScrolling: false });
  }
}

export function scrollThinkingToBottom(ctx: ScrollContext, blockId: string) {
  if (!blockId) {
    return;
  }
  if (!ctx.thinkingScrollLocks?.get(blockId)) {
    return;
  }
  const el = ctx.getThinkingContentElement?.(blockId);
  if (el) {
    el.scrollTop = el.scrollHeight;
  }
}
