import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { execFileSync } from "node:child_process";
import { describe, expect, it } from "vitest";
const testDir = dirname(fileURLToPath(import.meta.url));
const cliDir = resolve(testDir, "..", "..");
const workspaceRoot = resolve(cliDir, "..", "..");
const studioDir = resolve(workspaceRoot, "packages", "studio");
const CLI_PACK_TEST_TIMEOUT_MS = 30_000;
const STUDIO_PACK_TEST_TIMEOUT_MS = 120_000;
const sourceCliPackageJsonPromise = readFile(resolve(cliDir, "package.json"), "utf-8").then((raw) => JSON.parse(raw));
const sourceStudioPackageJsonPromise = readFile(resolve(studioDir, "package.json"), "utf-8").then((raw) => JSON.parse(raw));
function tarForceLocalArgs() {
    if (process.platform !== "win32")
        return [];
    try {
        const version = execFileSync("tar", ["--version"], {
            encoding: "utf-8",
            stdio: ["ignore", "pipe", "ignore"],
        });
        return version.includes("GNU tar") ? ["--force-local"] : [];
    }
    catch {
        return [];
    }
}
async function packPackage(packageDir, packDir) {
    const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
    execFileSync(npmCmd, ["pack", "--pack-destination", packDir], {
        cwd: packageDir,
        env: process.env,
        encoding: "utf-8",
        shell: process.platform === "win32",
    });
    const tgzFiles = (await readdir(packDir)).filter((name) => name.endsWith(".tgz"));
    if (tgzFiles.length !== 1) {
        throw new Error(`Expected exactly one tarball in ${packDir}, found ${tgzFiles.length}`);
    }
    return join(packDir, tgzFiles[0]);
}
async function extractPackedPackageJson(packageDir, packDir) {
    const tarballPath = await packPackage(packageDir, packDir);
    const tarArgs = [...tarForceLocalArgs(), "-xOf"];
    return execFileSync("tar", [...tarArgs, tarballPath, "package/package.json"], {
        cwd: workspaceRoot,
        encoding: "utf-8",
    });
}
describe.sequential("publish packaging", () => {
    it("rewrites workspace package versions for canary publishing", async () => {
        const tempRoot = await mkdtemp(join(tmpdir(), "inkos-version-script-"));
        const tempPackagesDir = join(tempRoot, "packages");
        const tempCoreDir = join(tempPackagesDir, "core");
        const tempCliDir = join(tempPackagesDir, "cli");
        try {
            await mkdir(tempCoreDir, { recursive: true });
            await mkdir(tempCliDir, { recursive: true });
            await writeFile(join(tempRoot, "package.json"), `${JSON.stringify({ name: "inkos", version: "0.4.6" }, null, 2)}\n`);
            await writeFile(join(tempCoreDir, "package.json"), `${JSON.stringify({ name: "@actalk/inkos-core", version: "0.4.6" }, null, 2)}\n`);
            await writeFile(join(tempCliDir, "package.json"), `${JSON.stringify({
                name: "@actalk/inkos",
                version: "0.4.6",
                dependencies: {
                    "@actalk/inkos-core": "workspace:*",
                    commander: "^13.0.0",
                },
            }, null, 2)}\n`);
            execFileSync("node", [resolve(workspaceRoot, "scripts/set-package-versions.mjs"), "0.4.8-canary.7", "--root", tempRoot], {
                cwd: workspaceRoot,
                env: process.env,
                encoding: "utf-8",
            });
            const rootPackageJson = JSON.parse(await readFile(join(tempRoot, "package.json"), "utf-8"));
            const corePackageJson = JSON.parse(await readFile(join(tempCoreDir, "package.json"), "utf-8"));
            const cliPackageJson = JSON.parse(await readFile(join(tempCliDir, "package.json"), "utf-8"));
            expect(rootPackageJson.version).toBe("0.4.8-canary.7");
            expect(corePackageJson.version).toBe("0.4.8-canary.7");
            expect(cliPackageJson.version).toBe("0.4.8-canary.7");
            expect(cliPackageJson.dependencies["@actalk/inkos-core"]).toBe("0.4.8-canary.7");
        }
        finally {
            await rm(tempRoot, { recursive: true, force: true });
        }
    });
    it("keeps source CLI dependencies linked through the workspace protocol", async () => {
        const cliPackageJson = await sourceCliPackageJsonPromise;
        expect(cliPackageJson.dependencies["@actalk/inkos-core"]).toBe("workspace:*");
        expect(cliPackageJson.dependencies["@actalk/inkos-studio"]).toBe("workspace:*");
    });
    it("verifies publishable manifests before npm publish runs", async () => {
        const cliPackageJson = await sourceCliPackageJsonPromise;
        const corePackageJson = JSON.parse(await readFile(resolve(workspaceRoot, "packages/core/package.json"), "utf-8"));
        expect(cliPackageJson.scripts.prepublishOnly).toBe("node ../../scripts/verify-no-workspace-protocol.mjs .");
        expect(corePackageJson.scripts.prepublishOnly).toBe("node ../../scripts/verify-no-workspace-protocol.mjs .");
    });
    it("allows source workspace protocol manifests when they normalize cleanly for publish", async () => {
        const tempRoot = await mkdtemp(join(tmpdir(), "inkos-publish-verify-pass-"));
        const tempPackagesDir = join(tempRoot, "packages");
        const tempCoreDir = join(tempPackagesDir, "core");
        const tempCliDir = join(tempPackagesDir, "cli");
        try {
            await mkdir(tempCoreDir, { recursive: true });
            await mkdir(tempCliDir, { recursive: true });
            await writeFile(join(tempRoot, "package.json"), `${JSON.stringify({ name: "inkos", version: "0.5.1" }, null, 2)}\n`);
            await writeFile(join(tempCoreDir, "package.json"), `${JSON.stringify({ name: "@actalk/inkos-core", version: "0.5.1" }, null, 2)}\n`);
            await writeFile(join(tempCliDir, "package.json"), `${JSON.stringify({
                name: "@actalk/inkos",
                version: "0.5.1",
                dependencies: {
                    "@actalk/inkos-core": "workspace:*",
                    commander: "^13.0.0",
                },
            }, null, 2)}\n`);
            expect(() => execFileSync("node", [resolve(workspaceRoot, "scripts/verify-no-workspace-protocol.mjs"), "packages/core", "packages/cli"], {
                cwd: tempRoot,
                env: process.env,
                encoding: "utf-8",
                stdio: "pipe",
            })).not.toThrow();
        }
        finally {
            await rm(tempRoot, { recursive: true, force: true });
        }
    });
    it("rejects workspace protocol manifests that normalize to the wrong internal version", async () => {
        const tempRoot = await mkdtemp(join(tmpdir(), "inkos-publish-verify-fail-"));
        const tempPackagesDir = join(tempRoot, "packages");
        const tempCoreDir = join(tempPackagesDir, "core");
        const tempCliDir = join(tempPackagesDir, "cli");
        try {
            await mkdir(tempCoreDir, { recursive: true });
            await mkdir(tempCliDir, { recursive: true });
            await writeFile(join(tempRoot, "package.json"), `${JSON.stringify({ name: "inkos", version: "0.5.1" }, null, 2)}\n`);
            await writeFile(join(tempCoreDir, "package.json"), `${JSON.stringify({ name: "@actalk/inkos-core", version: "0.5.1" }, null, 2)}\n`);
            await writeFile(join(tempCliDir, "package.json"), `${JSON.stringify({
                name: "@actalk/inkos",
                version: "0.5.1",
                dependencies: {
                    "@actalk/inkos-core": "workspace:0.5.0",
                },
            }, null, 2)}\n`);
            expect(() => execFileSync("node", [resolve(workspaceRoot, "scripts/verify-no-workspace-protocol.mjs"), "packages/cli"], {
                cwd: tempRoot,
                env: process.env,
                encoding: "utf-8",
                stdio: "pipe",
            })).toThrow(/normalizes to 0\.5\.0, expected 0\.5\.1/);
        }
        finally {
            await rm(tempRoot, { recursive: true, force: true });
        }
    });
    it("replaces workspace dependencies before npm pack", { timeout: CLI_PACK_TEST_TIMEOUT_MS }, async () => {
        const packDir = await mkdtemp(join(tmpdir(), "inkos-cli-pack-"));
        try {
            const packedPackageJson = JSON.parse(await extractPackedPackageJson(cliDir, packDir));
            const corePackageJson = JSON.parse(await readFile(resolve(workspaceRoot, "packages/core/package.json"), "utf-8"));
            const studioPackageJson = await sourceStudioPackageJsonPromise;
            expect(packedPackageJson.dependencies["@actalk/inkos-core"]).toBe(corePackageJson.version);
            expect(packedPackageJson.dependencies["@actalk/inkos-studio"]).toBe(studioPackageJson.version);
        }
        finally {
            await rm(packDir, { recursive: true, force: true });
        }
    });
    it("packs the studio runtime entry alongside the built frontend", { timeout: STUDIO_PACK_TEST_TIMEOUT_MS }, async () => {
        const packDir = await mkdtemp(join(tmpdir(), "inkos-studio-pack-"));
        try {
            const tarballPath = await packPackage(studioDir, packDir);
            const tarArgs = [...tarForceLocalArgs(), "-tf"];
            const archiveListing = execFileSync("tar", [...tarArgs, tarballPath], {
                cwd: workspaceRoot,
                encoding: "utf-8",
            });
            expect(archiveListing).toContain("package/dist/index.html");
            expect(archiveListing).toContain("package/dist/api/index.js");
        }
        finally {
            await rm(packDir, { recursive: true, force: true });
        }
    });
});
//# sourceMappingURL=publish-package.test.js.map