/**
 * `wrangler.toml` generator.
 *
 * Emits a minimal but complete Cloudflare Workers config covering:
 *   - ModuleWorker entry point (`main = "..."`)
 *   - Node.js compatibility flag for `nodejs_compat` consumers
 *   - Static assets binding (for `mandu build --target=workers` public/ output)
 *   - Compatibility date
 *
 * Phase 15.2+ will extend this with KV/D1/R2 bindings, Cron triggers, and
 * Durable Objects sections when session/db/storage adapters ship.
 */

export interface WranglerConfigOptions {
  /** Worker name (appears in Cloudflare dashboard URL). Required. */
  projectName: string;
  /**
   * Relative path from `wrangler.toml` to the built worker script. Defaults
   * to `.mandu/workers/worker.js`.
   */
  main?: string;
  /**
   * Compatibility date. Should match Cloudflare's docs — defaults to a
   * known-good recent date so published projects don't drift silently.
   * @default "2025-09-23"
   */
  compatibilityDate?: string;
  /**
   * Compatibility flags. `nodejs_compat` is enabled by default — it's
   * required for `AsyncLocalStorage` and a handful of Node-shimmed imports
   * we pull in transitively (e.g. `node:buffer` via React).
   * @default ["nodejs_compat"]
   */
  compatibilityFlags?: string[];
  /**
   * Directory of static assets (relative to project root). When set, emits
   * a `[assets]` block so Wrangler serves the files before routing to the
   * worker. Usually `"public"`.
   */
  assetsDir?: string;
  /**
   * Binding name for the assets handler. Only used when `assetsDir` is set.
   * @default "ASSETS"
   */
  assetsBinding?: string;
  /**
   * Environment variables exposed on `env.X`. Values are stored in plain
   * text — secrets should go through `wrangler secret put` instead.
   */
  vars?: Record<string, string>;
  /**
   * Custom domains / routes (e.g. `"example.com/*"`). When empty the worker
   * is only reachable via the `.workers.dev` subdomain.
   */
  routes?: Array<{ pattern: string; zoneName?: string; custom_domain?: boolean }>;
  /**
   * Workers Cron Triggers. Phase 15.2+ will wire these into
   * `@mandujs/core/scheduler` automatically.
   */
  crons?: string[];
  /**
   * Opt-in observability logs. Cloudflare offers logpush/tail — this maps
   * to `[observability]` in wrangler.toml.
   */
  observability?: boolean;
}

/**
 * Generate a `wrangler.toml` file contents as a string. The caller is
 * responsible for writing it to disk; this keeps the function trivially
 * testable.
 *
 * @example
 * ```ts
 * const toml = generateWranglerConfig({
 *   projectName: "my-mandu-app",
 *   assetsDir: "public",
 * });
 * await Bun.write("./wrangler.toml", toml);
 * ```
 */
export function generateWranglerConfig(options: WranglerConfigOptions): string {
  if (!options.projectName || typeof options.projectName !== "string") {
    throw new Error("generateWranglerConfig: projectName is required");
  }
  if (!/^[a-z0-9-]+$/.test(options.projectName)) {
    throw new Error(
      `generateWranglerConfig: projectName must match /^[a-z0-9-]+$/ ` +
        `(got: "${options.projectName}")`
    );
  }

  const main = options.main ?? ".mandu/workers/worker.js";
  const compatDate = options.compatibilityDate ?? "2025-09-23";
  const compatFlags = options.compatibilityFlags ?? ["nodejs_compat"];

  const lines: string[] = [];
  lines.push(`# Generated by mandu build --target=workers`);
  lines.push(`# See https://developers.cloudflare.com/workers/wrangler/configuration/`);
  lines.push("");
  lines.push(`name = "${options.projectName}"`);
  lines.push(`main = "${main}"`);
  lines.push(`compatibility_date = "${compatDate}"`);

  if (compatFlags.length > 0) {
    lines.push(`compatibility_flags = ${JSON.stringify(compatFlags)}`);
  }

  if (options.observability) {
    lines.push("");
    lines.push("[observability]");
    lines.push("enabled = true");
  }

  if (options.assetsDir) {
    const binding = options.assetsBinding ?? "ASSETS";
    lines.push("");
    lines.push("[assets]");
    lines.push(`directory = "${options.assetsDir}"`);
    lines.push(`binding = "${binding}"`);
  }

  if (options.vars && Object.keys(options.vars).length > 0) {
    lines.push("");
    lines.push("[vars]");
    for (const [key, value] of Object.entries(options.vars)) {
      if (!/^[A-Z_][A-Z0-9_]*$/.test(key)) {
        throw new Error(
          `generateWranglerConfig: var name "${key}" must be UPPER_SNAKE_CASE`
        );
      }
      lines.push(`${key} = ${JSON.stringify(value)}`);
    }
  }

  if (options.routes && options.routes.length > 0) {
    for (const route of options.routes) {
      lines.push("");
      lines.push("[[routes]]");
      lines.push(`pattern = ${JSON.stringify(route.pattern)}`);
      if (route.zoneName) {
        lines.push(`zone_name = ${JSON.stringify(route.zoneName)}`);
      }
      if (route.custom_domain) {
        lines.push(`custom_domain = true`);
      }
    }
  }

  if (options.crons && options.crons.length > 0) {
    lines.push("");
    lines.push("[triggers]");
    lines.push(`crons = ${JSON.stringify(options.crons)}`);
  }

  // Trailing newline for POSIX file conventions.
  return lines.join("\n") + "\n";
}
