import nock from "nock";
import yargs from "yargs/yargs";
import deployCommand from "../../cmds/deploy.js";
import { cleanupTest, RequestCapture, setupAuthenticatedNock, setupTestEnvironment, TEST_ACCOUNT_NAME, TEST_API_BASE, TEST_AUTH_TOKEN, TEST_PROJECT_NAME, } from "./test-utils.js";
jest.mock("../../common/validators/file-system-validator.js", () => ({
    validDeployDirectoryValidator: {
        validate: jest.fn().mockResolvedValue(true),
    },
}));
jest.mock("../../deploy/archive.js", () => ({
    archive: jest.fn().mockResolvedValue({
        tarball: "/tmp/test-archive.tar.gz",
        metadata: {
            branch: "main",
            sha: "abc123",
            repoUrl: "https://github.com/user/repo.git",
        },
    }),
    generateMetadata: jest.fn().mockResolvedValue({
        branch: "main",
        sha: "abc123",
        repoUrl: "https://github.com/user/repo.git",
    }),
}));
jest.mock("../../deploy/file-upload.js", () => ({
    upload: jest.fn(async ({ uploadUrl }) => {
        const response = await fetch(uploadUrl, {
            method: "PUT",
            body: "mock-tarball-content",
        });
        return {
            ok: response.ok,
            status: response.status,
        };
    }),
}));
jest.mock("../../deploy/poll-deployment.js", () => ({
    pollDeployment: jest.fn().mockResolvedValue({
        url: "https://test-project-main.zuplo.app",
        logUrl: "https://portal.zuplo.com/logs/deployment-123",
    }),
    pollBuild: jest.fn().mockResolvedValue({
        conditionType: "Complete",
    }),
}));
jest.mock("../../common/populate.js", () => ({
    pullSystemConfig: jest.fn().mockResolvedValue(undefined),
    pullLocalConfig: jest.fn().mockResolvedValue(undefined),
}));
jest.mock("../../deploy/environments.js", () => ({
    retrieveOrCreateEnvironment: jest.fn().mockResolvedValue({
        name: "development",
        type: "development",
    }),
    UnableToAutoLinkToExistingProject: class extends Error {
    },
    isProductionBranchName: jest.fn().mockReturnValue(false),
}));
jest.mock("node:fs", () => ({
    existsSync: jest.fn().mockReturnValue(false),
    readFileSync: jest.fn((filePath) => {
        if (filePath.endsWith("tls.crt")) {
            return "mock-cert-content";
        }
        if (filePath.endsWith("tls.key")) {
            return "mock-key-content";
        }
        return Buffer.from("mock-tarball-content");
    }),
    readdirSync: jest.fn().mockReturnValue([]),
    writeFileSync: jest.fn(),
}));
async function executeDeployCommand(args) {
    const yargsInstance = yargs([])
        .command(deployCommand)
        .help(false)
        .version(false)
        .exitProcess(false);
    const commandArgs = ["deploy"];
    if (args.account) {
        commandArgs.push("--account", args.account);
    }
    if (args.project) {
        commandArgs.push("--project", args.project);
    }
    if (args["api-key"]) {
        commandArgs.push("--api-key", args["api-key"]);
    }
    if (args.environment) {
        commandArgs.push("--environment", args.environment);
    }
    if (args["self-hosted-endpoint"]) {
        commandArgs.push("--self-hosted-endpoint", args["self-hosted-endpoint"]);
    }
    if (args["mtls-certificates-dir"]) {
        commandArgs.push("--mtls-certificates-dir", args["mtls-certificates-dir"]);
    }
    if (args.dir) {
        commandArgs.push("--dir", args.dir);
    }
    return await yargsInstance.parse(commandArgs);
}
const mockPrintResult = jest.mocked(require("../../common/output.js").printResultToConsoleAndExitGracefully);
const mockReadFileSync = jest.mocked(require("node:fs").readFileSync);
const mockReaddirSync = jest.mocked(require("node:fs").readdirSync);
const mockExistsSync = jest.mocked(require("node:fs").existsSync);
describe("Deploy Command Integration Tests", () => {
    let requestCapture;
    beforeEach(() => {
        setupTestEnvironment();
        requestCapture = new RequestCapture();
        nock.disableNetConnect();
        jest.clearAllMocks();
        mockExistsSync.mockReturnValue(false);
        mockReaddirSync.mockReturnValue([]);
        mockReadFileSync.mockImplementation((filePath) => {
            if (filePath.endsWith("tls.crt")) {
                return "mock-cert-content";
            }
            if (filePath.endsWith("tls.key")) {
                return "mock-key-content";
            }
            return Buffer.from("mock-tarball-content");
        });
    });
    afterEach(() => {
        cleanupTest();
        requestCapture.clear();
    });
    describe("SaaS deployment", () => {
        it("should intercept POST request to get source upload URL", async () => {
            const mockUploadUrlResponse = {
                uploadUrl: "https://storage.example.com/upload/abc123",
                deploymentId: "deployment-456",
            };
            const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
                .post("/v1/deployments/source-url")
                .reply(200, mockUploadUrlResponse);
            const uploadScope = nock("https://storage.example.com")
                .put("/upload/abc123")
                .reply(200, { message: "Upload successful" });
            [scope, uploadScope].forEach((s) => {
                s.on("request", (req, interceptor, body) => {
                    requestCapture.capture(req, interceptor, body);
                });
            });
            await executeDeployCommand({
                account: TEST_ACCOUNT_NAME,
                project: TEST_PROJECT_NAME,
                "api-key": TEST_AUTH_TOKEN,
                dir: ".",
            });
            expect(scope.isDone()).toBe(true);
            expect(uploadScope.isDone()).toBe(true);
            expect(mockPrintResult).toHaveBeenCalledWith(expect.stringContaining("Deployed to https://test-project-main.zuplo.app"), expect.any(Object));
            const capturedRequests = requestCapture.getRequests();
            expect(capturedRequests).toMatchSnapshot("deploy-saas-requests");
            const sourceUrlRequest = capturedRequests.find((req) => req.path === "/v1/deployments/source-url");
            expect(sourceUrlRequest?.method).toBe("POST");
            expect(sourceUrlRequest?.body).toEqual({
                accountName: TEST_ACCOUNT_NAME,
                projectName: TEST_PROJECT_NAME,
                environment: "main",
                repositoryUrl: "https://github.com/user/repo.git",
                sha: "abc123",
            });
        });
        it("should handle upload URL request failure", async () => {
            const errorResponse = {
                error: "Forbidden",
                message: "Insufficient permissions",
                statusCode: 403,
            };
            const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
                .post("/v1/deployments/source-url")
                .reply(403, errorResponse);
            scope.on("request", (req, interceptor, body) => {
                requestCapture.capture(req, interceptor, body);
            });
            await expect(executeDeployCommand({
                account: TEST_ACCOUNT_NAME,
                project: TEST_PROJECT_NAME,
                "api-key": TEST_AUTH_TOKEN,
                dir: ".",
            })).rejects.toThrow("Process would exit");
            expect(scope.isDone()).toBe(true);
            const capturedRequests = requestCapture.getRequests();
            expect(capturedRequests).toMatchSnapshot("deploy-saas-error-requests");
        });
    });
    describe("Self-hosted deployment", () => {
        it("should intercept POST request to self-hosted build endpoint", async () => {
            const selfHostedEndpoint = "https://my-zuplo.company.com";
            const mockBuildResponse = {
                buildName: "build-789",
            };
            const buildScope = setupAuthenticatedNock(nock(selfHostedEndpoint))
                .post("/v1/deployments/build")
                .reply(200, mockBuildResponse);
            const deploymentScope = setupAuthenticatedNock(nock(selfHostedEndpoint))
                .get("/v1/deployments/test-project-main")
                .reply(200, {
                deploymentUrl: "https://my-zuplo.company.com/test-project-main",
            });
            [buildScope, deploymentScope].forEach((scope) => {
                scope.on("request", (req, interceptor, body) => {
                    requestCapture.capture(req, interceptor, body);
                });
            });
            await executeDeployCommand({
                account: TEST_ACCOUNT_NAME,
                project: TEST_PROJECT_NAME,
                "api-key": TEST_AUTH_TOKEN,
                "self-hosted-endpoint": selfHostedEndpoint,
                dir: ".",
            });
            expect(buildScope.isDone()).toBe(true);
            expect(deploymentScope.isDone()).toBe(true);
            expect(mockPrintResult).toHaveBeenCalledWith(expect.stringContaining("Deployed to https://my-zuplo.company.com/test-project-main"), expect.any(Object));
            const capturedRequests = requestCapture.getRequests();
            expect(capturedRequests).toMatchSnapshot("deploy-self-hosted-requests");
            const buildRequest = capturedRequests.find((req) => req.path === "/v1/deployments/build");
            expect(buildRequest?.method).toBe("POST");
            expect(buildRequest?.body).toBeDefined();
        }, 60000);
        it("should handle self-hosted build failure", async () => {
            const selfHostedEndpoint = "https://my-zuplo.company.com";
            const errorResponse = {
                error: "Bad Request",
                message: "Invalid project configuration",
                statusCode: 400,
            };
            const scope = setupAuthenticatedNock(nock(selfHostedEndpoint))
                .post("/v1/deployments/build")
                .reply(400, errorResponse);
            scope.on("request", (req, interceptor, body) => {
                requestCapture.capture(req, interceptor, body);
            });
            await executeDeployCommand({
                account: TEST_ACCOUNT_NAME,
                project: TEST_PROJECT_NAME,
                "api-key": TEST_AUTH_TOKEN,
                "self-hosted-endpoint": selfHostedEndpoint,
                dir: ".",
            });
            expect(scope.isDone()).toBe(true);
            const capturedRequests = requestCapture.getRequests();
            expect(capturedRequests).toMatchSnapshot("deploy-self-hosted-error-requests");
        }, 60000);
        it("should include mtls certificates from mtls-certificates-dir", async () => {
            const selfHostedEndpoint = "https://my-zuplo.company.com";
            const mockBuildResponse = {
                buildName: "build-789",
            };
            let capturedBuildRequestBody = "";
            mockReaddirSync.mockReturnValue([
                {
                    name: "cert-name-2",
                    isDirectory: () => true,
                },
                {
                    name: "cert-name-1",
                    isDirectory: () => true,
                },
            ]);
            mockReadFileSync.mockImplementation((filePath) => {
                if (/cert-name-1[\\/]tls\.crt$/.test(filePath)) {
                    return "CERT_1";
                }
                if (/cert-name-1[\\/]tls\.key$/.test(filePath)) {
                    return "KEY_1";
                }
                if (/cert-name-2[\\/]tls\.crt$/.test(filePath)) {
                    return "CERT_2";
                }
                if (/cert-name-2[\\/]tls\.key$/.test(filePath)) {
                    return "KEY_2";
                }
                return Buffer.from("mock-tarball-content");
            });
            const buildScope = setupAuthenticatedNock(nock(selfHostedEndpoint))
                .post("/v1/deployments/build", (body) => {
                capturedBuildRequestBody = Buffer.isBuffer(body)
                    ? body.toString("utf-8")
                    : String(body);
                return true;
            })
                .reply(200, mockBuildResponse);
            const deploymentScope = setupAuthenticatedNock(nock(selfHostedEndpoint))
                .get("/v1/deployments/test-project-main")
                .reply(200, {
                deploymentUrl: "https://my-zuplo.company.com/test-project-main",
            });
            [buildScope, deploymentScope].forEach((scope) => {
                scope.on("request", (req, interceptor, body) => {
                    requestCapture.capture(req, interceptor, body);
                });
            });
            await executeDeployCommand({
                account: TEST_ACCOUNT_NAME,
                project: TEST_PROJECT_NAME,
                "api-key": TEST_AUTH_TOKEN,
                "self-hosted-endpoint": selfHostedEndpoint,
                "mtls-certificates-dir": "./mtls",
                dir: ".",
            });
            expect(buildScope.isDone()).toBe(true);
            expect(deploymentScope.isDone()).toBe(true);
            const capturedRequests = requestCapture.getRequests();
            const buildRequest = capturedRequests.find((req) => req.path === "/v1/deployments/build");
            expect(buildRequest?.method).toBe("POST");
            expect(capturedBuildRequestBody).toContain('name="mtlsCertificate"');
            expect((capturedBuildRequestBody.match(/name="mtlsCertificate"/g) || []).length).toBe(2);
            expect(capturedBuildRequestBody).toContain('"certName":"cert-name-1"');
            expect(capturedBuildRequestBody).toContain('"mtlsCrt":"CERT_1"');
            expect(capturedBuildRequestBody).toContain('"mtlsKey":"KEY_1"');
            expect(capturedBuildRequestBody).toContain('"certName":"cert-name-2"');
            expect(capturedBuildRequestBody).toContain('"mtlsCrt":"CERT_2"');
            expect(capturedBuildRequestBody).toContain('"mtlsKey":"KEY_2"');
            expect(capturedBuildRequestBody.indexOf('"certName":"cert-name-1"')).toBeLessThan(capturedBuildRequestBody.indexOf('"certName":"cert-name-2"'));
        }, 60000);
    });
    describe("Directory argument handling", () => {
        it("should work when --dir is not specified (uses default)", async () => {
            const mockUploadUrlResponse = {
                uploadUrl: "https://storage.example.com/upload/default-test",
                deploymentId: "deployment-default",
            };
            const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
                .post("/v1/deployments/source-url")
                .reply(200, mockUploadUrlResponse);
            const uploadScope = nock("https://storage.example.com")
                .put("/upload/default-test")
                .reply(200, { message: "Upload successful" });
            [scope, uploadScope].forEach((s) => {
                s.on("request", (req, interceptor, body) => {
                    requestCapture.capture(req, interceptor, body);
                });
            });
            await executeDeployCommand({
                account: TEST_ACCOUNT_NAME,
                project: TEST_PROJECT_NAME,
                "api-key": TEST_AUTH_TOKEN,
            });
            expect(scope.isDone()).toBe(true);
            expect(uploadScope.isDone()).toBe(true);
            expect(mockPrintResult).toHaveBeenCalledWith(expect.stringContaining("Deployed to https://test-project-main.zuplo.app"), expect.any(Object));
            const capturedRequests = requestCapture.getRequests();
            expect(capturedRequests).toMatchSnapshot("deploy-default-dir-requests");
        });
        it("should work with explicit --dir=.", async () => {
            const mockUploadUrlResponse = {
                uploadUrl: "https://storage.example.com/upload/explicit-dot",
                deploymentId: "deployment-dot",
            };
            const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
                .post("/v1/deployments/source-url")
                .reply(200, mockUploadUrlResponse);
            const uploadScope = nock("https://storage.example.com")
                .put("/upload/explicit-dot")
                .reply(200, { message: "Upload successful" });
            [scope, uploadScope].forEach((s) => {
                s.on("request", (req, interceptor, body) => {
                    requestCapture.capture(req, interceptor, body);
                });
            });
            await executeDeployCommand({
                account: TEST_ACCOUNT_NAME,
                project: TEST_PROJECT_NAME,
                "api-key": TEST_AUTH_TOKEN,
                dir: ".",
            });
            expect(scope.isDone()).toBe(true);
            expect(uploadScope.isDone()).toBe(true);
            const capturedRequests = requestCapture.getRequests();
            expect(capturedRequests).toMatchSnapshot("deploy-explicit-dot-requests");
        });
    });
    describe("Request validation", () => {
        it("should include correct headers for SaaS deployment", async () => {
            const mockResponse = {
                uploadUrl: "https://storage.example.com/upload/abc123",
                deploymentId: "deployment-456",
            };
            const scope = nock(TEST_API_BASE)
                .post("/v1/deployments/source-url")
                .matchHeader("authorization", `Bearer ${TEST_AUTH_TOKEN}`)
                .reply(200, mockResponse);
            nock("https://storage.example.com").put("/upload/abc123").reply(200);
            scope.on("request", (req, interceptor, body) => {
                requestCapture.capture(req, interceptor, body);
            });
            await executeDeployCommand({
                account: TEST_ACCOUNT_NAME,
                project: TEST_PROJECT_NAME,
                "api-key": TEST_AUTH_TOKEN,
                dir: ".",
            });
            expect(scope.isDone()).toBe(true);
            const capturedRequests = requestCapture.getRequests();
            expect(capturedRequests).toMatchSnapshot("deploy-headers-validation");
        });
    });
});
//# sourceMappingURL=deploy.integration.test.js.map