import { getContextToken } from "../messaging/inbound.js";
import { sendMessageWeixin } from "../messaging/send.js";
import { ensureWeixinContextTokenForOutbound } from "../messaging/context-token-init.js";
import { createWeixinWorkflowProgressCapability } from "../workflow-progress.js";
import { describe, expect, it, vi } from "vitest";
//#region extensions/weixin/src/__tests__/workflow-progress.test.ts
vi.mock("../auth/accounts.js", () => ({ resolveWeixinAccount: () => ({
	accountId: "default",
	enabled: true,
	configured: true,
	token: "tok-x",
	baseUrl: "https://example/weixin",
	routeTag: void 0
}) }));
vi.mock("../messaging/inbound.js", () => ({
	getContextToken: vi.fn(),
	restoreContextTokens: vi.fn()
}));
vi.mock("../messaging/context-token-init.js", () => ({ ensureWeixinContextTokenForOutbound: vi.fn() }));
vi.mock("../messaging/send.js", async () => {
	return {
		...await vi.importActual("../messaging/send.js"),
		sendMessageWeixin: vi.fn().mockResolvedValue({ messageId: "weixin-1" })
	};
});
const getCtxMock = vi.mocked(getContextToken);
const ensureMock = vi.mocked(ensureWeixinContextTokenForOutbound);
const sendMock = vi.mocked(sendMessageWeixin);
const SESSION = "agent:main:weixin:default:direct:ilink_user_abc";
function mkCap() {
	return createWeixinWorkflowProgressCapability({ getConfig: () => ({}) });
}
describe("weixin workflow progress capability", () => {
	it("declares final-only defaults (no editMessage on WeChat)", () => {
		const cap = mkCap();
		expect(cap.channelId).toBe("weixin");
		expect(cap.supportsEdit).toBe(false);
		expect(cap.defaultMode).toBe("final-only");
		expect(cap.defaultThrottleMs).toBeGreaterThanOrEqual(3e4);
	});
	it("sends the final message when a cached context token exists", async () => {
		getCtxMock.mockReturnValue("tok-cached");
		sendMock.mockClear();
		ensureMock.mockClear();
		const r = await mkCap().postProgress({
			sessionKey: SESSION,
			text: "done",
			isFinal: true
		});
		expect(ensureMock).not.toHaveBeenCalled();
		expect(sendMock).toHaveBeenCalledWith(expect.objectContaining({
			to: "ilink_user_abc",
			text: "done",
			opts: expect.objectContaining({ contextToken: "tok-cached" })
		}));
		expect(r.messageId).toBe("weixin-1");
	});
	it("hydrates a context token on demand when cache is empty", async () => {
		getCtxMock.mockReturnValue(void 0);
		ensureMock.mockResolvedValue("tok-hydrated");
		sendMock.mockClear();
		await mkCap().postProgress({
			sessionKey: SESSION,
			text: "done",
			isFinal: true
		});
		expect(ensureMock).toHaveBeenCalled();
		const sendArg = sendMock.mock.calls[0][0];
		expect(sendArg.opts.contextToken).toBe("tok-hydrated");
	});
	it("returns an empty messageId without sending when no context token can be found", async () => {
		getCtxMock.mockReturnValue(void 0);
		ensureMock.mockResolvedValue(void 0);
		sendMock.mockClear();
		const r = await mkCap().postProgress({
			sessionKey: SESSION,
			text: "done",
			isFinal: true
		});
		expect(sendMock).not.toHaveBeenCalled();
		expect(r.messageId).toBe("");
	});
	it("throws on unroutable sessionKey", async () => {
		await expect(mkCap().postProgress({
			sessionKey: "agent:main:telegram:default:direct:123",
			text: "x",
			isFinal: true
		})).rejects.toThrow(/cannot route/);
	});
	it("clamps oversized text to WeChat limit", async () => {
		getCtxMock.mockReturnValue("tok");
		sendMock.mockClear();
		await mkCap().postProgress({
			sessionKey: SESSION,
			text: "x".repeat(1e4),
			isFinal: true
		});
		const text = sendMock.mock.calls[0][0].text;
		expect(text.length).toBeLessThanOrEqual(4e3);
		expect(text.endsWith("…")).toBe(true);
	});
	describe("append-mode message decoration", () => {
		it("prefixes mid-run append messages with \"工作流进展\" header", async () => {
			getCtxMock.mockReturnValue("tok");
			sendMock.mockClear();
			await mkCap().postProgress({
				sessionKey: SESSION,
				text: "Inventory done · Review running",
				isFinal: false,
				mode: "append"
			});
			const text = sendMock.mock.calls[0][0].text;
			expect(text.startsWith("▾ 工作流进展\n")).toBe(true);
			expect(text).toContain("Inventory done · Review running");
		});
		it("prefixes final append message with \"工作流完成\" header", async () => {
			getCtxMock.mockReturnValue("tok");
			sendMock.mockClear();
			await mkCap().postProgress({
				sessionKey: SESSION,
				text: "Top findings (3): …",
				isFinal: true,
				mode: "append"
			});
			const text = sendMock.mock.calls[0][0].text;
			expect(text.startsWith("✓ 工作流完成\n")).toBe(true);
			expect(text).toContain("Top findings (3): …");
		});
		it("does NOT add a header in final-only mode (existing behaviour preserved)", async () => {
			getCtxMock.mockReturnValue("tok");
			sendMock.mockClear();
			await mkCap().postProgress({
				sessionKey: SESSION,
				text: "Top findings (3): …",
				isFinal: true,
				mode: "final-only"
			});
			const text = sendMock.mock.calls[0][0].text;
			expect(text).not.toContain("工作流");
			expect(text).toBe("Top findings (3): …");
		});
		it("does NOT add a header when mode is omitted (back-compat)", async () => {
			getCtxMock.mockReturnValue("tok");
			sendMock.mockClear();
			await mkCap().postProgress({
				sessionKey: SESSION,
				text: "plain",
				isFinal: true
			});
			const text = sendMock.mock.calls[0][0].text;
			expect(text).toBe("plain");
		});
	});
});
//#endregion
export {};

//# sourceMappingURL=workflow-progress.test.js.map