import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { mkdtemp, rm, realpath } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
vi.mock("../../../utils/config.js", () => ({
    ensureConfigDirs: vi.fn().mockResolvedValue(undefined),
}));
vi.mock("../../../db/repositories/projects.js", () => ({
    createProject: vi.fn(),
    getProject: vi.fn(),
    getProjectByPath: vi.fn(),
    listProjects: vi.fn(),
    deleteProject: vi.fn(),
}));
vi.mock("../../notify-re-register.js", () => ({
    notifyDaemonReRegister: vi.fn().mockResolvedValue(undefined),
}));
import { createProject, getProject, getProjectByPath, listProjects, deleteProject, } from "../../../db/repositories/projects.js";
import { notifyDaemonReRegister } from "../../notify-re-register.js";
import { addCommand } from "../add.js";
import { removeCommand } from "../remove.js";
import { projectsCommand } from "../projects.js";
const mockCreateProject = vi.mocked(createProject);
const mockGetProject = vi.mocked(getProject);
const mockGetProjectByPath = vi.mocked(getProjectByPath);
const mockListProjects = vi.mocked(listProjects);
const mockDeleteProject = vi.mocked(deleteProject);
const mockNotifyReRegister = vi.mocked(notifyDaemonReRegister);
describe("add command", () => {
    let tempDir;
    beforeEach(async () => {
        vi.clearAllMocks();
        process.exitCode = undefined;
        // Use realpath to get the canonical path (handles macOS /var -> /private/var symlink)
        const rawTempDir = await mkdtemp(join(tmpdir(), "pocketcoder-cli-projects-"));
        tempDir = await realpath(rawTempDir);
        mockGetProjectByPath.mockReturnValue(null);
    });
    afterEach(async () => {
        await rm(tempDir, { recursive: true, force: true });
    });
    it("should add project to database", async () => {
        mockCreateProject.mockReturnValue({
            project_id: "550e8400-e29b-41d4-a716-446655440000",
            name: tempDir.split("/").pop(),
            path: tempDir,
            created_at: "2026-01-01T00:00:00Z",
            last_opened_at: null,
        });
        const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
        await addCommand(tempDir);
        expect(mockCreateProject).toHaveBeenCalledOnce();
        expect(mockCreateProject).toHaveBeenCalledWith(tempDir.split("/").pop(), tempDir);
        consoleSpy.mockRestore();
    });
    it("should generate UUID for new project", async () => {
        const projectId = "550e8400-e29b-41d4-a716-446655440000";
        mockCreateProject.mockReturnValue({
            project_id: projectId,
            name: "my-project",
            path: tempDir,
            created_at: "2026-01-01T00:00:00Z",
            last_opened_at: null,
        });
        const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
        await addCommand(tempDir);
        expect(consoleSpy).toHaveBeenCalledWith(`Project registered: my-project`);
        expect(consoleSpy).toHaveBeenCalledWith(`ID: ${projectId}`);
        consoleSpy.mockRestore();
    });
    it("should error for non-existent path", async () => {
        const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
        await addCommand("/nonexistent/path/abc123");
        expect(process.exitCode).toBe(1);
        expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Path does not exist"));
        expect(mockCreateProject).not.toHaveBeenCalled();
        consoleSpy.mockRestore();
    });
    it("should error for file path (not directory)", async () => {
        const filePath = join(tempDir, "file.txt");
        const { writeFile } = await import("node:fs/promises");
        await writeFile(filePath, "content");
        const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
        await addCommand(filePath);
        expect(process.exitCode).toBe(1);
        expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Path is not a directory"));
        expect(mockCreateProject).not.toHaveBeenCalled();
        consoleSpy.mockRestore();
    });
    it("should error for already registered project", async () => {
        mockGetProjectByPath.mockReturnValue({
            project_id: "existing-id",
            name: "existing-project",
            path: tempDir,
            created_at: "2026-01-01T00:00:00Z",
            last_opened_at: null,
        });
        const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
        await addCommand(tempDir);
        expect(process.exitCode).toBe(1);
        expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Project already registered"));
        expect(mockCreateProject).not.toHaveBeenCalled();
        consoleSpy.mockRestore();
    });
    it("should re-send daemon_register after adding project", async () => {
        mockCreateProject.mockReturnValue({
            project_id: "550e8400-e29b-41d4-a716-446655440000",
            name: "my-project",
            path: tempDir,
            created_at: "2026-01-01T00:00:00Z",
            last_opened_at: null,
        });
        const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
        await addCommand(tempDir);
        expect(mockNotifyReRegister).toHaveBeenCalledOnce();
        consoleSpy.mockRestore();
    });
    it("should not re-register when add fails", async () => {
        const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
        await addCommand("/nonexistent/path/abc123");
        expect(mockNotifyReRegister).not.toHaveBeenCalled();
        consoleSpy.mockRestore();
    });
    it("should log warning if project ignored due to limit", async () => {
        mockCreateProject.mockReturnValue({
            project_id: "550e8400-e29b-41d4-a716-446655440000",
            name: "my-project",
            path: tempDir,
            created_at: "2026-01-01T00:00:00Z",
            last_opened_at: null,
        });
        // Simulate re-register returning a warning
        mockNotifyReRegister.mockImplementation(async () => {
            // The actual warning logging is done inside notifyDaemonReRegister
            // based on the response. This test verifies it's called.
        });
        const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
        await addCommand(tempDir);
        expect(mockNotifyReRegister).toHaveBeenCalledOnce();
        consoleSpy.mockRestore();
    });
});
describe("remove command", () => {
    beforeEach(() => {
        vi.clearAllMocks();
        process.exitCode = undefined;
    });
    it("should remove project from database", async () => {
        const projectId = "550e8400-e29b-41d4-a716-446655440000";
        mockGetProject.mockReturnValue({
            project_id: projectId,
            name: "my-project",
            path: "/home/user/my-project",
            created_at: "2026-01-01T00:00:00Z",
            last_opened_at: null,
        });
        const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
        await removeCommand(projectId);
        expect(mockDeleteProject).toHaveBeenCalledWith(projectId);
        expect(consoleSpy).toHaveBeenCalledWith("Project removed: my-project");
        consoleSpy.mockRestore();
    });
    it("should error for non-existent project", async () => {
        mockGetProject.mockReturnValue(null);
        const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
        await removeCommand("non-existent-id");
        expect(process.exitCode).toBe(1);
        expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Project not found"));
        expect(mockDeleteProject).not.toHaveBeenCalled();
        consoleSpy.mockRestore();
    });
    it("should re-send daemon_register after removing project", async () => {
        const projectId = "550e8400-e29b-41d4-a716-446655440000";
        mockGetProject.mockReturnValue({
            project_id: projectId,
            name: "my-project",
            path: "/home/user/my-project",
            created_at: "2026-01-01T00:00:00Z",
            last_opened_at: null,
        });
        const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
        await removeCommand(projectId);
        expect(mockDeleteProject).toHaveBeenCalledWith(projectId);
        expect(mockNotifyReRegister).toHaveBeenCalledOnce();
        consoleSpy.mockRestore();
    });
    it("should not re-register when remove fails", async () => {
        mockGetProject.mockReturnValue(null);
        const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
        await removeCommand("non-existent-id");
        expect(mockNotifyReRegister).not.toHaveBeenCalled();
        consoleSpy.mockRestore();
    });
});
describe("projects command", () => {
    beforeEach(() => {
        vi.clearAllMocks();
        process.exitCode = undefined;
    });
    it("should list all projects", async () => {
        mockListProjects.mockReturnValue([
            {
                project_id: "550e8400-e29b-41d4-a716-446655440000",
                name: "my-project",
                path: "/home/user/my-project",
                created_at: "2026-01-01T00:00:00Z",
                last_opened_at: null,
            },
            {
                project_id: "661f9511-f30c-52e5-b827-557766551111",
                name: "api",
                path: "/home/user/api",
                created_at: "2026-01-01T00:00:00Z",
                last_opened_at: null,
            },
        ]);
        const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
        await projectsCommand();
        // Header + 2 rows = 3 calls
        expect(consoleSpy).toHaveBeenCalledTimes(3);
        // Check header contains column names
        const headerCall = consoleSpy.mock.calls[0][0];
        expect(headerCall).toContain("ID");
        expect(headerCall).toContain("NAME");
        expect(headerCall).toContain("PATH");
        expect(headerCall).toContain("STATUS");
        // Check project rows contain project data
        const row1 = consoleSpy.mock.calls[1][0];
        expect(row1).toContain("550e8400-e29b-41d4-a716-446655440000");
        expect(row1).toContain("my-project");
        expect(row1).toContain("/home/user/my-project");
        const row2 = consoleSpy.mock.calls[2][0];
        expect(row2).toContain("661f9511-f30c-52e5-b827-557766551111");
        expect(row2).toContain("api");
        expect(row2).toContain("/home/user/api");
        consoleSpy.mockRestore();
    });
    it("should show message when no projects registered", async () => {
        mockListProjects.mockReturnValue([]);
        const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
        await projectsCommand();
        expect(consoleSpy).toHaveBeenCalledWith("No projects registered");
        consoleSpy.mockRestore();
    });
});
//# sourceMappingURL=cli-projects.test.js.map