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 ASTRO_CONFIG_FILENAMES = [
  "astro.config.js",
  "astro.config.mjs",
  "astro.config.cjs",
  "astro.config.ts",
  "astro.config.mts",
];

/**
 * Build strategy for Astro applications using the @astrojs/node adapter
 * in standalone mode. Runs `astro build` and produces an artifact from `dist/`.
 */
export class AstroBuild 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, ASTRO_CONFIG_FILENAMES, signal)) ||
      (await hasPackageDependency(this.#appPath, ["astro"], signal))
    );
  }

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

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

    return stageConfiguredArtifact({
      appPath: this.#appPath,
      outputDirectory: settings.outputDirectory,
      entrypoint: settings.entrypoint ?? "server/entry.mjs",
      buildType: "astro",
      label: "Astro",
      missingEntrypointMessage: settings.entrypoint
        ? undefined
        : 'Astro build did not produce a standalone server entrypoint. Install @astrojs/node and configure it with adapter: node({ mode: "standalone" }) in your astro.config file.',
      traceDependencies: true,
      signal,
    });
  }
}
