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";
// Suppress logger output in tests
vi.mock("../../utils/logger.js", () => ({
    logger: {
        debug: vi.fn(),
        info: vi.fn(),
        warn: vi.fn(),
        error: vi.fn(),
    },
}));
// Mock node-pty
const mockOnData = vi.fn();
const mockOnExit = vi.fn();
const mockWrite = vi.fn();
const mockResize = vi.fn();
const mockKill = vi.fn();
const mockSpawn = vi.fn().mockReturnValue({
    pid: 12345,
    onData: mockOnData,
    onExit: mockOnExit,
    write: mockWrite,
    resize: mockResize,
    kill: mockKill,
});
vi.mock("node-pty", () => ({
    spawn: (...args) => mockSpawn(...args),
}));
// Mock terminal-manager's createTerminalSession DB call
vi.mock("../../db/repositories/terminal-sessions.js", () => ({
    createTerminalSession: vi.fn().mockReturnValue({
        terminal_id: "mock-terminal-id",
        project_id: "mock-project-id",
        pid: 12345,
        created_at: "2026-02-20T00:00:00.000Z",
        closed_at: null,
    }),
    closeTerminalSession: vi.fn(),
}));
// We need to import after mocks
import { handleTerminalCreate } from "../terminal-create.js";
import { setTerminalSendFn } from "../../services/terminal-manager.js";
describe("terminal_create handler", () => {
    let tempDir;
    let projectDir;
    let dbPath;
    let projectId;
    beforeEach(async () => {
        vi.clearAllMocks();
        tempDir = await mkdtemp(join(tmpdir(), "pocketcoder-terminal-create-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;
        // Set up terminal send function
        setTerminalSendFn(vi.fn());
    });
    afterEach(async () => {
        closeDatabase();
        await rm(tempDir, { recursive: true, force: true });
    });
    function createRequest(overrides) {
        return {
            type: "terminal_create",
            requestId: "req-1",
            sessionId: projectId,
            payload: { ...overrides },
        };
    }
    function createSendCollector() {
        const responses = [];
        const send = (msg) => {
            responses.push(msg);
        };
        return { responses, send };
    }
    it("should spawn PTY process", async () => {
        const { responses, send } = createSendCollector();
        await handleTerminalCreate(createRequest(), send);
        expect(mockSpawn).toHaveBeenCalledOnce();
        expect(responses).toHaveLength(1);
        expect(responses[0].error).toBeUndefined();
    });
    it("should use user shell from SHELL env", async () => {
        const originalShell = process.env.SHELL;
        process.env.SHELL = "/bin/zsh";
        const { send } = createSendCollector();
        await handleTerminalCreate(createRequest(), send);
        expect(mockSpawn).toHaveBeenCalledWith("/bin/zsh", [], expect.objectContaining({ name: "xterm-256color" }));
        process.env.SHELL = originalShell;
    });
    it("should set working directory to project path", async () => {
        const { send } = createSendCollector();
        await handleTerminalCreate(createRequest(), send);
        expect(mockSpawn).toHaveBeenCalledWith(expect.any(String), [], expect.objectContaining({ cwd: projectDir }));
    });
    it("should return terminalId", async () => {
        const { responses, send } = createSendCollector();
        await handleTerminalCreate(createRequest(), send);
        expect(responses).toHaveLength(1);
        const resp = responses[0];
        expect(resp.type).toBe("terminal_create_response");
        expect(resp.requestId).toBe("req-1");
        expect(resp.payload.terminalId).toBeDefined();
        expect(typeof resp.payload.terminalId).toBe("string");
    });
    it("should set up onData handler for stdout", async () => {
        const { send } = createSendCollector();
        await handleTerminalCreate(createRequest(), send);
        // node-pty's onData should have been called to register the stdout handler
        expect(mockOnData).toHaveBeenCalledOnce();
        expect(typeof mockOnData.mock.calls[0][0]).toBe("function");
    });
    it("should pass cols and rows to pty.spawn", async () => {
        const { send } = createSendCollector();
        await handleTerminalCreate(createRequest({ cols: 120, rows: 40 }), send);
        expect(mockSpawn).toHaveBeenCalledWith(expect.any(String), [], expect.objectContaining({ cols: 120, rows: 40 }));
    });
    it("should default cols to 80 and rows to 24", async () => {
        const { send } = createSendCollector();
        await handleTerminalCreate(createRequest(), send);
        expect(mockSpawn).toHaveBeenCalledWith(expect.any(String), [], expect.objectContaining({ cols: 80, rows: 24 }));
    });
    it("should return error for non-existent project", async () => {
        const { responses, send } = createSendCollector();
        const request = {
            type: "terminal_create",
            requestId: "req-1",
            sessionId: "non-existent-project",
            payload: {},
        };
        await handleTerminalCreate(request, send);
        expect(responses).toHaveLength(1);
        expect(responses[0].error).toBeDefined();
        expect(responses[0].error.code).toBe(ErrorCode.INVALID_REQUEST);
    });
    it("should return error when PTY spawn fails", async () => {
        mockSpawn.mockImplementationOnce(() => {
            throw new Error("Failed to spawn PTY");
        });
        const { responses, send } = createSendCollector();
        await handleTerminalCreate(createRequest(), send);
        expect(responses).toHaveLength(1);
        expect(responses[0].error).toBeDefined();
        expect(responses[0].error.code).toBe(ErrorCode.TERMINAL_SPAWN_FAILED);
    });
});
//# sourceMappingURL=terminal-create.test.js.map