/**
 * Tests thread-aware outbound session route helpers exposed by the SDK.
 */
import { describe, expect, it } from "vitest";
import {
  buildThreadAwareOutboundSessionRoute,
  recoverCurrentThreadSessionId,
  type ChannelOutboundSessionRoute,
} from "./core.js";

function baseRoute(
  overrides: Partial<ChannelOutboundSessionRoute> = {},
): ChannelOutboundSessionRoute {
  return {
    sessionKey: "agent:main:workspace:channel:c123",
    baseSessionKey: "agent:main:workspace:channel:c123",
    peer: { kind: "channel", id: "c123" },
    chatType: "channel",
    from: "workspace:channel:c123",
    to: "channel:c123",
    ...overrides,
  };
}

describe("buildThreadAwareOutboundSessionRoute", () => {
  it("uses replyToId before threadId and recovered current-session thread by default", () => {
    const route = buildThreadAwareOutboundSessionRoute({
      route: baseRoute(),
      replyToId: "reply-1",
      threadId: "thread-1",
      currentSessionKey: "agent:main:workspace:channel:c123:thread:current-1",
    });

    expect(route).toEqual(
      baseRoute({
        sessionKey: "agent:main:workspace:channel:c123:thread:reply-1",
        threadId: "reply-1",
      }),
    );
  });

  it("supports provider-specific threadId-first precedence", () => {
    const route = buildThreadAwareOutboundSessionRoute({
      route: baseRoute(),
      replyToId: "reply-1",
      threadId: "thread-1",
      precedence: ["threadId", "replyToId", "currentSession"],
    });

    expect(route).toEqual(
      baseRoute({
        sessionKey: "agent:main:workspace:channel:c123:thread:thread-1",
        threadId: "thread-1",
      }),
    );
  });

  it("keeps numeric delivery thread ids on the route while stringifying the session suffix", () => {
    const route = buildThreadAwareOutboundSessionRoute({
      route: baseRoute(),
      threadId: 99,
    });

    expect(route).toEqual(
      baseRoute({
        sessionKey: "agent:main:workspace:channel:c123:thread:99",
        threadId: 99,
      }),
    );
  });

  it("recovers a current-session thread only when the base session matches", () => {
    expect(
      recoverCurrentThreadSessionId({
        route: baseRoute(),
        currentSessionKey: "agent:main:workspace:channel:c123:thread:current-1",
      }),
    ).toBe("current-1");
    expect(
      recoverCurrentThreadSessionId({
        route: baseRoute(),
        currentSessionKey: "agent:main:workspace:channel:other:thread:current-1",
      }),
    ).toBeUndefined();
  });

  it("does not recover current-session threads across case-distinct Matrix rooms", () => {
    const route = baseRoute({
      sessionKey: "agent:main:matrix:channel:!Mixed:example.org",
      baseSessionKey: "agent:main:matrix:channel:!Mixed:example.org",
      peer: { kind: "channel", id: "!Mixed:example.org" },
      from: "matrix:room:!Mixed:example.org",
      to: "room:!Mixed:example.org",
    });

    expect(
      recoverCurrentThreadSessionId({
        route,
        currentSessionKey: "agent:main:matrix:channel:!mixed:example.org:thread:$Root",
      }),
    ).toBeUndefined();
  });

  it("keeps recovering current-session threads for non-opaque folded channel keys", () => {
    expect(
      recoverCurrentThreadSessionId({
        route: baseRoute({
          sessionKey: "agent:main:slack:channel:c1",
          baseSessionKey: "agent:main:slack:channel:c1",
        }),
        currentSessionKey: "agent:main:slack:channel:C1:thread:1712345678.123456",
      }),
    ).toBe("1712345678.123456");
  });

  it("lets providers veto current-session recovery", () => {
    const route = buildThreadAwareOutboundSessionRoute({
      route: baseRoute(),
      currentSessionKey: "agent:main:workspace:channel:c123:thread:current-1",
      canRecoverCurrentThread: () => false,
    });

    expect(route).toEqual(
      baseRoute({
        sessionKey: "agent:main:workspace:channel:c123",
      }),
    );
  });

  it("preserves provider-specific thread case when requested", () => {
    const route = buildThreadAwareOutboundSessionRoute({
      route: baseRoute(),
      threadId: "$EventID:Example.Org",
      normalizeThreadId: (threadId) => threadId,
    });

    expect(route).toEqual(
      baseRoute({
        sessionKey: "agent:main:workspace:channel:c123:thread:$EventID:Example.Org",
        threadId: "$EventID:Example.Org",
      }),
    );
  });

  it("can carry a delivery thread without adding a session suffix", () => {
    const route = buildThreadAwareOutboundSessionRoute({
      route: baseRoute(),
      threadId: "thread-1",
      useSuffix: false,
    });

    expect(route).toEqual(
      baseRoute({
        sessionKey: "agent:main:workspace:channel:c123",
        threadId: "thread-1",
      }),
    );
  });
});
