import * as fs from "node:fs/promises";
import * as path from "node:path";
import * as vite from "vite";
import ora from "ora";
import { ResolvedEmbeddableConfig } from "./defineConfig";
import {
  findFiles,
  getComponentLibraryConfig,
  getContentHash,
} from "@embeddable.com/sdk-utils";
import fg from "fast-glob";

export const EMB_TYPE_FILE_REGEX = /^(.*)\.type\.emb\.[jt]s$/;
export const EMB_OPTIONS_FILE_REGEX = /^(.*)\.options\.emb\.[jt]s$/;

export default async (ctx: ResolvedEmbeddableConfig) => {
  const progress = ora("Building types...").start();

  await generate(ctx);

  await build(ctx);

  await cleanup(ctx);

  progress.succeed("Types built completed");
};

async function generate(ctx: ResolvedEmbeddableConfig) {
  const typeFiles = await findFiles(ctx.client.srcDir, EMB_TYPE_FILE_REGEX);
  const optionsFiles = await findFiles(
    ctx.client.srcDir,
    EMB_OPTIONS_FILE_REGEX,
  );

  const additionalImports =
    await getAdditionalImportsFromInstalledLibraries(ctx);

  const repositoryTypeImports = typeFiles
    .concat(optionsFiles)
    .map(
      ([_fileName, filePath]) =>
        `import '../${path
          .relative(ctx.client.rootDir, filePath)
          .replaceAll("\\", "/")}';`,
    )
    .join("\n");

  const typeImports = additionalImports.join("\n") + repositoryTypeImports;

  await fs.writeFile(
    path.resolve(
      ctx.client.buildDir,
      ctx.outputOptions.typesEntryPointFilename,
    ),
    typeImports,
  );
}

async function build(ctx: ResolvedEmbeddableConfig) {
  const typesFilePath = path.resolve(
    ctx.client.buildDir,
    ctx.outputOptions.typesEntryPointFilename,
  );

  await vite.build({
    logLevel: "error",
    build: {
      emptyOutDir: false,
      lib: {
        entry: typesFilePath,
        formats: ["es"],
        fileName: "embeddable-types",
      },
      outDir: ctx.client.buildDir,
    },
  });

  if (!ctx.dev?.watch) {
    const fileContent = await fs.readFile(typesFilePath, "utf8");

    const fileHash = getContentHash(fileContent);

    const fileName = `embeddable-types-${fileHash}.js`;

    await fs.rename(
      path.resolve(ctx.client.buildDir, "embeddable-types.js"),
      path.resolve(ctx.client.buildDir, fileName),
    );
  }
}

async function cleanup(ctx: ResolvedEmbeddableConfig) {
  await fs.rm(
    path.resolve(ctx.client.buildDir, "embeddable-types-entry-point.js"),
  );
}

async function getAdditionalImportsFromInstalledLibraries(
  ctx: ResolvedEmbeddableConfig,
) {
  const componentLibraries = ctx.client.componentLibraries;
  const additionalImports: string[] = [];
  for (const componentLibrary of componentLibraries) {
    const { libraryName } = getComponentLibraryConfig(componentLibrary);
    try {
      fg.sync(
        path.resolve(
          ctx.client.rootDir,
          "node_modules",
          libraryName,
          "dist",
          "embeddable-types-*.js",
        ),
      ).forEach((file: string) => {
        const fileName = path.basename(file);
        additionalImports.push(`import '${libraryName}/dist/${fileName}';`);
      });
    } catch (e) {
      console.error(`Can't load component library: ${libraryName}`, e);
      throw e;
    }
  }
  return additionalImports;
}
