import build from "./build";
import prepare from "./prepare";
import validate from "./validate";
import buildTypes from "./buildTypes";
import provideConfig from "./provideConfig";
import generate from "./generate";
import cleanup from "./cleanup";
import buildGlobalHooks from "./buildGlobalHooks";

// @ts-ignore
import reportErrorToRollbar from "./rollbar.mjs";
import { storeBuildSuccessFlag } from "./utils";
import { ResolvedEmbeddableConfig } from "./defineConfig";

vi.mock("./logger", () => ({
  initLogger: vi.fn().mockResolvedValue(undefined),
  logError: vi.fn().mockResolvedValue(undefined),
}));

const mockPlugin = {
  validate: vi.fn(),
  build: vi.fn(),
  cleanup: vi.fn(),
};

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

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

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

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

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

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

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

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

vi.mock("./utils", async () => {
  const actual = await vi.importActual("./utils");
  return {
    ...actual,
    storeBuildSuccessFlag: vi.fn(),
  };
});

const config = {
  plugins: [() => mockPlugin],
  pushComponents: true,
};

describe("build", () => {
  beforeEach(() => {
    vi.mocked(provideConfig).mockResolvedValue(
      config as unknown as ResolvedEmbeddableConfig,
    );
  });

  it("should call all the necessary functions", async () => {
    await build();

    expect(validate).toHaveBeenCalledWith(config);
    expect(prepare).toHaveBeenCalledWith(config);
    expect(buildTypes).toHaveBeenCalledWith(config);
    expect(buildGlobalHooks).toHaveBeenCalledWith(config);

    // Plugin
    expect(mockPlugin.validate).toHaveBeenCalledWith(config);
    expect(mockPlugin.build).toHaveBeenCalledWith(config);
    expect(mockPlugin.cleanup).toHaveBeenCalledWith(config);

    expect(generate).toHaveBeenCalledWith(config, "sdk-react");

    expect(cleanup).toHaveBeenCalledWith(config);
    expect(storeBuildSuccessFlag).toHaveBeenCalled();
  });

  it("should no-op when pushComponents is false", async () => {
    const exitSpy = vi
      .spyOn(process, "exit")
      .mockImplementation(() => null as never);
    const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
    vi.mocked(provideConfig).mockResolvedValue({
      ...config,
      pushComponents: false,
    } as unknown as ResolvedEmbeddableConfig);

    await build();

    expect(logSpy).toHaveBeenCalledWith(
      "Skipping build step as pushComponents config is set to false",
    );
    expect(exitSpy).toHaveBeenCalledWith(0);

    const exitOrder = exitSpy.mock.invocationCallOrder[0];
    const validateOrder = vi.mocked(validate).mock.invocationCallOrder[0];
    if (validateOrder !== undefined) {
      expect(exitOrder).toBeLessThan(validateOrder);
    }
  });

  it("should call reportErrorToRollbar and exit if an error occurs", async () => {
    const originalConsoleError = console.error;
    console.error = vi.fn();
    vi.spyOn(process, "exit").mockImplementation(() => null as never);
    vi.spyOn(console, "log").mockImplementation(() => undefined);
    const error = new Error("test error");

    vi.mocked(validate).mockRejectedValue(error);
    vi.mocked(reportErrorToRollbar).mockResolvedValue(undefined);

    await build();

    expect(reportErrorToRollbar).toHaveBeenCalledWith(error);
    expect(console.log).toHaveBeenCalledWith(error);
    expect(process.exit).toHaveBeenCalledWith(1);

    console.error = originalConsoleError;
  });
});
