import { describe, expect, test } from "bun:test";
import { generatePrompt, getAvailableCategories, getAvailableVariants, getRoleFraming, inferCategoryFromLabels, } from "../prompt-builder.js";
function makeCard(overrides = {}) {
    return {
        id: "card-1",
        short_id: 42,
        title: "Fix login button",
        description: "The login button is not responding on mobile devices.",
        priority: "high",
        done: false,
        labels: [{ name: "bug", color: "#ff0000" }],
        subtasks: [
            { title: "Reproduce on iOS", completed: true },
            { title: "Fix click handler", completed: false },
        ],
        links: [],
        assignee: { full_name: "Alice", email: "alice@test.com" },
        ...overrides,
    };
}
function makeMemory(overrides = {}) {
    return {
        id: "mem-1",
        type: "pattern",
        title: "Mobile Click Handler Pattern",
        content: "Use touchstart event for mobile devices to avoid 300ms delay on click events.",
        confidence: 0.95,
        tags: ["mobile", "performance"],
        ...overrides,
    };
}
// ─── Memory inclusion in generated prompts ──────────────────────────
describe("generatePrompt — memory inclusion", () => {
    test("includes legacy memories when no assembledContext", () => {
        const memories = [
            makeMemory({ title: "Pattern A", content: "Content A" }),
            makeMemory({ title: "Pattern B", content: "Content B" }),
        ];
        const result = generatePrompt({
            card: makeCard(),
            variant: "analysis",
            memories,
        });
        expect(result.prompt).toContain("## Relevant Memories");
        expect(result.prompt).toContain("Pattern A");
        expect(result.prompt).toContain("Pattern B");
        expect(result.prompt).toContain("Content A");
        expect(result.prompt).toContain("2 memories recalled");
        expect(result.contextSummary.memoryCount).toBe(2);
    });
    test("includes memory type and confidence in legacy format", () => {
        const memories = [
            makeMemory({
                type: "decision",
                confidence: 0.85,
                title: "Architecture Decision",
            }),
        ];
        const result = generatePrompt({
            card: makeCard(),
            variant: "analysis",
            memories,
        });
        expect(result.prompt).toContain("(decision, confidence: 0.85)");
    });
    test("includes memory tags in legacy format", () => {
        const memories = [
            makeMemory({ tags: ["auth", "security"], title: "Auth Pattern" }),
        ];
        const result = generatePrompt({
            card: makeCard(),
            variant: "analysis",
            memories,
        });
        expect(result.prompt).toContain("[auth, security]");
    });
    test("prefers assembledContext over legacy memories", () => {
        const assembledContext = "## Procedures (1 loaded)\n### Deploy Checklist\nStep 1: Run tests";
        const memories = [makeMemory({ title: "Should Not Appear" })];
        const result = generatePrompt({
            card: makeCard(),
            variant: "analysis",
            memories,
            assembledContext,
        });
        expect(result.prompt).toContain("Deploy Checklist");
        expect(result.prompt).not.toContain("Should Not Appear");
        expect(result.prompt).not.toContain("## Relevant Memories");
    });
    test("counts ### headings in assembledContext for memoryCount", () => {
        const assembledContext = [
            "## Relevant Memories (3 loaded)",
            "### Memory One",
            "Content one",
            "### Memory Two",
            "Content two",
            "### Memory Three",
            "Content three",
        ].join("\n");
        const result = generatePrompt({
            card: makeCard(),
            variant: "analysis",
            assembledContext,
        });
        expect(result.contextSummary.memoryCount).toBe(3);
    });
    test("memoryCount is 0 when no memories and no assembledContext", () => {
        const result = generatePrompt({
            card: makeCard(),
            variant: "analysis",
        });
        expect(result.contextSummary.memoryCount).toBe(0);
        expect(result.prompt).not.toContain("## Relevant Memories");
    });
    test("assemblyId is passed through to result", () => {
        const result = generatePrompt({
            card: makeCard(),
            variant: "execute",
            assembledContext: "### Test Memory\nContent",
            assemblyId: "ctx_abc123",
        });
        expect(result.assemblyId).toBe("ctx_abc123");
    });
    test("assemblyId is undefined when not provided", () => {
        const result = generatePrompt({
            card: makeCard(),
            variant: "analysis",
        });
        expect(result.assemblyId).toBeUndefined();
    });
});
// ─── Role framing based on labels ───────────────────────────────────
describe("inferCategoryFromLabels", () => {
    test("maps bug-related labels", () => {
        expect(inferCategoryFromLabels([{ name: "bug" }])).toBe("bug");
        expect(inferCategoryFromLabels([{ name: "fix" }])).toBe("bug");
        expect(inferCategoryFromLabels([{ name: "hotfix" }])).toBe("bug");
        expect(inferCategoryFromLabels([{ name: "defect" }])).toBe("bug");
    });
    test("maps feature-related labels", () => {
        expect(inferCategoryFromLabels([{ name: "feature" }])).toBe("feature");
        expect(inferCategoryFromLabels([{ name: "enhancement" }])).toBe("feature");
    });
    test("maps design-related labels", () => {
        expect(inferCategoryFromLabels([{ name: "design" }])).toBe("design");
        expect(inferCategoryFromLabels([{ name: "ui" }])).toBe("design");
        expect(inferCategoryFromLabels([{ name: "ux" }])).toBe("design");
    });
    test("returns custom for unknown labels", () => {
        expect(inferCategoryFromLabels([{ name: "unknown-label" }])).toBe("custom");
    });
    test("returns custom for empty labels", () => {
        expect(inferCategoryFromLabels([])).toBe("custom");
    });
    test("is case-insensitive", () => {
        expect(inferCategoryFromLabels([{ name: "BUG" }])).toBe("bug");
        expect(inferCategoryFromLabels([{ name: "Feature" }])).toBe("feature");
    });
    test("uses first matching label when multiple present", () => {
        const result = inferCategoryFromLabels([
            { name: "bug" },
            { name: "feature" },
        ]);
        expect(result).toBe("bug"); // First match wins
    });
});
// ─── Prompt structure ───────────────────────────────────────────────
describe("generatePrompt — structure", () => {
    test("includes role framing for bug category", () => {
        const result = generatePrompt({
            card: makeCard({ labels: [{ name: "bug", color: "#f00" }] }),
            variant: "analysis",
        });
        expect(result.role).toBe("Senior QA Engineer and Software Developer");
        expect(result.category).toBe("bug");
        expect(result.prompt).toContain("# Role: Senior QA Engineer");
    });
    test("includes variant instruction", () => {
        const analysis = generatePrompt({
            card: makeCard(),
            variant: "analysis",
        });
        expect(analysis.prompt).toContain("ANALYSIS MODE");
        expect(analysis.variant).toBe("analysis");
        const execute = generatePrompt({
            card: makeCard(),
            variant: "execute",
        });
        expect(execute.prompt).toContain("EXECUTE MODE");
    });
    test("includes card metadata", () => {
        const result = generatePrompt({
            card: makeCard(),
            column: { name: "In Progress" },
            variant: "analysis",
        });
        expect(result.prompt).toContain("# Task: Fix login button");
        expect(result.prompt).toContain("**Status:** In Progress");
        expect(result.prompt).toContain("**Priority:** high");
        expect(result.prompt).toContain("**Assignee:** Alice");
    });
    test("includes subtasks with completion status", () => {
        const result = generatePrompt({
            card: makeCard(),
            variant: "analysis",
        });
        expect(result.prompt).toContain("[x] Reproduce on iOS");
        expect(result.prompt).toContain("[ ] Fix click handler");
        expect(result.prompt).toContain("1/2 completed");
        expect(result.contextSummary.subtaskCount).toBe(2);
        expect(result.contextSummary.completedSubtasks).toBe(1);
    });
    test("includes description", () => {
        const result = generatePrompt({
            card: makeCard(),
            variant: "analysis",
        });
        expect(result.prompt).toContain("## Description");
        expect(result.prompt).toContain("login button is not responding on mobile");
        expect(result.contextSummary.hasDescription).toBe(true);
    });
    test("omits description when null", () => {
        const result = generatePrompt({
            card: makeCard({ description: null }),
            variant: "analysis",
        });
        expect(result.prompt).not.toContain("## Description");
        expect(result.contextSummary.hasDescription).toBe(false);
    });
    test("includes card reference footer", () => {
        const result = generatePrompt({
            card: makeCard({ short_id: 42 }),
            variant: "draft",
        });
        expect(result.prompt).toContain("Card #42");
        expect(result.prompt).toContain("draft mode");
    });
    test("execute variant includes progress tracking instructions", () => {
        const result = generatePrompt({
            card: makeCard(),
            variant: "execute",
        });
        expect(result.prompt).toContain("## Progress Tracking");
        expect(result.prompt).toContain("harmony_update_agent_progress");
    });
    test("non-execute variants omit progress tracking", () => {
        const analysis = generatePrompt({
            card: makeCard(),
            variant: "analysis",
        });
        expect(analysis.prompt).not.toContain("## Progress Tracking");
        const draft = generatePrompt({
            card: makeCard(),
            variant: "draft",
        });
        expect(draft.prompt).not.toContain("## Progress Tracking");
    });
    test("includes custom constraints when provided", () => {
        const result = generatePrompt({
            card: makeCard(),
            variant: "analysis",
            customConstraints: "Do not modify the database schema.",
        });
        expect(result.prompt).toContain("## Additional Instructions");
        expect(result.prompt).toContain("Do not modify the database schema.");
    });
});
// ─── Linked cards ───────────────────────────────────────────────────
describe("generatePrompt — linked cards", () => {
    test("includes related cards in prompt", () => {
        const result = generatePrompt({
            card: makeCard({
                links: [
                    {
                        target_card: { short_id: 10, title: "Auth refactor" },
                        display_type: "relates_to",
                        direction: "outgoing",
                    },
                    {
                        target_card: { short_id: 11, title: "API update" },
                        display_type: "is_blocked_by",
                        direction: "incoming",
                    },
                ],
            }),
            variant: "analysis",
        });
        expect(result.prompt).toContain("## Related Cards");
        expect(result.prompt).toContain("#10: Auth refactor");
        expect(result.prompt).toContain("#11: API update");
        expect(result.contextSummary.linkedCardCount).toBe(2);
    });
    test("synthesizes blocker as recommended next step", () => {
        const result = generatePrompt({
            card: makeCard({
                links: [
                    {
                        target_card: { short_id: 5, title: "Dependency task" },
                        display_type: "is_blocked_by",
                        direction: "incoming",
                    },
                ],
            }),
            variant: "execute",
        });
        expect(result.prompt).toContain("## Recommended Next Step");
        expect(result.prompt).toContain("Unblock first");
        expect(result.prompt).toContain("#5");
    });
});
// ─── One Thing synthesis ────────────────────────────────────────────
describe("generatePrompt — recommended next step", () => {
    test("suggests next incomplete subtask", () => {
        const result = generatePrompt({
            card: makeCard({
                links: [],
                subtasks: [
                    { title: "Step 1", completed: true },
                    { title: "Step 2", completed: false },
                    { title: "Step 3", completed: false },
                ],
            }),
            variant: "analysis",
        });
        expect(result.prompt).toContain("## Recommended Next Step");
        expect(result.prompt).toContain("Step 2");
    });
    test("suggests review when all subtasks done", () => {
        const result = generatePrompt({
            card: makeCard({
                links: [],
                subtasks: [
                    { title: "Step 1", completed: true },
                    { title: "Step 2", completed: true },
                ],
            }),
            variant: "analysis",
        });
        expect(result.prompt).toContain("All subtasks completed");
        expect(result.prompt).toContain("mark the card as done");
    });
    test("no recommended step when card is done", () => {
        const result = generatePrompt({
            card: makeCard({ done: true, links: [], subtasks: [] }),
            variant: "analysis",
        });
        expect(result.prompt).not.toContain("## Recommended Next Step");
    });
    test("resumes paused session from assembledContext", () => {
        const assembledContext = [
            "### Session: 2025-06-01",
            "Paused work on refactoring auth middleware",
            "Final task: Refactoring auth middleware",
            "Progress: 60%",
        ].join("\n");
        const result = generatePrompt({
            card: makeCard({ links: [], subtasks: [] }),
            variant: "execute",
            assembledContext,
        });
        expect(result.prompt).toContain("Resume previous session");
        expect(result.prompt).toContain("60% complete");
        expect(result.prompt).toContain("Refactoring auth middleware");
    });
});
// ─── Token estimation ───────────────────────────────────────────────
describe("generatePrompt — token estimation", () => {
    test("tokenEstimate is positive and proportional to content", () => {
        const short = generatePrompt({
            card: makeCard({ description: "Short" }),
            variant: "analysis",
        });
        const long = generatePrompt({
            card: makeCard({ description: "A".repeat(10000) }),
            variant: "analysis",
        });
        expect(short.tokenEstimate).toBeGreaterThan(0);
        expect(long.tokenEstimate).toBeGreaterThan(short.tokenEstimate);
    });
    test("memories increase token estimate", () => {
        const without = generatePrompt({
            card: makeCard(),
            variant: "analysis",
        });
        const with_ = generatePrompt({
            card: makeCard(),
            variant: "analysis",
            memories: [
                makeMemory({ content: "A".repeat(2000) }),
                makeMemory({ content: "B".repeat(2000) }),
            ],
        });
        expect(with_.tokenEstimate).toBeGreaterThan(without.tokenEstimate);
    });
});
// ─── Utility exports ────────────────────────────────────────────────
describe("utility exports", () => {
    test("getAvailableCategories returns all categories", () => {
        const categories = getAvailableCategories();
        expect(categories).toContain("bug");
        expect(categories).toContain("feature");
        expect(categories).toContain("custom");
        expect(categories.length).toBe(7);
    });
    test("getAvailableVariants returns all variants", () => {
        const variants = getAvailableVariants();
        expect(variants).toEqual(["analysis", "draft", "execute"]);
    });
    test("getRoleFraming returns framing for each category", () => {
        for (const category of getAvailableCategories()) {
            const framing = getRoleFraming(category);
            expect(framing.role).toBeTruthy();
            expect(framing.perspective).toBeTruthy();
            expect(framing.focus.length).toBeGreaterThan(0);
            expect(framing.outputSuggestions.length).toBeGreaterThan(0);
        }
    });
});
