/**
 * Tests for switch-skillset flow module
 *
 * These tests verify the switchSkillsetFlow function behavior including:
 * - Happy path: single agent, no changes, user confirms
 * - Agent selection when multiple agents installed
 * - Local change detection and handling (proceed, capture, abort)
 * - Confirmation and cancellation at various flow stages
 * - Zero agents edge case (defaults to claude-code)
 */
import * as clack from "@clack/prompts";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { switchSkillsetFlow, } from "./switchSkillset.js";
vi.mock("@clack/prompts", () => ({
    intro: vi.fn(),
    outro: vi.fn(),
    select: vi.fn(),
    confirm: vi.fn(),
    text: vi.fn(),
    spinner: vi.fn(() => ({
        start: vi.fn(),
        stop: vi.fn(),
        message: vi.fn(),
    })),
    note: vi.fn(),
    log: {
        info: vi.fn(),
        success: vi.fn(),
        warn: vi.fn(),
        error: vi.fn(),
        message: vi.fn(),
    },
    isCancel: vi.fn(() => false),
    cancel: vi.fn(),
}));
describe("switchSkillsetFlow", () => {
    let mockCallbacks;
    beforeEach(() => {
        vi.clearAllMocks();
        vi.mocked(clack.isCancel).mockReturnValue(false);
        mockCallbacks = {
            onResolveAgents: vi
                .fn()
                .mockResolvedValue([
                { name: "claude-code", displayName: "Claude Code" },
            ]),
            onPrepareSwitchInfo: vi.fn().mockResolvedValue({
                currentProfile: "senior-swe",
                localChanges: null,
            }),
            onCaptureConfig: vi.fn().mockResolvedValue(undefined),
            onExecuteSwitch: vi.fn().mockResolvedValue(undefined),
        };
    });
    describe("happy path: single agent, no changes, user confirms", () => {
        it("should not show agent select when single agent", async () => {
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(clack.select).not.toHaveBeenCalled();
        });
        it("should show confirmation with current and new skillset info", async () => {
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(clack.confirm).toHaveBeenCalledTimes(1);
            const confirmCall = vi.mocked(clack.confirm).mock.calls[0][0];
            expect(confirmCall.message).toContain("product-manager");
        });
        it("should display note with switch details before confirmation", async () => {
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            // note should show install dir, agent, current skillset, new skillset
            expect(clack.note).toHaveBeenCalledWith(expect.stringContaining("product-manager"), expect.any(String));
            expect(clack.note).toHaveBeenCalledWith(expect.stringContaining("senior-swe"), expect.any(String));
        });
        it("should call onExecuteSwitch when confirmed", async () => {
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(mockCallbacks.onExecuteSwitch).toHaveBeenCalledWith({
                installDir: "/test/dir",
                agentName: "claude-code",
                skillsetName: "product-manager",
            });
        });
        it("should return result with agent, profile name, and statusMessage", async () => {
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            const result = await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(result?.agentName).toBe("claude-code");
            expect(result?.skillsetName).toBe("product-manager");
            expect(result?.statusMessage).toBeDefined();
        });
    });
    describe("broadcasts to all agents", () => {
        it("should execute switch for all resolved agents without prompting for selection", async () => {
            vi.mocked(mockCallbacks.onResolveAgents).mockResolvedValueOnce([
                { name: "claude-code", displayName: "Claude Code" },
            ]);
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            // Should NOT show agent select - all agents are switched
            expect(clack.select).not.toHaveBeenCalled();
            // onExecuteSwitch receives all agent names
            expect(mockCallbacks.onExecuteSwitch).toHaveBeenCalledWith(expect.objectContaining({ skillsetName: "product-manager" }));
        });
        it("should call onExecuteSwitch for every resolved agent", async () => {
            vi.mocked(mockCallbacks.onResolveAgents).mockResolvedValueOnce([
                { name: "claude-code", displayName: "Claude Code" },
                { name: "agent-b", displayName: "Agent B" },
            ]);
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(mockCallbacks.onExecuteSwitch).toHaveBeenCalledTimes(2);
            expect(mockCallbacks.onExecuteSwitch).toHaveBeenCalledWith({
                installDir: "/test/dir",
                agentName: "claude-code",
                skillsetName: "product-manager",
            });
            expect(mockCallbacks.onExecuteSwitch).toHaveBeenCalledWith({
                installDir: "/test/dir",
                agentName: "agent-b",
                skillsetName: "product-manager",
            });
        });
    });
    describe("local changes detected - proceed", () => {
        it("should show note with changed files when changes detected", async () => {
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: ["skills/new-skill/SKILL.md"],
                    deleted: [],
                },
            });
            // First select: change handling (proceed)
            vi.mocked(clack.select).mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            // Should show note about changed files
            expect(clack.note).toHaveBeenCalledWith(expect.stringContaining("Modified"), expect.any(String));
        });
        it("should show select with proceed/capture/abort options", async () => {
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select).mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            const selectCall = vi.mocked(clack.select).mock.calls[0][0];
            expect(selectCall.options).toEqual(expect.arrayContaining([
                expect.objectContaining({ value: "proceed" }),
                expect.objectContaining({ value: "capture" }),
                expect.objectContaining({ value: "abort" }),
            ]));
        });
        it("should continue to confirmation after selecting proceed", async () => {
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select).mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(clack.confirm).toHaveBeenCalledTimes(1);
            expect(mockCallbacks.onExecuteSwitch).toHaveBeenCalled();
        });
    });
    describe("local changes detected - capture", () => {
        it("should prompt for skillset name when capture selected", async () => {
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select).mockResolvedValueOnce("capture");
            vi.mocked(clack.text).mockResolvedValueOnce("my-custom-skillset");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(clack.text).toHaveBeenCalledTimes(1);
            const textCall = vi.mocked(clack.text).mock.calls[0][0];
            expect(textCall.validate).toBeDefined();
        });
        it("should call onCaptureConfig with entered name", async () => {
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select).mockResolvedValueOnce("capture");
            vi.mocked(clack.text).mockResolvedValueOnce("my-custom-skillset");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(mockCallbacks.onCaptureConfig).toHaveBeenCalledWith({
                installDir: "/test/dir",
                skillsetName: "my-custom-skillset",
            });
        });
        it("should continue to confirmation after capture", async () => {
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select).mockResolvedValueOnce("capture");
            vi.mocked(clack.text).mockResolvedValueOnce("my-custom-skillset");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(clack.confirm).toHaveBeenCalledTimes(1);
            expect(mockCallbacks.onExecuteSwitch).toHaveBeenCalled();
        });
    });
    describe("local changes detected - abort", () => {
        it("should return null when user selects abort", async () => {
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select).mockResolvedValueOnce("abort");
            const result = await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(result).toBeNull();
        });
        it("should not call onExecuteSwitch when user aborts", async () => {
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select).mockResolvedValueOnce("abort");
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(mockCallbacks.onExecuteSwitch).not.toHaveBeenCalled();
            expect(clack.confirm).not.toHaveBeenCalled();
        });
    });
    describe("user declines confirmation", () => {
        it("should return null when user declines", async () => {
            vi.mocked(clack.confirm).mockResolvedValueOnce(false);
            const result = await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(result).toBeNull();
        });
    });
    describe("cancellation", () => {
        it("should return null when user cancels at confirmation", async () => {
            const cancelSymbol = Symbol.for("cancel");
            vi.mocked(clack.confirm).mockResolvedValueOnce(cancelSymbol);
            vi.mocked(clack.isCancel).mockImplementation((value) => value === cancelSymbol);
            const result = await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(result).toBeNull();
            expect(mockCallbacks.onExecuteSwitch).not.toHaveBeenCalled();
        });
        it("should return null when user cancels at change handling select", async () => {
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            const cancelSymbol = Symbol.for("cancel");
            vi.mocked(clack.select).mockResolvedValueOnce(cancelSymbol);
            vi.mocked(clack.isCancel).mockImplementation((value) => value === cancelSymbol);
            const result = await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(result).toBeNull();
            expect(mockCallbacks.onExecuteSwitch).not.toHaveBeenCalled();
        });
    });
    describe("multi-agent change detection", () => {
        it("should call onPrepareSwitchInfo for all resolved agents", async () => {
            vi.mocked(mockCallbacks.onResolveAgents).mockResolvedValueOnce([
                { name: "claude-code", displayName: "Claude Code" },
                { name: "agent-b", displayName: "Agent B" },
            ]);
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValue({
                currentProfile: "senior-swe",
                localChanges: null,
            });
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            // onPrepareSwitchInfo should be called for all agents
            expect(mockCallbacks.onPrepareSwitchInfo).toHaveBeenCalledWith({
                installDir: "/test/dir",
                agentName: "claude-code",
            });
            expect(mockCallbacks.onPrepareSwitchInfo).toHaveBeenCalledWith({
                installDir: "/test/dir",
                agentName: "agent-b",
            });
        });
        it("should show local changes if any agent has changes", async () => {
            vi.mocked(mockCallbacks.onResolveAgents).mockResolvedValueOnce([
                { name: "claude-code", displayName: "Claude Code" },
                { name: "agent-b", displayName: "Agent B" },
            ]);
            // First agent has no changes, second agent has changes
            vi.mocked(mockCallbacks.onPrepareSwitchInfo)
                .mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: null,
            })
                .mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["some-file.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select).mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            // Should show the change handling UI since agent-b has changes
            expect(clack.select).toHaveBeenCalled();
            // Should still execute the switch for all agents
            expect(mockCallbacks.onExecuteSwitch).toHaveBeenCalledTimes(2);
        });
        it("should call onCaptureConfig when user chooses capture with multi-agent changes", async () => {
            vi.mocked(mockCallbacks.onResolveAgents).mockResolvedValueOnce([
                { name: "claude-code", displayName: "Claude Code" },
                { name: "agent-b", displayName: "Agent B" },
            ]);
            vi.mocked(mockCallbacks.onPrepareSwitchInfo)
                .mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            })
                .mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: null,
            });
            vi.mocked(clack.select).mockResolvedValueOnce("capture");
            vi.mocked(clack.text).mockResolvedValueOnce("my-custom-skillset");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(mockCallbacks.onCaptureConfig).toHaveBeenCalledWith({
                installDir: "/test/dir",
                skillsetName: "my-custom-skillset",
            });
        });
    });
    describe("zero agents", () => {
        it("should default to claude-code when no agents resolved", async () => {
            vi.mocked(mockCallbacks.onResolveAgents).mockResolvedValueOnce([]);
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(clack.select).not.toHaveBeenCalled();
            expect(mockCallbacks.onExecuteSwitch).toHaveBeenCalled();
        });
    });
    describe("local changes detected - view changes", () => {
        it("should include viewDiff option when modified files exist and onReadFileDiff is provided", async () => {
            mockCallbacks.onReadFileDiff = vi
                .fn()
                .mockResolvedValue({ original: "old", current: "new" });
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select).mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            const selectCall = vi.mocked(clack.select).mock.calls[0][0];
            expect(selectCall.options).toEqual(expect.arrayContaining([
                expect.objectContaining({ value: "viewDiff" }),
            ]));
        });
        it("should not include viewDiff option when onReadFileDiff is not provided", async () => {
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select).mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            const selectCall = vi.mocked(clack.select).mock.calls[0][0];
            expect(selectCall.options).not.toEqual(expect.arrayContaining([
                expect.objectContaining({ value: "viewDiff" }),
            ]));
        });
        it("should not include viewDiff option when no modified files exist", async () => {
            mockCallbacks.onReadFileDiff = vi.fn();
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: [],
                    added: ["skills/new-skill/SKILL.md"],
                    deleted: ["skills/old-skill/SKILL.md"],
                },
            });
            vi.mocked(clack.select).mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            const selectCall = vi.mocked(clack.select).mock.calls[0][0];
            expect(selectCall.options).not.toEqual(expect.arrayContaining([
                expect.objectContaining({ value: "viewDiff" }),
            ]));
        });
        it("should show file picker when viewDiff selected with multiple modified files", async () => {
            mockCallbacks.onReadFileDiff = vi
                .fn()
                .mockResolvedValue({ original: "old", current: "new" });
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md", "skills/other/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            // First select: viewDiff, second select: pick file, third select: proceed
            vi.mocked(clack.select)
                .mockResolvedValueOnce("viewDiff")
                .mockResolvedValueOnce("skills/my-skill/SKILL.md")
                .mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            // Second select call should be the file picker
            const filePickerCall = vi.mocked(clack.select).mock.calls[1][0];
            expect(filePickerCall.options).toEqual(expect.arrayContaining([
                expect.objectContaining({ value: "skills/my-skill/SKILL.md" }),
                expect.objectContaining({ value: "skills/other/SKILL.md" }),
            ]));
        });
        it("should skip file picker and show diff directly when only one modified file", async () => {
            mockCallbacks.onReadFileDiff = vi.fn().mockResolvedValue({
                original: "old content\n",
                current: "new content\n",
            });
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            // First select: viewDiff, second select: proceed (no file picker needed)
            vi.mocked(clack.select)
                .mockResolvedValueOnce("viewDiff")
                .mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            // Should call onReadFileDiff with the single modified file
            expect(mockCallbacks.onReadFileDiff).toHaveBeenCalledWith({
                relativePath: "skills/my-skill/SKILL.md",
                installDir: "/test/dir",
            });
            // Should show a note with the diff content
            expect(clack.note).toHaveBeenCalledWith(expect.any(String), expect.stringContaining("skills/my-skill/SKILL.md"));
            // Only 2 select calls: viewDiff + proceed (no file picker)
            expect(clack.select).toHaveBeenCalledTimes(2);
        });
        it("should call onReadFileDiff and display diff note", async () => {
            mockCallbacks.onReadFileDiff = vi.fn().mockResolvedValue({
                original: "old content\n",
                current: "new content\n",
            });
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md", "CLAUDE.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select)
                .mockResolvedValueOnce("viewDiff")
                .mockResolvedValueOnce("skills/my-skill/SKILL.md")
                .mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(mockCallbacks.onReadFileDiff).toHaveBeenCalledWith({
                relativePath: "skills/my-skill/SKILL.md",
                installDir: "/test/dir",
            });
            // Should show a note containing the diff
            expect(clack.note).toHaveBeenCalledWith(expect.any(String), expect.stringContaining("skills/my-skill/SKILL.md"));
        });
        it("should show diff unavailable note when onReadFileDiff returns null", async () => {
            mockCallbacks.onReadFileDiff = vi.fn().mockResolvedValue(null);
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["CLAUDE.md"],
                    added: [],
                    deleted: [],
                },
            });
            vi.mocked(clack.select)
                .mockResolvedValueOnce("viewDiff")
                .mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(clack.note).toHaveBeenCalledWith(expect.stringContaining("not available"), expect.any(String));
        });
        it("should loop back to main select after viewing a diff", async () => {
            mockCallbacks.onReadFileDiff = vi
                .fn()
                .mockResolvedValue({ original: "old\n", current: "new\n" });
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md"],
                    added: [],
                    deleted: [],
                },
            });
            // First select: viewDiff, second select: proceed
            vi.mocked(clack.select)
                .mockResolvedValueOnce("viewDiff")
                .mockResolvedValueOnce("proceed");
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            const result = await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            // Should successfully complete after viewing diff and selecting proceed
            expect(result).toEqual({
                agentName: "claude-code",
                skillsetName: "product-manager",
                statusMessage: expect.stringContaining("Restart your agents to apply"),
            });
            expect(mockCallbacks.onExecuteSwitch).toHaveBeenCalled();
        });
        it("should return null when user cancels at file picker", async () => {
            mockCallbacks.onReadFileDiff = vi.fn();
            vi.mocked(mockCallbacks.onPrepareSwitchInfo).mockResolvedValueOnce({
                currentProfile: "senior-swe",
                localChanges: {
                    modified: ["skills/my-skill/SKILL.md", "CLAUDE.md"],
                    added: [],
                    deleted: [],
                },
            });
            const cancelSymbol = Symbol.for("cancel");
            vi.mocked(clack.select)
                .mockResolvedValueOnce("viewDiff")
                .mockResolvedValueOnce(cancelSymbol);
            vi.mocked(clack.isCancel).mockImplementation((value) => value === cancelSymbol);
            const result = await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(result).toBeNull();
            expect(mockCallbacks.onExecuteSwitch).not.toHaveBeenCalled();
        });
    });
    describe("redownload behavior", () => {
        it("should call onRedownload unconditionally when callback is provided", async () => {
            mockCallbacks.onRedownload = vi.fn().mockResolvedValue(undefined);
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(mockCallbacks.onRedownload).toHaveBeenCalledWith({
                skillsetName: "product-manager",
            });
            // Only one confirm call (the switch confirmation), no redownload prompt
            expect(clack.confirm).toHaveBeenCalledTimes(1);
        });
        it("should call onRedownload before executing the switch", async () => {
            const callOrder = [];
            mockCallbacks.onRedownload = vi.fn().mockImplementation(async () => {
                callOrder.push("redownload");
            });
            mockCallbacks.onExecuteSwitch = vi.fn().mockImplementation(async () => {
                callOrder.push("switch");
            });
            vi.mocked(clack.confirm).mockResolvedValueOnce(true);
            await switchSkillsetFlow({
                skillsetName: "product-manager",
                installDir: "/test/dir",
                callbacks: mockCallbacks,
            });
            expect(callOrder).toEqual(["redownload", "switch"]);
        });
    });
});
//# sourceMappingURL=switchSkillset.test.js.map