import fs from "node:fs/promises";
import path from "node:path";
import pluralize from "pluralize";
import { InternalConfiguration } from "./configurationReader";
import { extension, serialize } from "./serializer";
import { TransformedCollection } from "./transformer";

function createArrayConstName(name: string) {
  let suffix = name.charAt(0).toUpperCase() + name.slice(1);
  return "all" + pluralize(suffix);
}

type DataFileCollection = Pick<TransformedCollection, "name" | "documents">;

// visible for testing
export async function createDataFile(
  directory: string,
  collection: DataFileCollection,
) {
  const dataPath = path.join(
    directory,
    `${createArrayConstName(collection.name)}.${extension}`,
  );

  await fs.writeFile(
    dataPath,
    serialize(collection.documents.map((doc) => doc.document)),
  );
}

function createDataFiles(
  directory: string,
  collections: Array<DataFileCollection>,
) {
  return Promise.all(
    collections.map((collection) => createDataFile(directory, collection)),
  );
}

type JavaScriptFileConfiguration = {
  collections: Array<Pick<TransformedCollection, "name">>;
};

async function createJavaScriptFile(
  directory: string,
  configuration: JavaScriptFileConfiguration,
) {
  const collections = configuration.collections.map(({ name }) =>
    createArrayConstName(name),
  );

  let content = `// generated by content-collections at ${new Date()}\n\n`;
  for (const name of collections) {
    content += `import ${name} from "./${name}.${extension}";\n`;
  }
  content += "\n";
  content += "export { " + collections.join(", ") + " };\n";

  await fs.writeFile(path.join(directory, "index.js"), content, "utf-8");
}

type TypeDefinitionFileConfiguration = Pick<
  InternalConfiguration,
  "path" | "generateTypes"
> & {
  collections: Array<Pick<TransformedCollection, "name" | "typeName">>;
};

function createImportPath(directory: string, target: string) {
  let importPath = path.posix.join(
    ...path.relative(directory, target).split(path.sep),
  );
  if (!importPath.startsWith(".")) {
    importPath = "./" + importPath;
  }
  return importPath;
}

async function createTypeDefinitionFile(
  directory: string,
  configuration: TypeDefinitionFileConfiguration,
) {
  if (!configuration.generateTypes) {
    return;
  }

  const importPath = createImportPath(directory, configuration.path);
  let content = `import configuration from "${importPath}";
import { GetTypeByName } from "@content-collections/core";
`;

  const collections = configuration.collections;
  for (const collection of collections) {
    content += `\n`;
    content += `export type ${collection.typeName} = GetTypeByName<typeof configuration, "${collection.name}">;\n`;
    content += `export declare const ${createArrayConstName(
      collection.name,
    )}: Array<${collection.typeName}>;\n`;
  }

  content += "\n";
  // https://github.com/microsoft/TypeScript/issues/38592
  content += "export {};\n";

  await fs.writeFile(path.join(directory, "index.d.ts"), content, "utf-8");
}

export async function createWriter(directory: string) {
  await fs.mkdir(directory, { recursive: true });
  return {
    createJavaScriptFile: (configuration: JavaScriptFileConfiguration) =>
      createJavaScriptFile(directory, configuration),
    createTypeDefinitionFile: (
      configuration: TypeDefinitionFileConfiguration,
    ) => createTypeDefinitionFile(directory, configuration),
    createDataFiles: (collections: Array<DataFileCollection>) =>
      createDataFiles(directory, collections),
  };
}

export type Writer = Awaited<ReturnType<typeof createWriter>>;
