import { type BuildSettings, resolveBuildSettings } from "./build-settings.ts";
import type { BuildArtifact, BuildStrategy } from "./build-strategy.ts";
import {
  hasPackageDependency,
  hasRootFile,
  runBuildSettingsCommand,
} from "./build-strategy.ts";
import { stageConfiguredArtifact } from "./configured-artifact.ts";
import type { BuildCommandIo } from "./workspace.ts";

const NUXT_CONFIG_FILENAMES = [
  "nuxt.config.js",
  "nuxt.config.mjs",
  "nuxt.config.cjs",
  "nuxt.config.ts",
  "nuxt.config.mts",
];

/**
 * Build strategy for Nuxt applications using the Nitro `node-server` preset
 * (the default). Runs `nuxt build` and produces an artifact from `.output/`.
 */
export class NuxtBuild implements BuildStrategy {
  readonly #appPath: string;
  readonly #buildSettings?: BuildSettings;
  readonly #io?: BuildCommandIo;

  constructor(options: {
    appPath: string;
    buildSettings?: BuildSettings;
    io?: BuildCommandIo;
  }) {
    this.#appPath = options.appPath;
    this.#buildSettings = options.buildSettings;
    this.#io = options.io;
  }

  async canBuild(signal?: AbortSignal): Promise<boolean> {
    return (
      (await hasRootFile(this.#appPath, NUXT_CONFIG_FILENAMES, signal)) ||
      (await hasPackageDependency(this.#appPath, ["nuxt"], signal))
    );
  }

  async execute(signal?: AbortSignal): Promise<BuildArtifact> {
    signal?.throwIfAborted();
    const settings =
      this.#buildSettings ??
      (await resolveBuildSettings({
        appPath: this.#appPath,
        buildType: "nuxt",
        signal,
      }));

    await runBuildSettingsCommand({
      appPath: this.#appPath,
      settings,
      failurePrefix: "Nuxt",
      defaultCli: {
        source: "Nuxt default",
        cliName: "nuxt",
        args: ["build"],
        missingMessage:
          "Could not find the Nuxt CLI. Install it with `npm install nuxt` or ensure npx/bunx is available.",
      },
      io: this.#io,
      signal,
    });

    return stageConfiguredArtifact({
      appPath: this.#appPath,
      outputDirectory: settings.outputDirectory,
      entrypoint: settings.entrypoint ?? "server/index.mjs",
      buildType: "nuxt",
      label: "Nuxt",
      missingEntrypointMessage: settings.entrypoint
        ? undefined
        : `Nuxt build did not produce a Nitro node server entrypoint at ${settings.outputDirectory}/server/index.mjs. Ensure nitro.preset is 'node-server' (the default) - set NITRO_PRESET=node-server or remove any custom preset from nuxt.config.`,
      signal,
    });
  }
}
