// Copyright (c) Microsoft Corporation
// Licensed under the MIT license.

import { getDirectoryPath, resolvePath } from "@typespec/compiler";
import { JsContext, Module, isModule } from "./ctx.js";

import { emitModuleBody } from "./common/namespace.js";
import { OnceQueue, createOnceQueue } from "./util/once-queue.js";

import estreePlugin from "prettier/plugins/estree";
import tsPlugin from "prettier/plugins/typescript";
import { format as prettierFormat } from "prettier/standalone";

import { bifilter } from "./util/iter.js";

/**
 * Writes the tree of modules to the output directory.
 *
 * @param ctx - The emitter context.
 * @param baseOutputPath - The base output directory to write the module tree to.
 * @param rootModule - The root module to begin emitting from.
 * @param format - Whether to format the output using Prettier.
 */
export async function writeModuleTree(
  ctx: JsContext,
  baseOutputPath: string,
  rootModule: Module,
  format: boolean,
): Promise<void> {
  const queue = createOnceQueue(rootModule);

  while (!queue.isEmpty()) {
    const module = queue.take()!;
    await writeModuleFile(ctx, baseOutputPath, module, queue, format);
  }
}

/**
 * Write a single module file to the output directory.
 *
 * @param ctx - The emitter context.
 * @param baseOutputPath - The base output directory to write the module tree to.
 * @param module - The module to write.
 * @param queue - The queue of modules to write.
 * @param format - Whether to format the output using Prettier.
 * @param spit - The function used to write the file to disk (default: `ctx.program.host.writeFile`).
 */
export async function writeModuleFile(
  ctx: JsContext,
  baseOutputPath: string,
  module: Module,
  queue: OnceQueue<Module>,
  format: boolean,
  spit: (path: string, contents: string) => Promise<void> = async (name, contents) => {
    await ctx.program.host.mkdirp(getDirectoryPath(name));
    await ctx.program.host.writeFile(name, contents);
  },
): Promise<void> {
  const moduleText = [
    "// Generated by Microsoft TypeSpec",
    "",
    ...emitModuleBody(ctx, module, queue),
  ];

  const [declaredModules, declaredText] = bifilter(module.declarations, isModule);

  if (declaredText.length === 0) {
    // Early exit to avoid writing an empty module.
    return;
  }

  const isIndex = module.cursor.path.length === 0 || declaredModules.length > 0;

  const moduleRelativePath =
    module.cursor.path.length > 0
      ? module.cursor.path.join("/") + (isIndex ? "/index.ts" : ".ts")
      : "index.ts";

  const modulePath = resolvePath(baseOutputPath, moduleRelativePath);

  const text = format
    ? await prettierFormat(moduleText.join("\n"), {
        parser: "typescript",
        plugins: [tsPlugin, estreePlugin],
      })
    : moduleText.join("\n");

  await spit(modulePath, text);
}
