import { test, expect, vi } from "vitest";
import { saveProjectToDirectory } from "./saveProjectToDirectory.js";
import { Volume } from "memfs";
import { loadProjectInMemory } from "./loadProjectInMemory.js";
import { newProject } from "./newProject.js";
import { loadProjectFromDirectory } from "./loadProjectFromDirectory.js";
import { selectBundleNested } from "../query-utilities/selectBundleNested.js";
test("it should throw if the path doesn't end with .inlang", async () => {
    await expect(() => saveProjectToDirectory({
        fs: {},
        project: {},
        path: "/foo/bar",
    })).rejects.toThrowError("The path must end with .inlang");
});
test("it should overwrite all files to the directory except the db.sqlite file", async () => {
    const mockFs = Volume.fromJSON({
        "/foo/bar.inlang/settings.json": JSON.stringify({
            baseLocale: "en",
            locales: ["en"],
        }),
    }).promises;
    const project = await loadProjectInMemory({
        blob: await newProject({
            settings: {
                baseLocale: "en",
                locales: ["en", "fr", "mock"],
            },
        }),
    });
    await saveProjectToDirectory({
        fs: mockFs,
        project,
        path: "/foo/bar.inlang",
    });
    const files = await mockFs.readdir("/foo/bar.inlang");
    const updatedSettingsFile = await mockFs.readFile("/foo/bar.inlang/settings.json", "utf-8");
    const updatedSettings = JSON.parse(updatedSettingsFile);
    // only testing known files at the time of the test.
    // this test should be updated for files that should NOT
    // be contained in the directory in the future
    expect(files).toContain("settings.json");
    expect(files).not.toContain("db.sqlite");
    expect(updatedSettings.baseLocale).toBe("en");
    expect(updatedSettings.locales).toEqual(["en", "fr", "mock"]);
});
test("a roundtrip should work", async () => {
    const bundles = [{ id: "mock-bundle", declarations: [] }];
    const messages = [{ bundleId: "mock-bundle", locale: "en" }];
    const variants = [];
    const volume = Volume.fromJSON({
        "/mock-file.json": JSON.stringify({ bundles, messages, variants }),
    });
    const mockPlugin = {
        key: "mock-plugin",
        toBeImportedFiles: async () => {
            return [{ path: "/mock-file.json", locale: "mock" }];
        },
        importFiles: async ({ files }) => {
            const { bundles, messages, variants } = JSON.parse(new TextDecoder().decode(files[0]?.content));
            return { bundles, messages, variants };
        },
        exportFiles: async ({ bundles }) => {
            return [
                {
                    content: new TextEncoder().encode(JSON.stringify(bundles)),
                    name: "mock-file.json",
                    locale: "mock",
                },
            ];
        },
    };
    const exportFilesSpy = vi.spyOn(mockPlugin, "exportFiles");
    const importFilesSpy = vi.spyOn(mockPlugin, "importFiles");
    const project = await loadProjectInMemory({
        blob: await newProject(),
        providePlugins: [mockPlugin],
    });
    await project.db.insertInto("bundle").values(bundles).execute();
    await project.db.insertInto("message").values(messages).execute();
    await saveProjectToDirectory({
        fs: volume.promises,
        project,
        path: "/foo/bar.inlang",
    });
    // const fileTree = volume.toJSON();
    expect(exportFilesSpy).toHaveBeenCalled();
    expect(importFilesSpy).not.toHaveBeenCalled();
    // TODO deactivated since mockBundleNested no longer contains the id of the messages
    // expect(fileTree).toEqual(
    // 	expect.objectContaining({
    // 		"/foo/mock-file.json": JSON.stringify([mockBundleNested]),
    // 	})
    // );
    // testing roundtrip
    const project2 = await loadProjectFromDirectory({
        fs: volume,
        path: "/foo/bar.inlang",
        providePlugins: [mockPlugin],
    });
    expect(mockPlugin.importFiles).toHaveBeenCalled();
    const bundlesAfter = await project2.db
        .selectFrom("bundle")
        .selectAll()
        .execute();
    const messagesAfter = await project2.db
        .selectFrom("message")
        .selectAll()
        .execute();
    const variantsAfter = await project2.db
        .selectFrom("variant")
        .selectAll()
        .execute();
    expect(bundlesAfter).lengthOf(1);
    expect(messagesAfter).lengthOf(1);
    expect(variantsAfter).lengthOf(0);
    expect(bundlesAfter[0]).toStrictEqual(expect.objectContaining(bundles[0]));
    expect(messagesAfter[0]).toStrictEqual(expect.objectContaining(messagesAfter[0]));
});
test.todo("a roundtrip with legacy load and save messages should work", async () => {
    const mockMessageV1 = {
        id: "mock-legacy-message",
        alias: {},
        selectors: [],
        variants: [
            {
                languageTag: "en",
                match: [],
                pattern: [{ type: "Text", value: "Hello from legacy message" }],
            },
        ],
    };
    const volume = Volume.fromJSON({
        "/foo/bar.inlang/settings.json": JSON.stringify({
            baseLocale: "en",
            locales: ["en"],
        }),
        "/foo/i18n/en.json": JSON.stringify([mockMessageV1]),
    });
    const mockPlugin = {
        id: "mock-legacy-plugin",
        key: "mock-legacy-plugin",
        loadMessages: async ({ nodeishFs }) => {
            // expecting `loadMessages` to transform the relative path
            // to an absolute path `./i18n/en.json` -> `/foo/i18n/en.json`
            const file = await nodeishFs.readFile("./i18n/en.json", {
                encoding: "utf-8",
            });
            return JSON.parse(file);
        },
        saveMessages: async ({ messages, nodeishFs }) => {
            await nodeishFs.writeFile("./i18n/en.json", new TextEncoder().encode(JSON.stringify(messages))
                .buffer);
        },
    };
    const loadMessagesSpy = vi.spyOn(mockPlugin, "loadMessages");
    const saveMessagesSpy = vi.spyOn(mockPlugin, "saveMessages");
    const project = await loadProjectFromDirectory({
        fs: volume,
        path: "/foo/bar.inlang",
        providePlugins: [mockPlugin],
    });
    expect(loadMessagesSpy).toHaveBeenCalled();
    expect(saveMessagesSpy).not.toHaveBeenCalled();
    const bundles1 = await selectBundleNested(project.db).execute();
    expect(bundles1[0]?.messages).lengthOf(1);
    expect(bundles1[0]?.messages[0]?.variants).toEqual([
        expect.objectContaining({
            pattern: [
                {
                    type: "text",
                    value: "Hello from legacy message",
                },
            ],
        }),
    ]);
    // await project.db
    // 	.updateTable("variant")
    // 	.set({
    // 		pattern: [{ type: "text", value: "Updated message" }],
    // 	})
    // 	.where("id", "=", bundles1[0]?.messages[0]?.variants[0]?.id as string)
    // 	.execute();
    // testing the saveMessages function by removing the en.json file
    await volume.promises.rm("/foo/i18n/en.json");
    await saveProjectToDirectory({
        fs: volume.promises,
        project,
        path: "/foo/bar.inlang",
    });
    expect(saveMessagesSpy).toHaveBeenCalled();
    const fileTree = volume.toJSON();
    const parsed = JSON.parse(fileTree["/foo/i18n/en.json"]);
    expect(parsed).toEqual(expect.objectContaining([mockMessageV1]));
    // testing roundtrip
    const project2 = await loadProjectFromDirectory({
        fs: volume,
        path: "/foo/bar.inlang",
        providePlugins: [mockPlugin],
    });
    const bundles2 = await selectBundleNested(project2.db).execute();
    // TODO deactivated since the ids must not be equal for separate imports - matching happens on language and matcher now
    expect(bundles1).toStrictEqual(bundles2);
});
test("it should preserve the formatting of existing json resource files", async () => {
    const mockJson = JSON.stringify({ key: "value" }, undefined, 
    // tab spacing
    "\t") +
        // ends with new line
        "\n";
    const mockPlugin = {
        key: "mock",
        exportFiles: async () => {
            return [
                {
                    name: "en.json",
                    // no beautified json
                    content: new TextEncoder().encode(JSON.stringify({ key: "value" })),
                    locale: "en",
                },
            ];
        },
    };
    const volume = Volume.fromJSON({
        "/foo/project.inlang/settings.json": JSON.stringify({
            baseLocale: "en",
            locales: ["en"],
        }),
        "/foo/en.json": mockJson,
    });
    const project = await loadProjectInMemory({
        blob: await newProject(),
        providePlugins: [mockPlugin],
    });
    await saveProjectToDirectory({
        path: "/foo/project.inlang",
        fs: volume.promises,
        project,
    });
    const fileAfterSave = await volume.promises.readFile("/foo/en.json", "utf-8");
    expect(fileAfterSave).toBe(mockJson);
});
test("adds a gitignore file if it doesn't exist", async () => {
    const fs = Volume.fromJSON({});
    const project = await loadProjectInMemory({
        blob: await newProject(),
    });
    await saveProjectToDirectory({
        fs: fs.promises,
        project,
        path: "/foo/bar.inlang",
    });
    const gitignore = await fs.promises.readFile("/foo/bar.inlang/.gitignore", "utf-8");
    expect(gitignore).toBe("cache");
});
test("uses exportFiles when both exportFiles and saveMessages are defined", async () => {
    const exportFilesSpy = vi.fn().mockResolvedValue([]);
    const saveMessagesSpy = vi.fn();
    const mockPlugin = {
        key: "mock",
        exportFiles: exportFilesSpy,
        saveMessages: saveMessagesSpy,
    };
    const volume = Volume.fromJSON({});
    const project = await loadProjectInMemory({
        blob: await newProject(),
        providePlugins: [mockPlugin],
    });
    await saveProjectToDirectory({
        path: "/foo/project.inlang",
        fs: volume.promises,
        project,
    });
    expect(exportFilesSpy).toHaveBeenCalled();
    expect(saveMessagesSpy).not.toHaveBeenCalled();
});
test("uses saveMessages when exportFiles is not defined", async () => {
    const saveMessagesSpy = vi.fn().mockResolvedValue([]);
    const mockPlugin = {
        key: "mock",
        saveMessages: saveMessagesSpy,
    };
    const volume = Volume.fromJSON({});
    const project = await loadProjectInMemory({
        blob: await newProject(),
        providePlugins: [mockPlugin],
    });
    await saveProjectToDirectory({
        path: "/foo/project.inlang",
        fs: volume.promises,
        project,
    });
    expect(saveMessagesSpy).toHaveBeenCalled();
});
//# sourceMappingURL=saveProjectToDirectory.test.js.map