import test from "node:test";
import assert from "node:assert/strict";
import * as conversationScroll from "./conversation-scroll.js";
import {
  conversationDirectSendMaxScrollTop,
  conversationDirectSendScrollGuard,
  conversationDirectSendSpacerHeight,
  conversationScrollTop,
  conversationTurnAnchorScrollTop,
  isNearConversationBottom,
  shouldAnchorConversationSend,
  shouldScrollConversation
} from "./conversation-scroll.js";

test("conversation viewport detects whether latest content is visible", () => {
  assert.equal(isNearConversationBottom({ scrollHeight: 1000, scrollTop: 500, clientHeight: 450 }), true);
  assert.equal(isNearConversationBottom({ scrollHeight: 1000, scrollTop: 200, clientHeight: 450 }), false);
});

test("conversation scrolls on chat open and follows updates only near bottom", () => {
  assert.equal(shouldScrollConversation({ sessionChanged: true, wasNearBottom: false }), true);
  assert.equal(shouldScrollConversation({ sessionChanged: false, wasNearBottom: true }), true);
  assert.equal(shouldScrollConversation({ sessionChanged: false, wasNearBottom: false }), false);
});

test("conversation scrolls when a new turn is added even if the reader was above the bottom", () => {
  assert.equal(shouldScrollConversation({ sessionChanged: false, wasNearBottom: false, turnAdded: true }), true);
});

test("conversation scroll interpolation reaches the bottom quickly with ease-out motion", () => {
  assert.equal(conversationScrollTop(100, 500, 0), 100);
  assert.ok(conversationScrollTop(100, 500, 110) > 300);
  assert.equal(conversationScrollTop(100, 500, 220), 500);
  assert.equal(conversationScrollTop(100, 500, 400), 500);
});

test("an idle direct send anchors its new turn 24 pixels below the header without anchoring queued work", () => {
  assert.equal(shouldAnchorConversationSend("completed"), true);
  assert.equal(shouldAnchorConversationSend("running"), false);
  assert.equal(shouldAnchorConversationSend("thinking"), false);
  assert.equal(conversationTurnAnchorScrollTop({
    currentScrollTop: 300,
    viewportTop: 100,
    turnTop: 520
  }), 640);
  assert.equal(conversationTurnAnchorScrollTop({
    currentScrollTop: 0,
    viewportTop: 100,
    turnTop: 150
  }), 0);
});

test("a direct send cannot scroll into reserved response space but unlocks real response growth", () => {
  assert.equal(conversationDirectSendMaxScrollTop({
    anchorScrollTop: 640,
    initialScrollHeight: 1400,
    currentScrollHeight: 1400,
    initialAvailableSpace: 200
  }), 640);
  assert.equal(conversationDirectSendMaxScrollTop({
    anchorScrollTop: 640,
    initialScrollHeight: 1400,
    currentScrollHeight: 1510,
    initialAvailableSpace: 200
  }), 640);
  assert.equal(conversationDirectSendMaxScrollTop({
    anchorScrollTop: 640,
    initialScrollHeight: 1400,
    currentScrollHeight: 1650,
    initialAvailableSpace: 200
  }), 690);
});

test("a direct send without reserved space leaves a short chat free to reach its real bottom", () => {
  const guard = {
    sessionId: "session-1",
    anchorScrollTop: 0,
    initialScrollHeight: 700,
    initialAvailableSpace: 500
  };
  assert.equal(conversationDirectSendScrollGuard({
    reservedSpaceHeight: 0,
    ...guard
  }), null);
  assert.deepEqual(conversationDirectSendScrollGuard({
    reservedSpaceHeight: 120,
    ...guard
  }), guard);
});

test("a completed direct send releases its scroll guard without releasing active work", () => {
  assert.equal(typeof conversationScroll.shouldReleaseConversationDirectSendGuard, "function");
  assert.equal(conversationScroll.shouldReleaseConversationDirectSendGuard({
    guardSessionId: "session-1",
    sessionId: "session-1",
    status: "completed"
  }), true);
  assert.equal(conversationScroll.shouldReleaseConversationDirectSendGuard({
    guardSessionId: "session-1",
    sessionId: "session-1",
    status: "running"
  }), false);
  assert.equal(conversationScroll.shouldReleaseConversationDirectSendGuard({
    guardSessionId: "session-1",
    sessionId: "session-1",
    status: "waiting_approval"
  }), false);
  assert.equal(conversationScroll.shouldReleaseConversationDirectSendGuard({
    guardSessionId: "session-2",
    sessionId: "session-1",
    status: "completed"
  }), false);
  assert.equal(conversationScroll.shouldReleaseConversationDirectSendGuard({
    guardSessionId: undefined,
    sessionId: undefined,
    status: undefined
  }), false);
});

test("a direct send reserves only the extra height needed to reach its anchor", () => {
  assert.equal(conversationDirectSendSpacerHeight({
    targetScrollTop: 640,
    scrollHeight: 1000,
    clientHeight: 450
  }), 90);
  assert.equal(conversationDirectSendSpacerHeight({
    targetScrollTop: 500,
    scrollHeight: 1000,
    clientHeight: 450
  }), 0);
});
