import { expect, test } from "bun:test";
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { format } from "prettier";
import { scaffoldApp } from "../src/create.ts";
import { currentCliVersion } from "../src/update-notifier.ts";

test("scaffoldApp creates a comprehensive .gitignore", () => {
  const parent = mkdtempSync(join(tmpdir(), "wrnexus-create-"));
  const root = join(parent, "demo-app");

  try {
    scaffoldApp(root, "demo-app");

    const file = join(root, ".gitignore");
    expect(existsSync(file)).toBe(true);
    const contents = readFileSync(file, "utf8");
    for (const entry of [
      "node_modules/",
      "dist/",
      ".wrnexus/",
      ".env.*",
      "!.env.example",
      "!.env.*.example",
      "*.log",
      "*.db",
      "uploads/",
      "coverage/",
      "mobile/android/",
      ".vscode/",
      "*.tsbuildinfo",
    ]) {
      expect(contents).toContain(entry);
    }
  } finally {
    rmSync(parent, { recursive: true, force: true });
  }
});

test("scaffoldApp includes production build and start scripts", () => {
  const parent = mkdtempSync(join(tmpdir(), "wrnexus-create-"));
  const root = join(parent, "production-app");

  try {
    scaffoldApp(root, "production-app");
    const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));

    expect(pkg.scripts.build).toBe("wrnexus build .");
    expect(pkg.scripts.start).toBe("bun dist/server.js");
    expect(pkg.scripts.production).toBe("bun run build && bun run start");
    expect(pkg.scripts.typecheck).toBe("tsc --noEmit");
    expect(pkg.scripts.test).toBe("wrnexus test .");
    expect(pkg.scripts.check).toBe(
      "bun run typecheck && bun run lint && bun run test && bun run format:check",
    );
  } finally {
    rmSync(parent, { recursive: true, force: true });
  }
});

test("scaffoldApp includes the complete v0.8 configuration and starter structure", () => {
  const parent = mkdtempSync(join(tmpdir(), "wrnexus-create-"));
  const root = join(parent, "complete-app");

  try {
    scaffoldApp(root, "complete-app");
    const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
    const config = readFileSync(join(root, "wrnexus.config.ts"), "utf8");

    for (const packageName of [
      "@wrnexus/auth",
      "@wrnexus/captcha",
      "@wrnexus/db",
      "@wrnexus/encryption",
      "@wrnexus/i18n",
      "@wrnexus/image",
      "@wrnexus/jwt",
      "@wrnexus/observability",
      "@wrnexus/realtime",
      "@wrnexus/security",
      "@wrnexus/store",
      "@wrnexus/ui",
      "@wrnexus/uploader",
      "@wrnexus/validation",
    ]) {
      expect(pkg.dependencies[packageName]).toBe(currentCliVersion());
    }

    for (const block of [
      "plugins:",
      "imports:",
      "types:",
      "stores:",
      "compatibility:",
      "performance:",
      "observability:",
      "tenancy:",
      "build:",
      "navigation:",
      "devToolbar:",
      "theme:",
      "i18n:",
      "db:",
      "databases:",
      "storage:",
      "realtime:",
      "profiles:",
    ]) {
      expect(config).toContain(block);
    }

    for (const relative of [
      ".env.example",
      ".env.test.example",
      "app/locales/en.json",
      "app/db/migrations/0001_init.sql",
      "app/db/seed.ts",
      "app/schemas/contact.ts",
      "app/realtime/chat.ts",
      "test/smoke.test.ts",
    ]) {
      expect(existsSync(join(root, relative))).toBe(true);
    }
  } finally {
    rmSync(parent, { recursive: true, force: true });
  }
});

test("scaffoldApp pins the current framework and exposes llms.txt publicly", () => {
  const parent = mkdtempSync(join(tmpdir(), "wrnexus-create-"));
  const root = join(parent, "current-app");

  try {
    scaffoldApp(root, "current-app");
    const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
    const version = currentCliVersion();

    expect(pkg.wrnexus.version).toBe(version);
    expect(pkg.dependencies["@wrnexus/helpers"]).toBe(version);
    for (const dependency of Object.values(pkg.dependencies) as string[]) {
      expect(dependency).toBe(version);
    }
    expect(pkg.devDependencies["@wrnexus/cli"]).toBe(version);
    expect(pkg.devDependencies["@iconify/tailwind4"]).toBe("^1.2.3");
    expect(pkg.devDependencies["@iconify-json/lucide"]).toBe("^1.2.118");
    const globalCss = readFileSync(join(root, "app", "styles", "global.css"), "utf8");
    expect(globalCss).toContain('@plugin "@iconify/tailwind4";');
    expect(globalCss).toContain('"Plus Jakarta Sans"');
    expect(globalCss).toContain("--wire-font-sans");
    const appConfig = readFileSync(join(root, "wrnexus.config.ts"), "utf8");
    expect(appConfig).toContain('family: "Plus Jakarta Sans"');
    expect(appConfig).toContain("weights: [400, 500, 600, 700]");
    expect(existsSync(join(root, "public", "llms.txt"))).toBe(true);
    expect(existsSync(join(root, "llms.txt"))).toBe(false);
    expect(readFileSync(join(root, ".prettierignore"), "utf8")).toContain("CLAUDE.md");
    expect(readFileSync(join(root, ".vscode", "settings.json"), "utf8")).toContain(
      '"editor.formatOnSave": true',
    );
    expect(readFileSync(join(root, ".vscode", "settings.json"), "utf8")).toContain(
      '"editor.defaultFormatter": "wrnexus.wrnexus"',
    );
    expect(readFileSync(join(root, ".vscode", "extensions.json"), "utf8")).toContain(
      "wrnexus.wrnexus",
    );
    expect(readFileSync(join(root, "app", "pages", "index.wrn"), "utf8")).toContain(
      'href="/api/hello"',
    );
    expect(existsSync(join(root, "app", "pages", "about.wrn"))).toBe(true);
  } finally {
    rmSync(parent, { recursive: true, force: true });
  }
});

test("scaffoldApp emits Prettier-compliant editor and framework files", async () => {
  const parent = mkdtempSync(join(tmpdir(), "wrnexus-create-"));
  const root = join(parent, "formatted-app");

  try {
    scaffoldApp(root, "formatted-app");
    for (const relative of [
      "app/styles/global.css",
      "eslint.config.js",
      ".vscode/settings.json",
      ".vscode/extensions.json",
    ]) {
      const file = join(root, relative);
      const source = readFileSync(file, "utf8");
      const formatted = await format(source, {
        filepath: file,
        printWidth: 100,
        tabWidth: 2,
        useTabs: false,
        semi: true,
        singleQuote: false,
        trailingComma: "all",
        endOfLine: "lf",
      });
      expect(formatted).toBe(source);
    }
  } finally {
    rmSync(parent, { recursive: true, force: true });
  }
});
