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 { 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
let onDataCallback = null;
let _onExitCallback = null;
const mockWrite = vi.fn();
const mockResize = vi.fn();
const mockKill = vi.fn();
const mockSpawn = vi.fn().mockImplementation(() => ({
    pid: 12345,
    onData: (cb) => {
        onDataCallback = cb;
    },
    onExit: (cb) => {
        _onExitCallback = cb;
    },
    write: mockWrite,
    resize: mockResize,
    kill: mockKill,
}));
vi.mock("node-pty", () => ({
    spawn: (...args) => mockSpawn(...args),
}));
// Mock terminal-sessions DB
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(),
}));
import { createTerminal, setTerminalSendFn, killAllTerminals } from "../terminal-manager.js";
describe("terminal_stdout emission", () => {
    let tempDir;
    let projectDir;
    let dbPath;
    let projectId;
    let sentMessages;
    beforeEach(async () => {
        vi.clearAllMocks();
        onDataCallback = null;
        _onExitCallback = null;
        tempDir = await mkdtemp(join(tmpdir(), "pocketcoder-terminal-stdout-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;
        sentMessages = [];
        setTerminalSendFn((msg) => {
            sentMessages.push(msg);
        });
    });
    afterEach(async () => {
        killAllTerminals();
        closeDatabase();
        await rm(tempDir, { recursive: true, force: true });
    });
    it("should forward PTY output as terminal_stdout event", () => {
        const terminalId = createTerminal(projectId, projectDir);
        expect(onDataCallback).toBeDefined();
        // Simulate PTY stdout output
        onDataCallback("hello world");
        expect(sentMessages).toHaveLength(1);
        const event = sentMessages[0];
        expect(event.type).toBe("terminal_stdout");
        expect(event.payload.data).toBe("hello world");
        expect(event.payload.terminalId).toBe(terminalId);
    });
    it("should include terminalId in stdout events", () => {
        const terminalId = createTerminal(projectId, projectDir);
        onDataCallback("test output");
        const event = sentMessages[0];
        expect(event.payload.terminalId).toBe(terminalId);
        expect(typeof event.payload.terminalId).toBe("string");
        expect(event.payload.terminalId).toHaveLength(36); // UUID format
    });
    it("should preserve ANSI escape codes in output", () => {
        createTerminal(projectId, projectDir);
        // Common ANSI sequences: bold, color, cursor movement, reset
        const ansiOutput = "\x1b[1m\x1b[32mSuccess:\x1b[0m All tests passed\r\n";
        onDataCallback(ansiOutput);
        const event = sentMessages[0];
        expect(event.payload.data).toBe(ansiOutput);
        // Verify escape sequences are preserved exactly
        expect(event.payload.data).toContain("\x1b[1m");
        expect(event.payload.data).toContain("\x1b[32m");
        expect(event.payload.data).toContain("\x1b[0m");
        expect(event.payload.data).toContain("\r\n");
    });
    it("should forward multiple stdout chunks separately", () => {
        const terminalId = createTerminal(projectId, projectDir);
        onDataCallback("chunk 1");
        onDataCallback("chunk 2");
        onDataCallback("chunk 3");
        expect(sentMessages).toHaveLength(3);
        expect(sentMessages[0].payload.data).toBe("chunk 1");
        expect(sentMessages[1].payload.data).toBe("chunk 2");
        expect(sentMessages[2].payload.data).toBe("chunk 3");
        // All should reference the same terminal
        for (const msg of sentMessages) {
            expect(msg.payload.terminalId).toBe(terminalId);
        }
    });
    it("should not emit stdout when sendFn is not set", () => {
        setTerminalSendFn(null);
        // Reset sendFn to null - terminal-manager checks for null
        // We need to directly set it to null through the module
        // Since setTerminalSendFn requires a function, we test via the early return path
        // Create a terminal with a valid sendFn first, then unset it
        sentMessages = [];
        setTerminalSendFn((msg) => {
            sentMessages.push(msg);
        });
        createTerminal(projectId, projectDir);
        // Verify it works with sendFn
        onDataCallback("first");
        expect(sentMessages).toHaveLength(1);
        // Now nullify sendFn and verify no more messages
        sentMessages = [];
        setTerminalSendFn(null);
        onDataCallback("second");
        // sendFn is null, so no message should be emitted
        expect(sentMessages).toHaveLength(0);
    });
    it("should handle empty string data", () => {
        createTerminal(projectId, projectDir);
        onDataCallback("");
        expect(sentMessages).toHaveLength(1);
        const event = sentMessages[0];
        expect(event.payload.data).toBe("");
    });
    it("should handle binary-like data in string form", () => {
        createTerminal(projectId, projectDir);
        // Null bytes and control characters that might appear in terminal output
        const binaryLikeData = "data\x00with\x01control\x07chars";
        onDataCallback(binaryLikeData);
        const event = sentMessages[0];
        expect(event.payload.data).toBe(binaryLikeData);
    });
});
//# sourceMappingURL=terminal-stdout.test.js.map