import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, it } from "node:test";
import { assert } from "chai";
import { parse as dotenvParse } from "dotenv";
import { MockAgent, setGlobalDispatcher } from "undici";
const TEST_ENVIRONMENT = "test-env-123";
const TEST_AUTH_TOKEN = "test-auth-token-456";
const TEST_API_ENDPOINT = "https://dev.zuplo.com";
describe("pullSystemConfig", () => {
    let mockAgent;
    let testDir;
    beforeEach(async () => {
        mockAgent = new MockAgent();
        mockAgent.disableNetConnect();
        setGlobalDispatcher(mockAgent);
        testDir = await mkdtemp(join(tmpdir(), "zuplo-test-"));
    });
    afterEach(async () => {
        await rm(testDir, { recursive: true, force: true });
    });
    it("should write account and project name to .env.zuplo", async () => {
        const mockPayload = {
            accountName: "test-account",
            projectName: "test-project",
            environmentType: "development",
            systemConfigurations: "config-123",
        };
        mockAgent
            .get(TEST_API_ENDPOINT)
            .intercept({
            path: `/v1/environments/${TEST_ENVIRONMENT}/configurations`,
            method: "GET",
            headers: {
                authorization: `Bearer ${TEST_AUTH_TOKEN}`,
            },
        })
            .reply(200, mockPayload);
        const { pullSystemConfig } = await import("../common/populate.js");
        await pullSystemConfig({
            dir: testDir,
            environment: TEST_ENVIRONMENT,
            authToken: TEST_AUTH_TOKEN,
        });
        const content = await readFile(join(testDir, ".env.zuplo"), "utf-8");
        assert.include(content, "ZUPLO_ACCOUNT_NAME=test-account");
        assert.include(content, "ZUPLO_PROJECT_NAME=test-project");
        assert.include(content, "ZUPLO_ENVIRONMENT_TYPE=development");
        assert.include(content, "ZUPLO_SYSTEM_CONFIGURATIONS=config-123");
        assert.notInclude(content, "ZUPLO_ENVIRONMENT_NAME");
    });
});
describe("pullLocalConfig", () => {
    let mockAgent;
    let testDir;
    beforeEach(async () => {
        mockAgent = new MockAgent();
        mockAgent.disableNetConnect();
        setGlobalDispatcher(mockAgent);
        testDir = await mkdtemp(join(tmpdir(), "zuplo-test-"));
    });
    it("should generate correct .env.zuplo file with system variables", async () => {
        const mockPayload = {
            accountName: "test-account",
            projectName: "test-project",
            environmentType: "development",
            systemConfigurations: "config-123",
        };
        mockAgent
            .get(TEST_API_ENDPOINT)
            .intercept({
            path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
            method: "GET",
            headers: {
                authorization: `Bearer ${TEST_AUTH_TOKEN}`,
            },
        })
            .reply(200, mockPayload);
        const { pullLocalConfig } = await import("../common/populate.js");
        await pullLocalConfig({
            dir: testDir,
            environment: TEST_ENVIRONMENT,
            authToken: TEST_AUTH_TOKEN,
        });
        const envFilePath = join(testDir, ".env.zuplo");
        const content = await readFile(envFilePath, "utf-8");
        assert.include(content, "# This file is auto-generated from zuplo link. Please do not edit it manually.");
        assert.include(content, "# It will be auto-generated afresh the next time you run zuplo link.");
        assert.include(content, "# If you wish to add your own environment variables, create a separate .env file.");
        assert.include(content, "ZUPLO_ACCOUNT_NAME=test-account");
        assert.include(content, "ZUPLO_PROJECT_NAME=test-project");
        assert.include(content, "ZUPLO_ENVIRONMENT_TYPE=development");
        assert.include(content, "ZUPLO_SYSTEM_CONFIGURATIONS=config-123");
        assert.notInclude(content, "ZUPLO_ENVIRONMENT_NAME");
        assert.notInclude(content, "# Public Zuplo environment variables");
        assert.notInclude(content, "# Environment variables defined in the Zuplo UI for the environment");
        await rm(testDir, { recursive: true, force: true });
    });
    it("should handle payload with public and user environment variables", async () => {
        const mockPayload = {
            accountName: "my-account",
            projectName: "my-project",
            environmentType: "production",
            systemConfigurations: "prod-config-456",
            ZUPLO_PUBLIC_API_URL: "https://api.example.com",
            ZUPLO_PUBLIC_DOCS_URL: "https://docs.example.com",
            DATABASE_URL: "postgres://localhost:5432/db",
            API_KEY: "secret-key-123",
            REDIS_URL: "redis://localhost:6379",
        };
        mockAgent
            .get(TEST_API_ENDPOINT)
            .intercept({
            path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
            method: "GET",
            headers: {
                authorization: `Bearer ${TEST_AUTH_TOKEN}`,
            },
        })
            .reply(200, mockPayload);
        const { pullLocalConfig } = await import("../common/populate.js");
        await pullLocalConfig({
            dir: testDir,
            environment: TEST_ENVIRONMENT,
            authToken: TEST_AUTH_TOKEN,
        });
        const envFilePath = join(testDir, ".env.zuplo");
        const content = await readFile(envFilePath, "utf-8");
        assert.include(content, "ZUPLO_ACCOUNT_NAME=my-account");
        assert.include(content, "ZUPLO_PROJECT_NAME=my-project");
        assert.include(content, "ZUPLO_ENVIRONMENT_TYPE=production");
        assert.include(content, "ZUPLO_SYSTEM_CONFIGURATIONS=prod-config-456");
        const publicSectionRegex = /# Public Zuplo environment variables[\s\S]*?(?=# Environment variables defined in the Zuplo UI|$)/;
        const publicSection = content.match(publicSectionRegex)?.[0] || "";
        assert.include(publicSection, 'ZUPLO_PUBLIC_API_URL="https://api.example.com"');
        assert.include(publicSection, 'ZUPLO_PUBLIC_DOCS_URL="https://docs.example.com"');
        const userSectionRegex = /# Environment variables defined in the Zuplo UI[\s\S]*$/;
        const userSection = content.match(userSectionRegex)?.[0] || "";
        assert.include(userSection, 'DATABASE_URL="postgres://localhost:5432/db"');
        assert.include(userSection, 'API_KEY="secret-key-123"');
        assert.include(userSection, 'REDIS_URL="redis://localhost:6379"');
        assert.notInclude(userSection, "accountName=");
        assert.notInclude(userSection, "projectName=");
        assert.notInclude(userSection, "environmentType=");
        assert.notInclude(userSection, "systemConfigurations=");
        await rm(testDir, { recursive: true, force: true });
    });
    it("should handle empty environment variables gracefully", async () => {
        const mockPayload = {
            accountName: "test-account",
            projectName: "test-project",
            environmentType: "development",
            systemConfigurations: "config-789",
        };
        mockAgent
            .get(TEST_API_ENDPOINT)
            .intercept({
            path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
            method: "GET",
            headers: {
                authorization: `Bearer ${TEST_AUTH_TOKEN}`,
            },
        })
            .reply(200, mockPayload);
        const { pullLocalConfig } = await import("../common/populate.js");
        await pullLocalConfig({
            dir: testDir,
            environment: TEST_ENVIRONMENT,
            authToken: TEST_AUTH_TOKEN,
        });
        const envFilePath = join(testDir, ".env.zuplo");
        const content = await readFile(envFilePath, "utf-8");
        assert.include(content, "ZUPLO_ACCOUNT_NAME=test-account");
        assert.include(content, "ZUPLO_PROJECT_NAME=test-project");
        assert.include(content, "ZUPLO_ENVIRONMENT_TYPE=development");
        assert.include(content, "ZUPLO_SYSTEM_CONFIGURATIONS=config-789");
        assert.notInclude(content, "# Public Zuplo environment variables");
        assert.notInclude(content, "# Environment variables defined in the Zuplo UI for the environment");
        await rm(testDir, { recursive: true, force: true });
    });
    it("should handle special characters in environment variable values", async () => {
        const mockPayload = {
            accountName: "test-account",
            projectName: "test-project",
            environmentType: "development",
            systemConfigurations: "config-123",
            DATABASE_URL: "postgres://user:p@ssw0rd!@localhost:5432/db?ssl=true",
            API_SECRET: "secret-with-special-chars!@#$%^&*()",
            JSON_CONFIG: '{"key": "value", "nested": {"array": [1, 2, 3]}}',
        };
        mockAgent
            .get(TEST_API_ENDPOINT)
            .intercept({
            path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
            method: "GET",
            headers: {
                authorization: `Bearer ${TEST_AUTH_TOKEN}`,
            },
        })
            .reply(200, mockPayload);
        const { pullLocalConfig } = await import("../common/populate.js");
        await pullLocalConfig({
            dir: testDir,
            environment: TEST_ENVIRONMENT,
            authToken: TEST_AUTH_TOKEN,
        });
        const envFilePath = join(testDir, ".env.zuplo");
        const content = await readFile(envFilePath, "utf-8");
        assert.include(content, 'DATABASE_URL="postgres://user:p@ssw0rd!@localhost:5432/db?ssl=true"');
        assert.include(content, 'API_SECRET="secret-with-special-chars!@#$%^&*()"');
        assert.include(content, 'JSON_CONFIG="{\\"key\\": \\"value\\", \\"nested\\": {\\"array\\": [1, 2, 3]}}"');
        const userSectionRegex = /# Environment variables defined in the Zuplo UI[\s\S]*$/;
        const userSection = content.match(userSectionRegex)?.[0] || "";
        assert.include(userSection, 'DATABASE_URL="postgres://user:p@ssw0rd!@localhost:5432/db?ssl=true"');
        assert.include(userSection, 'API_SECRET="secret-with-special-chars!@#$%^&*()"');
        assert.include(userSection, 'JSON_CONFIG="{\\"key\\": \\"value\\", \\"nested\\": {\\"array\\": [1, 2, 3]}}"');
        await rm(testDir, { recursive: true, force: true });
    });
    it("should only include public variables section when public variables exist", async () => {
        const mockPayload = {
            accountName: "test-account",
            projectName: "test-project",
            environmentType: "development",
            systemConfigurations: "config-123",
            ZUPLO_PUBLIC_API_URL: "https://api.example.com",
            ZUPLO_PUBLIC_DOCS_URL: "https://docs.example.com",
        };
        mockAgent
            .get(TEST_API_ENDPOINT)
            .intercept({
            path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
            method: "GET",
            headers: {
                authorization: `Bearer ${TEST_AUTH_TOKEN}`,
            },
        })
            .reply(200, mockPayload);
        const { pullLocalConfig } = await import("../common/populate.js");
        await pullLocalConfig({
            dir: testDir,
            environment: TEST_ENVIRONMENT,
            authToken: TEST_AUTH_TOKEN,
        });
        const envFilePath = join(testDir, ".env.zuplo");
        const content = await readFile(envFilePath, "utf-8");
        assert.include(content, "ZUPLO_ACCOUNT_NAME=test-account");
        assert.include(content, "ZUPLO_PROJECT_NAME=test-project");
        assert.include(content, "ZUPLO_ENVIRONMENT_TYPE=development");
        assert.include(content, "ZUPLO_SYSTEM_CONFIGURATIONS=config-123");
        assert.include(content, "# Public Zuplo environment variables");
        assert.include(content, 'ZUPLO_PUBLIC_API_URL="https://api.example.com"');
        assert.include(content, 'ZUPLO_PUBLIC_DOCS_URL="https://docs.example.com"');
        assert.notInclude(content, "# Environment variables defined in the Zuplo UI for the environment");
        await rm(testDir, { recursive: true, force: true });
    });
    it("should only include user variables section when user variables exist", async () => {
        const mockPayload = {
            accountName: "test-account",
            projectName: "test-project",
            environmentType: "development",
            systemConfigurations: "config-123",
            DATABASE_URL: "postgres://localhost:5432/db",
            API_KEY: "secret-key-123",
        };
        mockAgent
            .get(TEST_API_ENDPOINT)
            .intercept({
            path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
            method: "GET",
            headers: {
                authorization: `Bearer ${TEST_AUTH_TOKEN}`,
            },
        })
            .reply(200, mockPayload);
        const { pullLocalConfig } = await import("../common/populate.js");
        await pullLocalConfig({
            dir: testDir,
            environment: TEST_ENVIRONMENT,
            authToken: TEST_AUTH_TOKEN,
        });
        const envFilePath = join(testDir, ".env.zuplo");
        const content = await readFile(envFilePath, "utf-8");
        assert.include(content, "ZUPLO_ACCOUNT_NAME=test-account");
        assert.include(content, "ZUPLO_PROJECT_NAME=test-project");
        assert.include(content, "ZUPLO_ENVIRONMENT_TYPE=development");
        assert.include(content, "ZUPLO_SYSTEM_CONFIGURATIONS=config-123");
        assert.notInclude(content, "# Public Zuplo environment variables");
        assert.include(content, "# Environment variables defined in the Zuplo UI for the environment");
        assert.include(content, 'DATABASE_URL="postgres://localhost:5432/db"');
        assert.include(content, 'API_KEY="secret-key-123"');
        await rm(testDir, { recursive: true, force: true });
    });
    it("should properly escape double quotes in environment variable values for dotenv parsing", async () => {
        const mockPayload = {
            accountName: "test-account",
            projectName: "test-project",
            environmentType: "development",
            systemConfigurations: "config-123",
            DATABASE_URL: "postgres://user:p'assw'ord@localhost:5432/db",
            API_KEY: "secret'with'quotes",
            JSON_CONFIG: '{"message": "Hello \'world\'", "nested": {"value": "test\'s"}}',
            ZUPLO_PUBLIC_MESSAGE: "Welcome to Zuplo's platform!",
            COMPLEX_VALUE: "?\\\"!##==A_special_chars'''",
            EDGE_CASE_1: "value with 'single' and \"double\" quotes",
            EDGE_CASE_2: "path\\to\\file's location",
            EDGE_CASE_3: "it's a $pecial @#$%^&*() value!",
            EDGE_CASE_4: "multi\nline\nwith'quotes",
            EDGE_CASE_5: "tabs\tand\tspaces and 'quotes'",
        };
        mockAgent
            .get(TEST_API_ENDPOINT)
            .intercept({
            path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
            method: "GET",
            headers: {
                authorization: `Bearer ${TEST_AUTH_TOKEN}`,
            },
        })
            .reply(200, mockPayload);
        const { pullLocalConfig } = await import("../common/populate.js");
        await pullLocalConfig({
            dir: testDir,
            environment: TEST_ENVIRONMENT,
            authToken: TEST_AUTH_TOKEN,
        });
        const envFilePath = join(testDir, ".env.zuplo");
        const content = await readFile(envFilePath, "utf-8");
        assert.include(content, "DATABASE_URL=\"postgres://user:p'assw'ord@localhost:5432/db\"");
        assert.include(content, "API_KEY=\"secret'with'quotes\"");
        assert.include(content, 'JSON_CONFIG="{\\"message\\": \\"Hello \'world\'\\", \\"nested\\": {\\"value\\": \\"test\'s\\"}}"');
        assert.include(content, 'ZUPLO_PUBLIC_MESSAGE="Welcome to Zuplo\'s platform!"');
        assert.include(content, `COMPLEX_VALUE="?\\\\"!##==A_special_chars'''"`);
        assert.include(content, 'EDGE_CASE_1="value with \'single\' and \\"double\\" quotes"');
        assert.include(content, 'EDGE_CASE_2="path\\to\\file\'s location"');
        assert.include(content, 'EDGE_CASE_3="it\'s a $pecial @#$%^&*() value!"');
        assert.include(content, 'EDGE_CASE_4="multi\nline\nwith\'quotes"');
        assert.include(content, "EDGE_CASE_5=\"tabs\tand\tspaces and 'quotes'\"");
        const fileContent = await readFile(envFilePath, "utf-8");
        const parsedEnv = dotenvParse(fileContent);
        assert.isUndefined(parsedEnv.error, "dotenv should parse the file without errors");
        assert.equal(parsedEnv.DATABASE_URL, mockPayload.DATABASE_URL);
        assert.equal(parsedEnv.API_KEY, mockPayload.API_KEY);
        assert.isString(parsedEnv.JSON_CONFIG);
        assert.include(parsedEnv.JSON_CONFIG, '\\"message\\"');
        assert.equal(parsedEnv.ZUPLO_PUBLIC_MESSAGE, mockPayload.ZUPLO_PUBLIC_MESSAGE);
        assert.isString(parsedEnv.COMPLEX_VALUE);
        assert.include(parsedEnv.COMPLEX_VALUE, "special_chars");
        assert.isString(parsedEnv.EDGE_CASE_1);
        assert.include(parsedEnv.EDGE_CASE_1, "single");
        assert.equal(parsedEnv.EDGE_CASE_2, mockPayload.EDGE_CASE_2);
        assert.equal(parsedEnv.EDGE_CASE_3, mockPayload.EDGE_CASE_3);
        assert.equal(parsedEnv.EDGE_CASE_4, mockPayload.EDGE_CASE_4);
        assert.equal(parsedEnv.EDGE_CASE_5, mockPayload.EDGE_CASE_5);
        await rm(testDir, { recursive: true, force: true });
    });
});
//# sourceMappingURL=populate.test.js.map