import push, { buildArchive, EMBEDDABLE_FILES, sendBuild, sendBuildByApiKey } from "./push";
import provideConfig from "./provideConfig";
import { fileFromPath } from "formdata-node/file-from-path";
import * as path from "path";
import yazl from "yazl";
import * as fs from "node:fs/promises";
import * as fsSync from "node:fs";
import { findFiles } from "@embeddable.com/sdk-utils";
import { ResolvedEmbeddableConfig } from "./defineConfig";
import { vi, describe, it, expect, beforeEach } from "vitest";
import { checkBuildSuccess, checkNodeVersion, getArgumentByKey, shouldSkipModelCheck } from "./utils";

// @ts-ignore
import reportErrorToRollbar from "./rollbar.mjs";
import { server } from "../../../mocks/server";
import { http, HttpResponse } from "msw";

const warnMock = vi.fn();

const infoMock = {
  info: vi.fn(),
  succeed: vi.fn(),
  fail: vi.fn(),
};

const startMock = {
  succeed: vi.fn(),
  info: () => infoMock,
  fail: vi.fn(),
};

vi.mock("ora", () => ({
  default: () => ({
    start: vi.fn().mockReturnValue(startMock),
    info: vi.fn(),
    warn: warnMock,
  }),
}));

vi.mock("./utils", () => ({
  checkNodeVersion: vi.fn(),
  checkBuildSuccess: vi.fn(),
  getArgumentByKey: vi.fn(),
  getSDKVersions: vi.fn(),
  shouldSkipModelCheck: vi.fn().mockReturnValue(false),
}));

vi.mock("node:fs/promises", () => ({
  writeFile: vi.fn(),
  readFile: vi.fn(),
  access: vi.fn(),
  rm: vi.fn(),
  mkdir: vi.fn(),
  stat: vi.fn(),
  appendFile: vi.fn(),
}));

vi.mock("node:fs", () => ({
  createWriteStream: vi.fn(),
  readdirSync: vi.fn().mockReturnValue([]),
  statSync: vi.fn().mockReturnValue({ isFile: () => true }),
  existsSync: vi.fn().mockReturnValue(true),
}));

vi.mock("./provideConfig", () => ({
  default: vi.fn().mockResolvedValue({
    client: {
      rootDir: "rootDir",
      buildDir: "buildDir",
      archiveFile: "embeddable-build.zip",
    },
  }),
}));

vi.mock("./rollbar.mjs", () => ({
  default: vi.fn(),
}));

vi.mock("@embeddable.com/sdk-utils", () => ({
  findFiles: vi.fn(),
}));

const { zipRef } = vi.hoisted(() => ({
  zipRef: { current: null as any },
}));

vi.mock("yazl", () => ({
  default: {
    ZipFile: vi.fn(function () { return zipRef.current; }),
  },
}));

vi.mock("formdata-node/file-from-path", () => ({
  fileFromPath: vi.fn().mockReturnValue(new Blob([new ArrayBuffer(8)])),
}));

const config = {
  client: {
    rootDir: "rootDir",
    buildDir: "buildDir",
    archiveFile: "embeddable-build.zip",
    customCanvasCss: "src/custom-canvas.css",
  },
  pushBaseUrl: "http://localhost:3000",
  previewBaseUrl: "http://localhost:3000",
  pushComponents: true,
};

