/**
 * `netlify.toml` generator for Netlify Edge Functions.
 *
 * Emits a minimal Netlify config covering:
 *   - [build] block (functions directory + dev command)
 *   - [[edge_functions]] catch-all entry pointing at the SSR handler
 *   - Optional [[edge_functions]] rewrites / path overrides
 *   - Optional [functions] block for scheduled functions (Netlify Scheduled)
 *
 * Netlify uses TOML for its config file. We hand-assemble the TOML to
 * avoid a new dependency on a TOML serializer.
 */

export interface NetlifyEdgeConfigOptions {
  /** Project / site name — used for the leading comment only. */
  projectName: string;
  /**
   * Relative path to the Edge Function entry. Defaults to
   * `netlify/edge-functions/ssr.ts`. Must live under
   * `netlify/edge-functions/` for Netlify to auto-detect it.
   */
  functionPath?: string;
  /**
   * Function name for the [[edge_functions]] block. Defaults to `ssr`.
   * Must match the exported function's identifier for logging purposes
   * and be unique across your site's edge functions.
   */
  functionName?: string;
  /**
   * Path glob for the SSR catch-all. Defaults to `/*` so Mandu sees
   * every request. Users can pass a narrower pattern (e.g. `/api/*`)
   * if they only want to route specific paths through Mandu.
   */
  path?: string;
  /**
   * Extra edge_functions entries for custom routing / middleware.
   */
  extraEdgeFunctions?: Array<{
    path: string;
    function: string;
    cache?: "off" | "manual";
  }>;
  /** Build publish directory. Defaults to `public`. */
  publishDir?: string;
  /** Dev server command for `netlify dev`. Defaults to `mandu dev`. */
  devCommand?: string;
  /** Build command. Defaults to `mandu build --target=netlify-edge`. */
  buildCommand?: string;
  /**
   * Netlify scheduled functions. Each entry maps to a `[functions."name"]`
   * block with a `schedule` cron value.
   */
  scheduled?: Array<{ name: string; schedule: string }>;
}

const TOML_STRING_RE = /^[^"\\\n\r]*$/;

function quoteTomlString(value: string): string {
  // Reject control chars / quotes / backslashes to keep the generator
  // unambiguous. Callers should preserve ASCII printable content only.
  if (!TOML_STRING_RE.test(value)) {
    throw new Error(
      `generateNetlifyEdgeConfig: string value "${value}" contains characters ` +
        `that require TOML escaping — use a simpler value.`
    );
  }
  return `"${value}"`;
}

/**
 * Generate `netlify.toml` contents as a string.
 *
 * @example
 * ```ts
 * const toml = generateNetlifyEdgeConfig({ projectName: "my-mandu-app" });
 * await Bun.write("./netlify.toml", toml);
 * ```
 */
export function generateNetlifyEdgeConfig(
  options: NetlifyEdgeConfigOptions
): string {
  if (!options.projectName || typeof options.projectName !== "string") {
    throw new Error("generateNetlifyEdgeConfig: projectName is required");
  }
  if (!/^[a-z0-9-]+$/.test(options.projectName)) {
    throw new Error(
      `generateNetlifyEdgeConfig: projectName must match /^[a-z0-9-]+$/ ` +
        `(got: "${options.projectName}")`
    );
  }

  const functionPath = options.functionPath ?? "netlify/edge-functions/ssr.ts";
  if (!functionPath.startsWith("netlify/edge-functions/")) {
    throw new Error(
      `generateNetlifyEdgeConfig: functionPath must live under ` +
        `netlify/edge-functions/ (got: "${functionPath}")`
    );
  }

  const functionName = options.functionName ?? "ssr";
  if (!/^[a-z][a-z0-9_-]*$/i.test(functionName)) {
    throw new Error(
      `generateNetlifyEdgeConfig: functionName must be a valid identifier ` +
        `(got: "${functionName}")`
    );
  }

  const path = options.path ?? "/*";
  const publishDir = options.publishDir ?? "public";
  const devCommand = options.devCommand ?? "mandu dev";
  const buildCommand = options.buildCommand ?? "mandu build --target=netlify-edge";

  const lines: string[] = [];
  lines.push(`# Generated by mandu build --target=netlify-edge`);
  lines.push(`# Project: ${options.projectName}`);
  lines.push(`# See https://docs.netlify.com/configure-builds/file-based-configuration/`);
  lines.push("");

  // [build]
  lines.push("[build]");
  lines.push(`  command = ${quoteTomlString(buildCommand)}`);
  lines.push(`  publish = ${quoteTomlString(publishDir)}`);
  lines.push(`  edge_functions = "netlify/edge-functions"`);

  // [dev]
  lines.push("");
  lines.push("[dev]");
  lines.push(`  command = ${quoteTomlString(devCommand)}`);
  lines.push(`  framework = "#custom"`);
  lines.push(`  targetPort = 3333`);

  // Primary [[edge_functions]] entry — the SSR catch-all.
  lines.push("");
  lines.push("[[edge_functions]]");
  lines.push(`  path = ${quoteTomlString(path)}`);
  lines.push(`  function = ${quoteTomlString(functionName)}`);

  // Additional edge function entries.
  if (options.extraEdgeFunctions && options.extraEdgeFunctions.length > 0) {
    for (const entry of options.extraEdgeFunctions) {
      if (!entry.path || !entry.function) {
        throw new Error(
          `generateNetlifyEdgeConfig: extraEdgeFunctions entries require both path and function`
        );
      }
      lines.push("");
      lines.push("[[edge_functions]]");
      lines.push(`  path = ${quoteTomlString(entry.path)}`);
      lines.push(`  function = ${quoteTomlString(entry.function)}`);
      if (entry.cache) {
        lines.push(`  cache = ${quoteTomlString(entry.cache)}`);
      }
    }
  }

  // Scheduled functions live under [functions."<name>"] and use a
  // Serverless Function (not Edge) — but we wire the config here so
  // users have a single source of truth.
  if (options.scheduled && options.scheduled.length > 0) {
    for (const entry of options.scheduled) {
      if (!entry.name || !entry.schedule) {
        throw new Error(
          `generateNetlifyEdgeConfig: scheduled entries require both name and schedule`
        );
      }
      if (!/^[a-z][a-z0-9_-]*$/i.test(entry.name)) {
        throw new Error(
          `generateNetlifyEdgeConfig: scheduled function name "${entry.name}" ` +
            `must be a valid identifier`
        );
      }
      lines.push("");
      lines.push(`[functions.${entry.name}]`);
      lines.push(`  schedule = ${quoteTomlString(entry.schedule)}`);
    }
  }

  return lines.join("\n") + "\n";
}
