import { mkdtemp, rm, mkdir } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { ErrorCode } from "@pocketcoder/shared/constants";
import { openDatabase, closeDatabase } from "../../db/database.js";
import { createProject } from "../../db/repositories/projects.js";
import { handleAgentSessionNew } from "../agent-session-new.js";
// Suppress logger output in tests
vi.mock("../../utils/logger.js", () => ({
    logger: {
        debug: vi.fn(),
        info: vi.fn(),
        warn: vi.fn(),
        error: vi.fn(),
    },
}));
// Mock the OpenCode proxy
vi.mock("../../opencode/proxy.js", () => ({
    proxyToOpenCode: vi.fn(),
}));
import { proxyToOpenCode } from "../../opencode/proxy.js";
const mockProxyToOpenCode = vi.mocked(proxyToOpenCode);
describe("agent_session_new handler", () => {
    let tempDir;
    let projectDir;
    let dbPath;
    let projectId;
    beforeEach(async () => {
        tempDir = await mkdtemp(join(tmpdir(), "pocketcoder-agent-session-new-test-"));
        projectDir = join(tempDir, "project");
        dbPath = join(tempDir, "daemon.db");
        await mkdir(projectDir);
        closeDatabase();
        openDatabase(dbPath);
        const project = createProject("test-project", projectDir);
        projectId = project.project_id;
        mockProxyToOpenCode.mockReset();
    });
    afterEach(async () => {
        closeDatabase();
        await rm(tempDir, { recursive: true, force: true });
    });
    function createRequest(content = "Create a Button component") {
        return {
            type: "agent_session_new",
            requestId: "req-1",
            sessionId: projectId,
            payload: { content },
        };
    }
    function createSendCollector() {
        const messages = [];
        const send = (msg) => {
            messages.push(msg);
        };
        return { messages, send };
    }
    it("should create new session and return session ID from OpenCode", async () => {
        // Mock session create response
        mockProxyToOpenCode.mockResolvedValueOnce({
            status: 200,
            headers: {},
            body: JSON.stringify({ id: "new-session-123" }),
        });
        // Mock prompt_async response
        mockProxyToOpenCode.mockResolvedValueOnce({
            status: 202,
            headers: {},
            body: "",
        });
        const { messages, send } = createSendCollector();
        await handleAgentSessionNew(createRequest(), send);
        // Should get response with session ID
        expect(messages.length).toBeGreaterThanOrEqual(1);
        const resp = messages[0];
        expect(resp.type).toBe("agent_session_new_response");
        expect(resp.error).toBeUndefined();
        expect(resp.payload).toBeDefined();
        expect(resp.payload.agentSessionId).toBe("new-session-123");
    });
    it("should call OpenCode API to create session and send prompt", async () => {
        mockProxyToOpenCode.mockResolvedValueOnce({
            status: 200,
            headers: {},
            body: JSON.stringify({ id: "session-abc" }),
        });
        mockProxyToOpenCode.mockResolvedValueOnce({
            status: 202,
            headers: {},
            body: "",
        });
        const { messages, send } = createSendCollector();
        await handleAgentSessionNew(createRequest("Hello world"), send);
        // Verify POST /session was called with directory
        expect(mockProxyToOpenCode).toHaveBeenNthCalledWith(1, {
            method: "POST",
            path: "/session",
            body: JSON.stringify({ directory: projectDir }),
        });
        // Verify POST /session/:id/prompt_async was called with content
        expect(mockProxyToOpenCode).toHaveBeenNthCalledWith(2, {
            method: "POST",
            path: "/session/session-abc/prompt_async",
            body: JSON.stringify({ content: "Hello world" }),
        });
        const resp = messages[0];
        expect(resp.payload.agentSessionId).toBe("session-abc");
    });
    it("should error for non-existent project", async () => {
        const msg = {
            type: "agent_session_new",
            requestId: "req-2",
            sessionId: "non-existent-project",
            payload: { content: "Hello" },
        };
        const { messages, send } = createSendCollector();
        await handleAgentSessionNew(msg, send);
        expect(messages).toHaveLength(1);
        const resp = messages[0];
        expect(resp.error).toBeDefined();
        expect(resp.error.code).toBe(ErrorCode.INVALID_REQUEST);
        expect(mockProxyToOpenCode).not.toHaveBeenCalled();
    });
    it("should error for empty content", async () => {
        const { messages, send } = createSendCollector();
        await handleAgentSessionNew(createRequest(""), send);
        expect(messages).toHaveLength(1);
        const resp = messages[0];
        expect(resp.error).toBeDefined();
        expect(resp.error.code).toBe(ErrorCode.INVALID_REQUEST);
        expect(resp.error.message).toContain("content is required");
        expect(mockProxyToOpenCode).not.toHaveBeenCalled();
    });
    it("should handle OpenCode unreachable error", async () => {
        mockProxyToOpenCode.mockRejectedValue(new Error("OPENCODE_UNREACHABLE"));
        const { messages, send } = createSendCollector();
        await handleAgentSessionNew(createRequest(), send);
        expect(messages).toHaveLength(1);
        const resp = messages[0];
        expect(resp.error).toBeDefined();
        expect(resp.error.code).toBe(ErrorCode.OPENCODE_UNREACHABLE);
    });
    it("should handle timeout error", async () => {
        mockProxyToOpenCode.mockRejectedValue(new Error("TIMEOUT"));
        const { messages, send } = createSendCollector();
        await handleAgentSessionNew(createRequest(), send);
        expect(messages).toHaveLength(1);
        const resp = messages[0];
        expect(resp.error).toBeDefined();
        expect(resp.error.code).toBe(ErrorCode.TIMEOUT);
    });
    it("should handle session creation failure", async () => {
        mockProxyToOpenCode.mockResolvedValue({
            status: 500,
            headers: {},
            body: "Internal server error",
        });
        const { messages, send } = createSendCollector();
        await handleAgentSessionNew(createRequest(), send);
        expect(messages).toHaveLength(1);
        const resp = messages[0];
        expect(resp.error).toBeDefined();
        expect(resp.error.code).toBe(ErrorCode.UNKNOWN_ERROR);
    });
    it("should still return session ID if prompt fails", async () => {
        // Session creation succeeds
        mockProxyToOpenCode.mockResolvedValueOnce({
            status: 200,
            headers: {},
            body: JSON.stringify({ id: "session-xyz" }),
        });
        // Prompt fails
        mockProxyToOpenCode.mockRejectedValueOnce(new Error("Connection lost"));
        const { messages, send } = createSendCollector();
        await handleAgentSessionNew(createRequest(), send);
        // Should still get success response with session ID
        // (the prompt error is logged but doesn't affect the response)
        expect(messages).toHaveLength(1);
        const resp = messages[0];
        expect(resp.error).toBeUndefined();
        expect(resp.payload.agentSessionId).toBe("session-xyz");
    });
});
//# sourceMappingURL=agent-session-new.test.js.map