import generate from "./generate";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import { checkNodeVersion } from "./utils";
import { loadConfig, createCompiler } from "@stencil/core/compiler";
import { findFiles, getContentHash } from "@embeddable.com/sdk-utils";
import { ResolvedEmbeddableConfig } from "./defineConfig";
const config = {
  client: {
    rootDir: "rootDir",
    srcDir: "srcDir",
    buildDir: "buildDir",
    tmpDir: "tmpDir",
    stencilBuild: "stencilBuild",
    componentDir: "componentDir",
    bundleHash: "hash",
    componentLibraries: [],
  },
  core: {
    rootDir: "rootDir",
    templatesDir: "templatesDir",
    configsDir: "configsDir",
  },
  "sdk-react": {
    rootDir: "rootDir",
    templatesDir: "templatesDir",
    configsDir: "configsDir",
    outputOptions: {
      buildName: "buildName",
    },
  },
};

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

vi.mock("./utils", () => ({
  checkNodeVersion: vi.fn(),
}));

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

vi.mock("node:fs/promises", () => ({
  writeFile: vi.fn(),
  readdir: vi.fn(),
  readFile: vi.fn(),
  rename: vi.fn(),
  cp: vi.fn(),
  rm: vi.fn(),
}));

vi.mock("node:path", async () => {
  const actual = await vi.importActual("node:path");
  return {
    ...actual,
    resolve: vi.fn(),
    join: vi.fn(),
  };
});

vi.mock("@stencil/core/compiler", () => ({
  createCompiler: vi.fn(),
  loadConfig: vi.fn(),
}));

describe("generate", () => {
  beforeEach(() => {
    vi.mocked(checkNodeVersion).mockResolvedValue(true);
    vi.mocked(fs.readdir).mockResolvedValue([
      "embeddable-wrapper.esm.js",
      "embeddable-wrapper.esm.js.map",
      "styles.css",
    ] as any);
    vi.mocked(path.resolve).mockImplementation((...args) => args.join("/"));
    vi.mocked(fs.readFile).mockResolvedValue("");
    vi.mocked(loadConfig).mockResolvedValue({
      config: {},
    } as any);
    vi.mocked(createCompiler).mockResolvedValue({
      build: vi.fn().mockResolvedValue({
        hasError: false,
      }),
      destroy: vi.fn(),
    } as any);

    vi.mocked(getContentHash).mockReturnValue("hash");
    vi.mocked(findFiles).mockResolvedValue([["", ""]]);

    Object.defineProperties(process, {
      chdir: {
        value: vi.fn(),
      },
    });
  });

  it("should generate bundle", async () => {
    await generate(config as unknown as ResolvedEmbeddableConfig, "sdk-react");

    // should inject css
    expect(fs.writeFile).toHaveBeenCalledWith("componentDir/style.css", "");

    // should inject bundle renderer
    expect(fs.writeFile).toHaveBeenCalledWith("componentDir/component.tsx", "");

    expect(loadConfig).toHaveBeenCalled();

    expect(createCompiler).toHaveBeenCalledWith({});

    // check if the file is renamed
    expect(fs.rename).toHaveBeenCalledWith(
      "stencilBuild/embeddable-wrapper.esm.js",
      "stencilBuild/embeddable-wrapper.esm-hash.js",
    );
  });

  it("should generate bundle in dev mode", async () => {
    const ctx = {
      ...config,
      dev: {
        watch: true,
        logger: vi.fn(),
        sys: vi.fn(),
      },
    };

    vi.mocked(fs.readFile).mockResolvedValue(
      "replace-this-with-component-name",
    );
    await generate(ctx as unknown as ResolvedEmbeddableConfig, "sdk-react");

    expect(createCompiler).toHaveBeenCalled();

    expect(fs.writeFile).toHaveBeenCalledWith(
      "componentDir/component.tsx",
      expect.stringContaining("embeddable-component"),
    );

    expect(loadConfig).toHaveBeenCalledWith({
      config: {
        configPath: "buildDir/stencil.config.ts",
        devMode: true,
        maxConcurrentWorkers: process.platform === "win32" ? 0 : 8,
        minifyCss: false,
        minifyJs: false,
        namespace: "embeddable-wrapper",
        outputTargets: [
          {
            type: "dist",
          },
        ],
        rootDir: "buildDir",
        sourceMap: true,
        srcDir: "buildDir/component",
        tsconfig: "buildDir/tsconfig.json",
        watchDirs: ["buildDir/buildName"],
      },
      initTsConfig: true,
      logger: expect.any(Function),
      sys: expect.any(Function),
    });
  });
});
