/**
 * `wrnexus create <app-name>` — scaffold a new app from an inline template.
 *
 * Kept dependency-free and explicit: the template files live here as strings so
 * scaffolding works without copying from anywhere on disk.
 */

import { existsSync, mkdirSync, writeFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { AI_GUIDE, CLAUDE_MD } from "./ai-guide.ts";
import { currentCliVersion } from "./update-notifier.ts";

const frameworkVersion = currentCliVersion();

/** Map of file (relative to app root) -> contents. */
const TEMPLATE: Record<string, string> = {
  // AI/agent context: teaches Claude Code / Cursor / Copilot the WrNexus conventions.
  "CLAUDE.md": CLAUDE_MD,
  "public/llms.txt": AI_GUIDE,
  ".gitignore": `# Dependencies
node_modules/

# WRNexusJS and production builds
dist/
.wrnexus/
**/.wrnexus/
coverage/

# Environment files and local secrets
.env
.env.*
!.env.example
!.env.*.example

# Logs and runtime files
*.log
logs/
*.pid
*.pid.lock

# Local databases
*.db
*.db-shm
*.db-wal
*.sqlite
*.sqlite3
uploads/

# Generated native projects
mobile/android/
mobile/ios/
mobile/.expo/

# Editors and operating systems
.idea/
.vscode/*
!.vscode/settings.json
!.vscode/extensions.json
*.swp
*.swo
.DS_Store
Thumbs.db

# TypeScript and test caches
*.tsbuildinfo
.eslintcache
.nyc_output/
`,
  "package.json": `{
  "name": "APP_NAME",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "wrnexus": {
    "version": "${frameworkVersion}"
  },
  "scripts": {
    "dev": "wrnexus dev .",
    "build": "wrnexus build .",
    "start": "bun dist/server.js",
    "production": "bun run build && bun run start",
    "typecheck": "tsc --noEmit",
    "test": "wrnexus test .",
    "test:watch": "wrnexus test . --watch",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "format": "prettier . --write",
    "format:check": "prettier . --check",
    "doctor": "wrnexus doctor .",
    "analyze": "wrnexus analyze .",
    "inspect": "wrnexus inspect packages .",
    "check": "bun run typecheck && bun run lint && bun run test && bun run format:check"
  },
  "dependencies": {
    "@wrnexus/ai": "${frameworkVersion}",
    "@wrnexus/auth": "${frameworkVersion}",
    "@wrnexus/captcha": "${frameworkVersion}",
    "@wrnexus/core": "${frameworkVersion}",
    "@wrnexus/csr": "${frameworkVersion}",
    "@wrnexus/db": "${frameworkVersion}",
    "@wrnexus/dev-server": "${frameworkVersion}",
    "@wrnexus/encryption": "${frameworkVersion}",
    "@wrnexus/helpers": "${frameworkVersion}",
    "@wrnexus/i18n": "${frameworkVersion}",
    "@wrnexus/image": "${frameworkVersion}",
    "@wrnexus/jwt": "${frameworkVersion}",
    "@wrnexus/observability": "${frameworkVersion}",
    "@wrnexus/realtime": "${frameworkVersion}",
    "@wrnexus/security": "${frameworkVersion}",
    "@wrnexus/store": "${frameworkVersion}",
    "@wrnexus/styles": "${frameworkVersion}",
    "@wrnexus/tracking": "${frameworkVersion}",
    "@wrnexus/ui": "${frameworkVersion}",
    "@wrnexus/uploader": "${frameworkVersion}",
    "@wrnexus/validation": "${frameworkVersion}",
    "@wrnexus/authz": "${frameworkVersion}"
  },
  "devDependencies": {
    "@wrnexus/cli": "${frameworkVersion}",
    "@eslint/js": "^9.0.0",
    "@iconify-json/lucide": "^1.2.118",
    "@iconify/tailwind4": "^1.2.3",
    "@tailwindcss/cli": "^4.0.0",
    "@types/bun": "latest",
    "eslint": "^9.0.0",
    "prettier": "latest",
    "tailwindcss": "^4.0.0",
    "typescript": "^5.5.0",
    "typescript-eslint": "latest"
  }
}
`,
  "tsconfig.json": `{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "lib": ["ESNext", "DOM", "DOM.Iterable"],
    "types": ["bun"],
    "strict": true,
    "skipLibCheck": true,
    "noEmit": true,
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": false,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "jsx": "react-jsx",
    "jsxImportSource": "@wrnexus/core"
  },
  "include": ["app", "test", "wrnexus.config.ts"],
  "exclude": ["node_modules", "dist", "**/dist", "**/.wrnexus"]
}
`,
  ".env.example": `# Copy to .env for local development. Never commit real secrets.
WRNEXUS_PUBLIC_ORIGIN=http://localhost:3000
DATABASE_URL=file:./dev.db
REDIS_URL=redis://localhost:6379
AUTH_SECRET=replace-with-at-least-32-random-characters
ENCRYPTION_KEY=replace-with-a-base64-encoded-32-byte-key
ANTHROPIC_API_KEY=
OTEL_EXPORTER_OTLP_ENDPOINT=
`,
  ".env.test.example": `WRNEXUS_PUBLIC_ORIGIN=http://localhost:3000
DATABASE_URL=file:./test.db
AUTH_SECRET=test-only-secret-replace-outside-tests
`,
  "eslint.config.js": `import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import js from "@eslint/js";
import tseslint from "typescript-eslint";

const tsconfigRootDir = dirname(fileURLToPath(import.meta.url));

export default tseslint.config(
  {
    ignores: [
      "node_modules/**",
      "dist/**",
      ".wrnexus/**",
      "**/.wrnexus/**",
      "mobile/android/**",
      "mobile/ios/**",
    ],
  },
  {
    languageOptions: {
      parserOptions: {
        tsconfigRootDir,
      },
    },
  },
  js.configs.recommended,
  ...tseslint.configs.recommended,
  {
    files: ["**/*.{ts,tsx}"],
    rules: {
      "no-undef": "off",
      "no-console": "off",
      "@typescript-eslint/no-explicit-any": "off",
      "@typescript-eslint/no-unused-vars": [
        "warn",
        {
          argsIgnorePattern: "^_",
          varsIgnorePattern: "^_",
          caughtErrorsIgnorePattern: "^_",
        },
      ],
    },
  },
);
`,
  ".prettierrc.json": `{
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": false,
  "trailingComma": "all",
  "endOfLine": "lf"
}
`,
  ".prettierignore": `node_modules/
dist/
.wrnexus/
**/.wrnexus/
*.log
CLAUDE.md
`,
  ".editorconfig": `root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
`,
  ".vscode/settings.json": `{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "prettier.requireConfig": true,
  "[wrn]": {
    "editor.defaultFormatter": "wrnexus.wrnexus",
    "editor.formatOnSave": true
  }
}
`,
  ".vscode/extensions.json": `{
  "recommendations": ["wrnexus.wrnexus", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
}
`,
  "wrnexus.config.ts": `import type { AppConfig } from "@wrnexus/styles";

const config: AppConfig = {
  compatibilityDate: "2026-08-02",
  frameworkBehaviour: 1,
  // v0.8 defaults: explicit imports, strict template types, safe stores, and
  // automatic progressive navigation. Package plugins are discovered from the
  // installed packages above; add custom plugins to this array when needed.
  plugins: [],
  imports: { mode: "explicit", autoImport: true, aliases: { "@": "./app" } },
  types: {
    strict: true,
    noImplicitAny: true,
    strictNullChecks: true,
    checkTemplates: true,
    checkComponentProps: true,
    generateDeclarations: true,
  },
  functions: { legacyDefaultRuntime: "current" },
  stores: { strictMutations: true, persistence: true },
  compatibility: {
    legacyEmit: false,
    legacyEventProps: false,
    legacyComponentDiscovery: false,
    stringLayouts: false,
  },
  experimental: {},

  performance: {
    enforcement: "warn",
    analyze: true,
    budgets: {
      routeJsBytes: 50 * 1024,
      routeCssBytes: 25 * 1024,
      lcpMs: 2_500,
      inpMs: 200,
      cls: 0.1,
    },
  },
  observability: {
    enabled: true,
    serviceName: "APP_SLUG",
    serverTiming: true,
    sampleRate: 1,
    exporter: process.env.OTEL_EXPORTER_OTLP_ENDPOINT ? "otlp" : "none",
    endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
    webVitals: true,
  },
  tenancy: { mode: "domain", required: false, rootDomains: ["localhost"] },
  build: { cache: true, sourceMaps: true, report: true, adapter: "bun" },
  navigation: { mode: "auto" },
  devToolbar: { enabled: true, position: "bottom-center", openEditor: true },

  mobile: {
    enabled: true,
    appId: "com.example.APP_SLUG",
    appName: "APP_NAME",
    userAgent: "WrNexusMobile",
    backgroundColor: "#0f172a",
    // layout: "mobile", // app/layouts/mobile.wrn
    // icon: "resources/icon.png",
  },

  // PWA support is enabled automatically. Override any install metadata here.
  pwa: {
    name: "APP_NAME",
    shortName: "APP_NAME",
    display: "standalone",
    themeColor: "#6366f1",
    backgroundColor: "#0f172a",
  },

  seo: {
    title: "APP_NAME",
    titleTemplate: "%s | APP_NAME",
    // Set WRNEXUS_PUBLIC_ORIGIN in production when TLS terminates at a proxy.
    canonicalBase: process.env.WRNEXUS_PUBLIC_ORIGIN,
    description: "An SSR-first WrNexus app.",
    robots: "index,follow",
    themeColor: "#6366f1",
  },

  styles: {
    entry: "app/styles/global.css",

    // Tailwind v4 build. Runs once at dev-serve time (cached; re-run on restart)
    // and at \`wrnexus build\`. \`@tailwindcss/cli\` writes to stdout, so we capture
    // and return the final CSS. Delete this hook to drop Tailwind — global.css is
    // still bundled and served as-is.
    process: async ({ entryPath, appRoot, mode }) => {
      const args = ["@tailwindcss/cli", "-i", entryPath!];
      if (mode === "production") args.push("--minify");
      return await Bun.$.cwd(appRoot)\`bunx \${args}\`.text();
    },
  },

  // Fonts — optimized preconnect, subsetted weights, font-display, and CSP.
  fonts: {
    sans: '"Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif',
    google: [{ family: "Plus Jakarta Sans", weights: [400, 500, 600, 700] }],
  },
  //
  //   // Or self-host (fastest, no third party) — drop files in public/fonts/:
  //   // local: [{ family: "Inter", src: "/fonts/inter.woff2", weight: "100 900", preload: true }],

  theme: { palette: "violet", default: "light" },
  i18n: { default: "en", locales: ["en"] },
  db: { driver: "sqlite", url: process.env.DATABASE_URL ?? "file:./dev.db" },
  databases: {},
  storage: {
    default: "public",
    stores: {
      public: {
        driver: "local",
        access: "public",
        dir: "uploads/public",
        maxBytes: 10_000_000,
        accept: ["image/*", "application/pdf"],
      },
      private: {
        driver: "local",
        access: "private",
        dir: "uploads/private",
        maxBytes: 10_000_000,
      },
    },
  },
  realtime: { scale: Boolean(process.env.REDIS_URL), redisUrl: process.env.REDIS_URL },
  port: Number(process.env.PORT ?? 3000),
  security: {
    cors: { enabled: false },
  },
  profiles: {
    development: {},
    test: {
      db: { driver: "sqlite", url: "file:./test.db" },
      observability: { exporter: "none", sampleRate: 0 },
    },
    staging: {
      seo: { robots: "noindex,nofollow" },
      performance: { enforcement: "error" },
      build: { sourceMaps: true, report: true },
    },
    production: {
      seo: { canonicalBase: process.env.WRNEXUS_PUBLIC_ORIGIN },
      performance: { enforcement: "error" },
      build: { sourceMaps: false, report: true },
      devToolbar: false,
    },
  },
};

export default config;
`,
  "public/robots.txt": `User-agent: *
Allow: /
`,
  "app/locales/en.json": `{
  "common": {
    "appName": "APP_NAME",
    "welcome": "Welcome to APP_NAME"
  }
}
`,
  "app/db/migrations/0001_init.sql": `-- Create application tables here.
-- Run with: bunx wrnexus db migrate
`,
  "app/db/seed.ts": `// Add deterministic development seed data here.
export async function seed(): Promise<void> {}
`,
  "app/schemas/contact.ts": `import { v } from "@wrnexus/validation";

export const contactSchema = v.object({
  email: v.string().email(),
  message: v.string().min(10).max(2_000),
});
`,
  "app/styles/global.css": `/*
 * Global stylesheet. Tailwind v4 is compiled by the styles.process hook in
 * wrnexus.config.ts and served at /__wrnexus/styles.css on every page.
 *
 * @source tells Tailwind which files to scan for class names.
 */
@import "tailwindcss";
@plugin "@iconify/tailwind4";
@source "../**/*.wrn";
@source "../**/*.tsx";

/* Make Tailwind's \`dark:\` variant follow the framework's data-theme attribute
 * (set on <html> by the theme system), not the OS setting. Any element with
 * data-wire-theme-toggle flips it. */
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));

body {
  font-family: var(--wire-font-sans, "Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif);
}
`,
  "app/layouts/document.wrn": `// Global document layout. The framework renders this once around the selected
// page layout and merges SEO metadata, styles, and scripts into <head>/<body>.
// Request cookies, resolved theme, language, URL, and pathname are available
// as SSR props, so document attributes do not need a client-side correction.
layout Document {
  props {
    cookies = {}
    theme = "light"
    language = "en"
    url = ""
    pathname = "/"
  }

  view {
    <html>
      <head></head>
      <body>
        <div id="app"><slot /></div>
      </body>
    </html>
  }
}
`,
  "app/pages/index.wrn": `// Home page (route: /). SSR-first: the view is server-rendered, then components
// (.wrn files under app/components) hydrate in the browser. Styled with Tailwind.
page Home {
  seo {
    title = "Home"
    description = "APP_NAME — built with WrNexus, an SSR-first Bun framework."
  }

  view {
    <main class="relative min-h-screen overflow-hidden bg-white text-slate-900 dark:bg-[#0b0f1e] dark:text-slate-100">
      <div aria-hidden="true" class="pointer-events-none absolute inset-x-0 -top-40 mx-auto h-96 max-w-2xl rounded-full bg-indigo-500/20 blur-3xl"></div>

      <div class="relative mx-auto flex min-h-screen max-w-3xl flex-col px-6">
        <header class="flex items-center justify-between py-6">
          <span class="flex items-center gap-2.5 font-semibold tracking-tight">
            <span class="grid h-7 w-7 place-items-center rounded-md bg-gradient-to-br from-indigo-500 to-violet-600 text-sm font-bold text-white">W</span>
            APP_NAME
          </span>
          <button data-wire-theme-toggle class="rounded-md border border-slate-200 px-3 py-1.5 text-sm text-slate-600 transition hover:border-slate-300 hover:text-slate-900 dark:border-white/10 dark:text-slate-400 dark:hover:border-white/20 dark:hover:text-white">
            Toggle theme
          </button>
        </header>

        <section class="flex flex-1 flex-col items-center justify-center py-16 text-center">
          <p class="font-mono text-xs uppercase tracking-[0.2em] text-indigo-500 dark:text-indigo-400">SSR-first · Bun-native</p>

          <h1 class="mt-5 text-4xl font-bold leading-[1.1] tracking-tight sm:text-6xl">
            Server-rendered.<br />
            Instantly <span class="bg-gradient-to-r from-indigo-500 to-violet-500 bg-clip-text text-transparent">interactive</span>.
          </h1>

          <p class="mt-5 max-w-md text-base leading-relaxed text-slate-600 dark:text-slate-400">
            APP_NAME runs on WrNexus — write <code class="rounded bg-slate-100 px-1.5 py-0.5 font-mono text-[0.85em] text-slate-800 dark:bg-white/10 dark:text-slate-200">.wrn</code> components, ship no client boilerplate, and let the server do the work.
          </p>

          <div class="mt-8 flex flex-wrap items-center justify-center gap-3">
            <a href="/about" class="rounded-lg bg-slate-900 px-5 py-2.5 text-sm font-medium text-white shadow-sm transition hover:bg-slate-700 dark:bg-white dark:text-slate-900 dark:hover:bg-slate-200">Get started</a>
            <a href="/api/hello" class="rounded-lg border border-slate-200 px-5 py-2.5 text-sm font-medium text-slate-700 transition hover:border-slate-300 dark:border-white/10 dark:text-slate-300 dark:hover:border-white/20">View API</a>
          </div>

          <div class="mt-14 w-full max-w-md rounded-2xl border border-slate-200 bg-white p-6 text-left shadow-sm dark:border-white/10 dark:bg-white/5">
            <div class="flex items-center gap-2 font-mono text-xs text-slate-400">
              <span class="h-2 w-2 rounded-full bg-emerald-400"></span>
              live · hydrated on the server
            </div>
            <div class="mt-4 flex items-center justify-between gap-4">
              <div data-component="counter" start="0" label="Clicks"></div>
              <span class="max-w-[10rem] text-right text-xs leading-snug text-slate-500">This button works. You wrote zero client JavaScript.</span>
            </div>
          </div>

          <p class="mt-10 font-mono text-xs text-slate-400 dark:text-slate-600">
            edit <span class="text-slate-600 dark:text-slate-400">app/pages/index.wrn</span> to make it yours
          </p>
        </section>

        <footer class="border-t border-slate-100 py-6 text-center text-xs text-slate-400 dark:border-white/5 dark:text-slate-600">
          Built with <a href="https://www.npmjs.com/package/@wrnexus/cli" class="text-slate-600 underline-offset-2 hover:underline dark:text-slate-400">WrNexus</a>
        </footer>
      </div>
    </main>
  }
}
`,
  "app/pages/about.wrn": `page About {
  seo {
    title = "About"
    description = "Learn how APP_NAME is built with WrNexus."
  }

  view {
    <main class="min-h-screen bg-white px-6 py-20 text-slate-900 dark:bg-[#0b0f1e] dark:text-slate-100">
      <article class="mx-auto max-w-2xl">
        <a href="/" class="text-sm text-indigo-600 hover:underline dark:text-indigo-400">← Home</a>
        <p class="mt-12 font-mono text-xs uppercase tracking-[0.2em] text-indigo-500">WrNexus application</p>
        <h1 class="mt-4 text-4xl font-bold tracking-tight">About APP_NAME</h1>
        <p class="mt-6 text-lg leading-8 text-slate-600 dark:text-slate-400">
          This page is server-rendered from <code>app/pages/about.wrn</code>. Add state,
          events, components, APIs, and data without switching to another UI framework.
        </p>
      </article>
    </main>
  }
}
`,
  "app/components/counter.wrn": `// A reusable component. Route: none — mounted inside a page with
// <div data-component="counter" ...props></div>.
//
// Components render on the SERVER (with their props applied) and are hydrated in
// the browser by the generic reactive runtime — they ship no JS of their own.
component Counter {
  // Props arrive as mount attributes, each coerced to the type of its default
  // (so start="5" arrives as the number 5).
  props {
    start = 0
    label = "Count"
  }

  // State can reference props. \`count\` seeds the reactive scope.
  state count = start

  view {
    <button @click="count++" class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-indigo-500 active:scale-[0.98]">{label}: {count}</button>
  }
}
`,
  "app/api/hello.ts": `export const GET = async () => {
  return Response.json({ message: "Hello API" });
};
`,
  "app/api/ai.ts": `// POST /api/ai  { "prompt": "..." }  → Claude's reply.
// Set ANTHROPIC_API_KEY in your environment (e.g. a .env file) to enable this.
import { createAI } from "@wrnexus/ai";
import type { Context } from "@wrnexus/core";

const ai = createAI(); // reads ANTHROPIC_API_KEY; defaults to claude-opus-4-8

export const POST = async (ctx: Context) => {
  if (!process.env.ANTHROPIC_API_KEY) {
    return Response.json({ error: "Set ANTHROPIC_API_KEY to use AI." }, { status: 501 });
  }
  const { prompt } = await ctx.req.json().catch(() => ({}));
  if (!prompt) return Response.json({ error: "Provide a 'prompt'." }, { status: 400 });

  // Stream the reply back as plain text. Use \`ai.generate(prompt)\` for a one-shot string.
  return ai.streamResponse(prompt);
};
`,
  "app/middleware/logger.ts": `import type { Middleware } from "@wrnexus/core";

const logger: Middleware = async (ctx, next) => {
  console.log(ctx.req.method, ctx.url.pathname);
  return next();
};

export default logger;
`,
  "app/realtime/chat.ts": `// ws://<host>/realtime/chat — a simple broadcast room.
//
// The client side is the framework's realtime runtime; a page opts in with
// \`data-room="chat"\`. Here we only handle room events.
//
//   client.send(msg)            → just this connection
//   client.broadcast(msg)       → everyone else in the room
//   client.room.broadcast(msg)  → everyone, including the sender
import { defineRoom } from "@wrnexus/core";

export default defineRoom({
  onConnect(client) {
    client.send({ type: "system", text: "connected" });
  },

  onMessage(client, msg) {
    // Echo each message to the whole room so every tab stays in sync.
    client.room.broadcast({ type: "message", data: msg });
  },
});
`,
  "test/smoke.test.ts": `import { expect, test } from "bun:test";
import { parseOrThrow } from "@wrnexus/validation";
import { contactSchema } from "../app/schemas/contact.ts";

test("starter validation schema accepts a contact request", () => {
  expect(
    parseOrThrow(contactSchema, {
      email: "hello@example.com",
      message: "Hello from the generated application.",
    }),
  ).toEqual({
    email: "hello@example.com",
    message: "Hello from the generated application.",
  });
});
`,
};

/**
 * Write the app template into `root` (absolute), substituting the app name.
 * Reused by `wrnexus create` and the workspace scaffolder. Refuses to overwrite.
 */
export function scaffoldApp(root: string, appName: string): void {
  if (existsSync(root)) {
    console.error(`Refusing to overwrite existing directory: ${root}`);
    process.exit(1);
  }
  for (const [rel, contents] of Object.entries(TEMPLATE)) {
    const target = join(root, rel);
    mkdirSync(dirname(target), { recursive: true });
    const appSlug = appName.toLowerCase().replace(/[^a-z0-9]+/g, "") || "app";
    writeFileSync(
      target,
      contents.replaceAll("APP_NAME", appName).replaceAll("APP_SLUG", appSlug),
      "utf8",
    );
  }
}

export function createApp(name: string): void {
  if (!name) {
    console.error("Usage: wrnexus create <app-name>");
    process.exit(1);
  }

  scaffoldApp(resolve(process.cwd(), name), name);

  console.log(`✓ Created ${name}`);
  console.log(`\nNext steps:`);
  console.log(`  cd ${name}`);
  console.log(`  bun install`);
  console.log(`  bun run dev`);
}
