/**
 * netlify.toml generator tests.
 *
 * Netlify uses TOML for config; we hand-assemble lines so tests focus on
 * the resulting text shape. Netlify's own build validates semantics at
 * `netlify deploy` time.
 */

import { describe, it, expect } from "bun:test";
import { generateNetlifyEdgeConfig } from "../src/netlify/netlify-config";

describe("generateNetlifyEdgeConfig", () => {
  it("emits a minimal valid config with just projectName", () => {
    const toml = generateNetlifyEdgeConfig({ projectName: "my-app" });

    expect(toml).toContain("# Generated by mandu build --target=netlify-edge");
    expect(toml).toContain("# Project: my-app");
    expect(toml).toContain("[build]");
    expect(toml).toContain(`command = "mandu build --target=netlify-edge"`);
    expect(toml).toContain(`publish = "public"`);
    expect(toml).toContain(`edge_functions = "netlify/edge-functions"`);

    expect(toml).toContain("[[edge_functions]]");
    expect(toml).toContain(`path = "/*"`);
    expect(toml).toContain(`function = "ssr"`);
    expect(toml.endsWith("\n")).toBe(true);
  });

  it("honors a custom function path + name", () => {
    const toml = generateNetlifyEdgeConfig({
      projectName: "app",
      functionPath: "netlify/edge-functions/mandu.ts",
      functionName: "mandu",
    });
    expect(toml).toContain(`function = "mandu"`);
  });

  it("rejects function paths outside netlify/edge-functions/", () => {
    expect(() =>
      generateNetlifyEdgeConfig({
        projectName: "app",
        functionPath: "server/handler.ts",
      })
    ).toThrow(/must live under netlify\/edge-functions\//);
  });

  it("rejects function names that are not valid identifiers", () => {
    expect(() =>
      generateNetlifyEdgeConfig({ projectName: "app", functionName: "1invalid" })
    ).toThrow(/must be a valid identifier/);

    expect(() =>
      generateNetlifyEdgeConfig({ projectName: "app", functionName: "has spaces" })
    ).toThrow(/must be a valid identifier/);

    expect(() =>
      generateNetlifyEdgeConfig({ projectName: "app", functionName: "" })
    ).toThrow(/must be a valid identifier/);
  });

  it("honors a narrower path pattern", () => {
    const toml = generateNetlifyEdgeConfig({
      projectName: "app",
      path: "/api/*",
    });
    expect(toml).toContain(`path = "/api/*"`);
  });

  it("emits extra [[edge_functions]] entries in order", () => {
    const toml = generateNetlifyEdgeConfig({
      projectName: "app",
      extraEdgeFunctions: [
        { path: "/auth/*", function: "auth", cache: "off" },
        { path: "/static/*", function: "static_passthrough", cache: "manual" },
      ],
    });
    // Primary SSR block plus 2 extra blocks = 3 occurrences total.
    const matches = toml.match(/\[\[edge_functions\]\]/g) ?? [];
    expect(matches.length).toBe(3);
    expect(toml).toContain(`function = "auth"`);
    expect(toml).toContain(`function = "static_passthrough"`);
    expect(toml).toContain(`cache = "off"`);
    expect(toml).toContain(`cache = "manual"`);
  });

  it("rejects extra edge_function entries missing path or function", () => {
    expect(() =>
      generateNetlifyEdgeConfig({
        projectName: "app",
        extraEdgeFunctions: [{ path: "", function: "x" }],
      })
    ).toThrow(/require both path and function/);
  });

  it("emits scheduled function blocks with per-name headers", () => {
    const toml = generateNetlifyEdgeConfig({
      projectName: "app",
      scheduled: [
        { name: "cleanup", schedule: "@daily" },
        { name: "warmup", schedule: "0 */5 * * *" },
      ],
    });
    expect(toml).toContain("[functions.cleanup]");
    expect(toml).toContain(`schedule = "@daily"`);
    expect(toml).toContain("[functions.warmup]");
    expect(toml).toContain(`schedule = "0 */5 * * *"`);
  });

  it("rejects scheduled entries missing name or schedule", () => {
    expect(() =>
      generateNetlifyEdgeConfig({
        projectName: "app",
        scheduled: [{ name: "", schedule: "@daily" }],
      })
    ).toThrow(/require both name and schedule/);

    expect(() =>
      generateNetlifyEdgeConfig({
        projectName: "app",
        scheduled: [{ name: "cleanup", schedule: "" }],
      })
    ).toThrow(/require both name and schedule/);
  });

  it("rejects scheduled function names that are not identifiers", () => {
    expect(() =>
      generateNetlifyEdgeConfig({
        projectName: "app",
        scheduled: [{ name: "has spaces", schedule: "@daily" }],
      })
    ).toThrow(/must be a valid identifier/);
  });

  it("honors custom publishDir, devCommand, and buildCommand", () => {
    const toml = generateNetlifyEdgeConfig({
      projectName: "app",
      publishDir: "dist",
      devCommand: "mandu dev --port 3000",
      buildCommand: "bun run build:edge",
    });
    expect(toml).toContain(`publish = "dist"`);
    expect(toml).toContain(`command = "mandu dev --port 3000"`);
    expect(toml).toContain(`command = "bun run build:edge"`);
  });

  it("rejects invalid project names", () => {
    expect(() => generateNetlifyEdgeConfig({ projectName: "" })).toThrow(
      /projectName is required/
    );
    expect(() =>
      generateNetlifyEdgeConfig({ projectName: "UPPERCASE" })
    ).toThrow(/must match/);
    expect(() =>
      generateNetlifyEdgeConfig({ projectName: "spaces not allowed" })
    ).toThrow(/must match/);
    expect(() =>
      generateNetlifyEdgeConfig({ projectName: "../escape" })
    ).toThrow(/must match/);
  });

  it("rejects TOML-unsafe string values (quotes / backslashes)", () => {
    expect(() =>
      generateNetlifyEdgeConfig({
        projectName: "app",
        buildCommand: 'echo "hello"',
      })
    ).toThrow(/require TOML escaping/);
  });
});