describe("push", () => {
  const zipMock = {
    addFile: vi.fn(),
    end: vi.fn(),
    outputStream: { pipe: vi.fn() },
  };

  beforeEach(() => {
    vi.mocked(checkNodeVersion).mockResolvedValue(true);
    vi.mocked(checkBuildSuccess).mockResolvedValue(true);
    vi.mocked(getArgumentByKey).mockReturnValue(undefined);
    vi.mocked(provideConfig).mockResolvedValue(
      config as ResolvedEmbeddableConfig
    );
    vi.mocked(fs.access).mockResolvedValue(undefined);
    vi.mocked(fs.readFile).mockImplementation(async () =>
      Buffer.from(`{"access_token":"mocked-token"}`)
    );
    vi.mocked(fs.stat).mockResolvedValue({
      size: 100,
    } as any);
    vi.mocked(findFiles).mockResolvedValue([["fileName", "filePath"]]);
    vi.mocked(fileFromPath).mockReturnValue(
      new Blob([new ArrayBuffer(8)]) as any
    );
    zipRef.current = zipMock;
    vi.mocked(fsSync.readdirSync).mockReturnValue([]);
    vi.mocked(fsSync.existsSync).mockReturnValue(true);

    vi.mocked(fsSync.createWriteStream).mockReturnValue({
      on: (event: string, cb: () => void) => {
        cb();
      },
      end: vi.fn(),
    } as any);

    vi.spyOn(process, "exit").mockImplementation(() => null as never);
  });

  it("should push the build", async () => {
    await push();

    expect(provideConfig).toHaveBeenCalled();
    expect(checkNodeVersion).toHaveBeenCalled();
    expect(checkBuildSuccess).toHaveBeenCalled();

    expect(fs.access).toHaveBeenCalledWith(config.client.buildDir);

    expect(yazl.ZipFile).toHaveBeenCalled();
    expect(fsSync.createWriteStream).toHaveBeenCalledWith(
      config.client.archiveFile
    );

    expect(zipMock.outputStream.pipe).toHaveBeenCalled();
    expect(zipMock.addFile).toHaveBeenCalledWith(
      "src/custom-canvas.css", "global.css", expect.objectContaining({ compress: true })
    );
    expect(fsSync.readdirSync).toHaveBeenCalledWith(
      "buildDir", expect.objectContaining({ recursive: true })
    );
    expect(zipMock.end).toHaveBeenCalled();
    // after publishing the file gets removed
    expect(fs.rm).toHaveBeenCalledWith(config.client.archiveFile);

    expect(infoMock.info).toHaveBeenCalledWith(
      "Publishing to mocked-workspace-name using http://localhost:3000/workspace/mocked-workspace-id..."
    );
    expect(infoMock.succeed).toHaveBeenCalledWith(
      "Published to mocked-workspace-name using http://localhost:3000/workspace/mocked-workspace-id"
    );
  });

  it("should fail if there are no workspaces", async () => {
    vi.spyOn(console, "error").mockImplementation(() => undefined);
    server.use(
      http.get("**/workspace", () => {
        return HttpResponse.json([]);
      })
    );

    await push();

    expect(startMock.fail).toHaveBeenCalledWith("No workspaces found");
    expect(process.exit).toHaveBeenCalledWith(1);
    expect(reportErrorToRollbar).toHaveBeenCalled();
  });

  it("should fail if the build is not successful", async () => {
    vi.spyOn(console, "error").mockImplementation(() => undefined);
    vi.mocked(checkBuildSuccess).mockResolvedValue(false);

    await push();

    expect(process.exit).toHaveBeenCalledWith(1);

    expect(console.error).toHaveBeenCalledWith(
      "Build failed or not completed. Please run `embeddable:build` first."
    );
  });

  it("should push by api key provided in the arguments", async () => {
    vi.mocked(getArgumentByKey).mockReturnValue("mocked-api-key");
    Object.defineProperties(process, {
      argv: {
        value: [
          "--api-key",
          "mocked-api-key",
          "--email",
          "mocked-email@valid.com",
        ],
      },
    });

    await push();

    expect(startMock.succeed).toHaveBeenCalledWith("Published using API key");
  });

  describe("push configuration", () => {
    it("should fail if pushModels, pushComponents, and pushEmbeddables are all disabled", async () => {
      vi.spyOn(console, "error").mockImplementation(() => undefined);
      vi.mocked(provideConfig).mockResolvedValue({
        ...config,
        pushModels: false,
        pushComponents: false,
        pushEmbeddables: false,
      } as ResolvedEmbeddableConfig);

      await push();

      expect(startMock.fail).toHaveBeenCalledWith(
        "Cannot push: pushModels, pushComponents, and pushEmbeddables are all disabled"
      );
      expect(process.exit).toHaveBeenCalledWith(1);
    });

    it("should only include component files when pushModels is false", async () => {
      const localZipMock = {
        addFile: vi.fn(),
        end: vi.fn(),
        outputStream: { pipe: vi.fn() },
      };
      zipRef.current = localZipMock;

      vi.mocked(provideConfig).mockResolvedValue({
        ...config,
        pushModels: false,
        pushComponents: true,
      } as ResolvedEmbeddableConfig);

      await push();

      // Should read component build directory
      expect(fsSync.readdirSync).toHaveBeenCalledWith(
        "buildDir", expect.objectContaining({ recursive: true })
      );
      // Should include global.css and client context files
      expect(localZipMock.addFile).toHaveBeenCalledWith(
        expect.anything(), "global.css", expect.objectContaining({ compress: true })
      );
    });
  });

  describe("API key validation", () => {
    it("should fail if API key is not provided", async () => {
      vi.spyOn(console, "error").mockImplementation(() => undefined);
      Object.defineProperties(process, {
        argv: {
          value: ["--api-key"],
        },
      });
      vi.mocked(getArgumentByKey).mockReturnValue(undefined);

      await push();

      expect(startMock.fail).toHaveBeenCalledWith("No API key provided");
      expect(process.exit).toHaveBeenCalledWith(1);
    });

    it("should fail if email is not provided with API key", async () => {
      vi.spyOn(console, "error").mockImplementation(() => undefined);
      Object.defineProperties(process, {
        argv: {
          value: ["--api-key", "some-key"],
        },
      });
      vi.mocked(getArgumentByKey)
        .mockReturnValueOnce("some-key") // API key
        .mockReturnValueOnce(undefined); // Email

      await push();

      expect(startMock.fail).toHaveBeenCalledWith(
        "Invalid email provided. Please provide a valid email using --email (-e) flag"
      );
      expect(process.exit).toHaveBeenCalledWith(1);
    });

    it("should fail if email is invalid", async () => {
      vi.spyOn(console, "error").mockImplementation(() => undefined);
      Object.defineProperties(process, {
        argv: {
          value: ["--api-key", "some-key", "--email", "invalid-email"],
        },
      });
      vi.mocked(getArgumentByKey)
        .mockReturnValueOnce("some-key") // API key
        .mockReturnValueOnce("invalid-email"); // Invalid email

      await push();

      expect(startMock.fail).toHaveBeenCalledWith(
        "Invalid email provided. Please provide a valid email using --email (-e) flag"
      );
      expect(process.exit).toHaveBeenCalledWith(1);
    });

    it("should accept optional message parameter", async () => {
      Object.defineProperties(process, {
        argv: {
          value: [
            "--api-key",
            "some-key",
            "--email",
            "valid@email.com",
            "--message",
            "test message",
            "--cube-version",
            "v1.34",
          ],
        },
      });
      vi.mocked(getArgumentByKey).mockImplementation((keysArg) => {
        const key = Array.isArray(keysArg) ? keysArg[0] : keysArg;
        if (key === "--api-key") return "some-key";
        if (key === "--email") return "valid@email.com";
        if (key === "--message") return "test message";
        if (key === "--cube-version") return "v1.34";
        return undefined;
      });

      await push();

      expect(startMock.succeed).toHaveBeenCalledWith("Published using API key");
    });
  });

  describe("error handling", () => {
    beforeEach(() => {
      // Reset all mocks to their default state
      vi.mocked(getArgumentByKey).mockReturnValue(undefined);
      Object.defineProperties(process, {
        argv: {
          value: [],
        },
      });
    });

    it("should fail if build directory does not exist", async () => {
      vi.spyOn(console, "error").mockImplementation(() => undefined);
      vi.mocked(fs.access).mockRejectedValue(new Error("No such directory"));
      vi.mocked(provideConfig).mockResolvedValue(
        config as ResolvedEmbeddableConfig
      );

      await push();

      expect(console.error).toHaveBeenCalledWith(
        "No embeddable build was produced."
      );
      expect(process.exit).toHaveBeenCalledWith(1);
    });

    it("should fail if token is not available", async () => {
      vi.spyOn(console, "error").mockImplementation(() => undefined);
      vi.mocked(fs.access).mockResolvedValue(undefined);
      vi.mocked(fs.readFile).mockResolvedValue(Buffer.from("{}"));
      vi.mocked(provideConfig).mockResolvedValue(
        config as ResolvedEmbeddableConfig
      );

      await push();

      expect(console.error).toHaveBeenCalledWith(
        'Unauthorized. Please login using "npm run embeddable:login"'
      );
      expect(process.exit).toHaveBeenCalledWith(1);
    });

    it("should handle and report errors during push", async () => {
      const testError = new Error("Test error");
      vi.mocked(provideConfig).mockRejectedValue(testError);
      vi.mocked(fs.access).mockResolvedValue(undefined);
      vi.mocked(fs.readFile).mockImplementation(async () =>
        Buffer.from(`{"access_token":"mocked-token"}`)
      );

      await push();

      expect(reportErrorToRollbar).toHaveBeenCalledWith(testError);
      expect(process.exit).toHaveBeenCalledWith(1);
    });
  });

  describe("buildArchive", () => {
    type MockZip = {
      addFile: ReturnType<typeof vi.fn>;
      end: ReturnType<typeof vi.fn>;
      outputStream: { pipe: ReturnType<typeof vi.fn> };
    };

    let mockZipLocal: MockZip;

    beforeEach(() => {
      mockZipLocal = {
        addFile: vi.fn(),
        end: vi.fn(),
        outputStream: { pipe: vi.fn() },
      };

      zipRef.current = mockZipLocal;
      vi.mocked(fsSync.readdirSync).mockReturnValue([]);
      vi.mocked(fsSync.existsSync).mockReturnValue(true);
      vi.mocked(findFiles).mockResolvedValue([]);
    });

    it("should include all file types when both flags are true", async () => {
      vi.mocked(findFiles)
        .mockResolvedValueOnce([
          ["model1.cube.yml", "/path/to/model1.cube.yml"],
          ["model2.cube.yaml", "/path/to/model2.cube.yaml"],
        ])
        .mockResolvedValueOnce([
          ["context1.sc.yml", "/path/to/context1.sc.yml"],
          ["context2.cc.yml", "/path/to/context2.cc.yml"],
        ]);

      const testConfig = {
        ...config,
        pushModels: true,
        pushComponents: true,
        client: {
          ...config.client,
          srcDir: "/src",
        },
      } as ResolvedEmbeddableConfig;

      await buildArchive(testConfig);

      // Should read component build directory
      expect(fsSync.readdirSync).toHaveBeenCalledWith(
        testConfig.client.buildDir,
        expect.objectContaining({ recursive: true })
      );
      // Should include global.css
      expect(mockZipLocal.addFile).toHaveBeenCalledWith(
        testConfig.client.customCanvasCss,
        "global.css",
        expect.objectContaining({ compress: true })
      );
      // Should include all model files
      expect(mockZipLocal.addFile).toHaveBeenCalledWith(
        "/path/to/model1.cube.yml",
        "model1.cube.yml",
        expect.objectContaining({ compress: true })
      );
      expect(mockZipLocal.addFile).toHaveBeenCalledWith(
        "/path/to/model2.cube.yaml",
        "model2.cube.yaml",
        expect.objectContaining({ compress: true })
      );
      // Should include all preset files
      expect(mockZipLocal.addFile).toHaveBeenCalledWith(
        "/path/to/context1.sc.yml",
        "context1.sc.yml",
        expect.objectContaining({ compress: true })
      );
      expect(mockZipLocal.addFile).toHaveBeenCalledWith(
        "/path/to/context2.cc.yml",
        "context2.cc.yml",
        expect.objectContaining({ compress: true })
      );
    });

    it("should only include component files when pushModels is false", async () => {
      const testConfig = {
        ...config,
        pushModels: false,
        pushComponents: true,
        client: {
          ...config.client,
          srcDir: "/src",
        },
      } as ResolvedEmbeddableConfig;

      await buildArchive(testConfig);

      // Should read component build directory
      expect(fsSync.readdirSync).toHaveBeenCalledWith(
        testConfig.client.buildDir,
        expect.objectContaining({ recursive: true })
      );
      // Should include global.css
      expect(mockZipLocal.addFile).toHaveBeenCalledWith(
        testConfig.client.customCanvasCss,
        "global.css",
        expect.objectContaining({ compress: true })
      );
      // Should only find client context files
      expect(findFiles).toHaveBeenCalledOnce();
    });

    it("should search in custom directories for model files", async () => {
      const testConfig = {
        ...config,
        pushModels: true,
        pushComponents: true,
        client: {
          ...config.client,
          srcDir: "/src",
          modelsSrc: "/custom/models/path",
          presetsSrc: "/custom/presets/path",
        },
      } as ResolvedEmbeddableConfig;

      await buildArchive(testConfig);

      expect(findFiles).toHaveBeenCalledWith(
        "/custom/models/path",
        expect.any(RegExp)
      );
      expect(findFiles).toHaveBeenCalledWith(
        "/custom/presets/path",
        expect.any(RegExp)
      );
    });

    it("should add directory files to zip with correct relative paths", async () => {
      vi.mocked(fsSync.readdirSync).mockReturnValue([
        "index.js",
        "components/widget.js",
      ] as any);
      vi.mocked(fsSync.statSync).mockReturnValue({ isFile: () => true } as any);

      const testConfig = {
        ...config,
        pushModels: false,
        pushComponents: true,
        client: {
          ...config.client,
          srcDir: "/src",
        },
      } as ResolvedEmbeddableConfig;

      await buildArchive(testConfig);

      expect(mockZipLocal.addFile).toHaveBeenCalledWith(
        path.join("buildDir", "index.js"),
        "index.js",
        expect.objectContaining({ compress: true })
      );
      expect(mockZipLocal.addFile).toHaveBeenCalledWith(
        path.join("buildDir", "components/widget.js"),
        "components/widget.js",
        expect.objectContaining({ compress: true })
      );
    });

    it("should skip directories when adding directory contents to zip", async () => {
      vi.mocked(fsSync.readdirSync).mockReturnValue(["components"] as any);
      vi.mocked(fsSync.statSync).mockReturnValue({ isFile: () => false } as any);

      const testConfig = {
        ...config,
        pushModels: false,
        pushComponents: true,
        client: {
          ...config.client,
          srcDir: "/src",
        },
      } as ResolvedEmbeddableConfig;

      await buildArchive(testConfig);

      const addFileCalls = mockZipLocal.addFile.mock.calls;
      const dirCall = addFileCalls.find(
        (call: any) => call[1] === "components"
      );
      expect(dirCall).toBeUndefined();
    });

    it("should skip customCanvasCss when file does not exist", async () => {
      vi.mocked(fsSync.existsSync).mockReturnValue(false);

      const testConfig = {
        ...config,
        pushModels: false,
        pushComponents: true,
        client: {
          ...config.client,
          srcDir: "/src",
        },
      } as ResolvedEmbeddableConfig;

      await buildArchive(testConfig);

      const addFileCalls = mockZipLocal.addFile.mock.calls;
      const cssCall = addFileCalls.find(
        (call: any) => call[1] === "global.css"
      );
      expect(cssCall).toBeUndefined();
    });

    it("should skip buildDir when pushComponents is false (even if it exists)", async () => {
      vi.mocked(fsSync.existsSync).mockReturnValue(true);

      const testConfig = {
        ...config,
        pushModels: true,
        pushComponents: false,
        client: {
          ...config.client,
          srcDir: "/src",
        },
      } as ResolvedEmbeddableConfig;

      await expect(buildArchive(testConfig)).resolves.not.toThrow();

      expect(fsSync.readdirSync).not.toHaveBeenCalled();
    });

    it("should use srcDir as fallback when modelsSrc/presetsSrc are not defined", async () => {
      const testConfig = {
        ...config,
        pushModels: true,
        pushComponents: true,
        client: {
          ...config.client,
          srcDir: "/src",
          modelsSrc: undefined,
          presetsSrc: undefined,
        },
      } as unknown as ResolvedEmbeddableConfig;

      await buildArchive(testConfig);

      expect(findFiles).toHaveBeenCalledWith("/src", expect.any(RegExp));
      expect(findFiles).toHaveBeenCalledWith("/src", expect.any(RegExp));
    });

    it("should include embeddable files when pushEmbeddables is true", async () => {
      vi.mocked(findFiles)
        .mockResolvedValueOnce([]) // cube files
        .mockResolvedValueOnce([]) // security context files
        .mockResolvedValueOnce([]) // client context files
        .mockResolvedValueOnce([
          ["dashboard.embeddable.yaml", "/src/dashboard.embeddable.yaml"],
          ["report.embeddable.yml", "/src/report.embeddable.yml"],
        ]); // embeddable files

      const testConfig = {
        ...config,
        pushModels: true,
        pushComponents: true,
        pushEmbeddables: true,
        client: {
          ...config.client,
          srcDir: "/src",
        },
      } as ResolvedEmbeddableConfig;

      await buildArchive(testConfig);

      expect(findFiles).toHaveBeenCalledWith("/src", EMBEDDABLE_FILES);
      expect(mockZipLocal.addFile).toHaveBeenCalledWith(
        "/src/dashboard.embeddable.yaml",
        "dashboard.embeddable.yaml",
        expect.objectContaining({ compress: true }),
      );
      expect(mockZipLocal.addFile).toHaveBeenCalledWith(
        "/src/report.embeddable.yml",
        "report.embeddable.yml",
        expect.objectContaining({ compress: true }),
      );
    });

    it("should not search for embeddable files when pushEmbeddables is false", async () => {
      // Reset call history to avoid interference from previous tests
      vi.mocked(findFiles).mockClear();

      const testConfig = {
        ...config,
        pushModels: true,
        pushComponents: true,
        pushEmbeddables: false,
        client: {
          ...config.client,
          srcDir: "/src",
        },
      } as ResolvedEmbeddableConfig;

      await buildArchive(testConfig);

      // findFiles should have been called for models and components but NOT for embeddable files
      const embeddableFilesCall = vi
        .mocked(findFiles)
        .mock.calls.find((call) => call[1] === EMBEDDABLE_FILES);
      expect(embeddableFilesCall).toBeUndefined();
    });

    it("should exit when all three push flags are disabled", async () => {
      vi.spyOn(process, "exit").mockImplementation(() => null as never);

      const testConfig = {
        ...config,
        pushModels: false,
        pushComponents: false,
        pushEmbeddables: false,
        client: {
          ...config.client,
          srcDir: "/src",
        },
      } as ResolvedEmbeddableConfig;

      await buildArchive(testConfig);

      expect(process.exit).toHaveBeenCalledWith(1);
    });
  });

  describe("EMBEDDABLE_FILES", () => {
    it("should match .embeddable.yaml files", () => {
      expect(EMBEDDABLE_FILES.test("my-dashboard.embeddable.yaml")).toBe(true);
      expect(EMBEDDABLE_FILES.test("src/dashboards/sales.embeddable.yaml")).toBe(
        true,
      );
    });

    it("should match .embeddable.yml files", () => {
      expect(EMBEDDABLE_FILES.test("my-dashboard.embeddable.yml")).toBe(true);
      expect(EMBEDDABLE_FILES.test("src/widgets/chart.embeddable.yml")).toBe(
        true,
      );
    });

    it("should not match unrelated yaml files", () => {
      expect(EMBEDDABLE_FILES.test("config.yaml")).toBe(false);
      expect(EMBEDDABLE_FILES.test("my-dashboard.cc.yaml")).toBe(false);
      expect(EMBEDDABLE_FILES.test("my-dashboard.sc.yaml")).toBe(false);
    });

    it("should not match cube files", () => {
      expect(EMBEDDABLE_FILES.test("my-model.cube.yaml")).toBe(false);
      expect(EMBEDDABLE_FILES.test("my-model.cube.yml")).toBe(false);
    });

    it("should not match non-yaml extensions", () => {
      expect(EMBEDDABLE_FILES.test("my-dashboard.embeddable.json")).toBe(false);
      expect(EMBEDDABLE_FILES.test("my-dashboard.embeddable.ts")).toBe(false);
    });
  });

  describe.each([
    {
      label: "sendBuild",
      endpoint: "**/bundle/:workspaceId/upload",
      invoke: (testConfig: ResolvedEmbeddableConfig, skipModelCheck?: boolean) =>
        sendBuild(testConfig, { workspaceId: "test-workspace", token: "test-token", skipModelCheck }),
    },
    {
      label: "sendBuildByApiKey",
      endpoint: "**/bundle/upload",
      invoke: (testConfig: ResolvedEmbeddableConfig, skipModelCheck?: boolean) =>
        sendBuildByApiKey(testConfig, { apiKey: "test-api-key", email: "test@example.com", skipModelCheck }),
    },
  ])("$label", ({ endpoint, invoke }) => {
    async function captureMetadata(pushEmbeddables: boolean, skipModelCheck?: boolean) {
      let capturedMetadata: Record<string, any> | undefined;

      server.use(
        http.post(endpoint, async ({ request }) => {
          const formData = await request.formData();
          const requestBlob = formData.get("request") as Blob;
          capturedMetadata = JSON.parse(await requestBlob.text());
          return HttpResponse.json({ bundleId: "mocked-bundle-id" });
        }),
      );

      const testConfig = {
        ...config,
        pushEmbeddables,
        region: "us",
        starterEmbeddables: {},
      } as unknown as ResolvedEmbeddableConfig;

      await invoke(testConfig, skipModelCheck);
      return capturedMetadata;
    }

    it("should include pushEmbeddables in form data metadata when true", async () => {
      const metadata = await captureMetadata(true);
      expect(metadata?.pushEmbeddables).toBe(true);
    });

    it("should include pushEmbeddables in form data metadata when false", async () => {
      const metadata = await captureMetadata(false);
      expect(metadata?.pushEmbeddables).toBe(false);
    });

    it("should include skipModelCheck in form data metadata when true", async () => {
      const metadata = await captureMetadata(true, true);
      expect(metadata?.skipModelCheck).toBe(true);
    });

    it("should not include skipModelCheck in form data metadata when false", async () => {
      const metadata = await captureMetadata(true, false);
      expect(metadata?.skipModelCheck).toBeUndefined();
    });
  });

  describe("sendBuild warning suppression", () => {
    beforeEach(() => {
      warnMock.mockClear();
    });

    async function invokeSendBuildWithWarnings(
      flags: { pushModels?: boolean; pushComponents?: boolean; pushEmbeddables?: boolean },
      warnings: string[],
    ) {
      server.use(
        http.post("**/bundle/:workspaceId/upload", () =>
          HttpResponse.json({ bundleId: "mocked-bundle-id", warnings }),
        ),
      );

      const testConfig = {
        ...config,
        pushModels: flags.pushModels ?? true,
        pushComponents: flags.pushComponents ?? true,
        pushEmbeddables: flags.pushEmbeddables ?? true,
        region: "us",
        starterEmbeddables: {},
      } as unknown as ResolvedEmbeddableConfig;

      await sendBuild(testConfig, { workspaceId: "test-workspace", token: "test-token" });
    }

    it("should suppress WARN-005 when pushEmbeddables is false", async () => {
      await invokeSendBuildWithWarnings({ pushEmbeddables: false }, ["WARN-005: Embeddable upload disabled"]);
      expect(warnMock).not.toHaveBeenCalled();
    });

    it("should suppress WARN-001 when pushModels is false", async () => {
      await invokeSendBuildWithWarnings({ pushModels: false }, ["WARN-001: Model upload disabled"]);
      expect(warnMock).not.toHaveBeenCalled();
    });

    it("should suppress WARN-003 when pushComponents is false", async () => {
      await invokeSendBuildWithWarnings({ pushComponents: false }, ["WARN-003: Component upload disabled"]);
      expect(warnMock).not.toHaveBeenCalled();
    });

    it("should still show unexpected warnings even when some flags are false", async () => {
      await invokeSendBuildWithWarnings(
        { pushEmbeddables: false },
        ["WARN-005: Embeddable upload disabled", "WARN-999: Something unexpected"],
      );
      expect(warnMock).toHaveBeenCalledWith("WARN-999: Something unexpected");
    });

    it("should not suppress warnings with a code that only starts with a suppressed code", async () => {
      await invokeSendBuildWithWarnings(
        { pushModels: false },
        ["WARN-0010: Some other warning"],
      );
      expect(warnMock).toHaveBeenCalledWith("WARN-0010: Some other warning");
    });


    it("should show all warnings when all flags are true", async () => {
      await invokeSendBuildWithWarnings(
        { pushModels: true, pushComponents: true, pushEmbeddables: true },
        ["WARN-001: Model upload disabled"],
      );
      expect(warnMock).toHaveBeenCalledWith("WARN-001: Model upload disabled");
    });
  });
});
