import { existsSync, mkdirSync, writeFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";

function pascal(value: string): string {
  return value
    .split(/[^A-Za-z0-9]+/)
    .filter(Boolean)
    .map((part) => part[0]!.toUpperCase() + part.slice(1))
    .join("");
}

function camel(value: string): string {
  const name = pascal(value);
  return name ? name[0]!.toLowerCase() + name.slice(1) : name;
}

function safe(value: string): string {
  const name = value
    .trim()
    .toLowerCase()
    .replace(/^@wrnexus\//, "")
    .replace(/[^a-z0-9-]+/g, "-")
    .replace(/^-+|-+$/g, "");
  if (!name) throw new Error("System name is required");
  return name;
}

/** Scaffold the standard package/component/runtime/test layout for a WRNexus system. */
export function generateSystem(rootDir: string, input: string): string[] {
  const root = resolve(rootDir);
  const name = safe(input);
  const directory = join(root, "packages", name);
  if (existsSync(directory)) throw new Error(`Package already exists: packages/${name}`);

  const className = pascal(name);
  const functionName = camel(name);
  const files: Record<string, string> = {
    "package.json":
      JSON.stringify(
        {
          name: `@wrnexus/${name}`,
          version: "0.4.0",
          type: "module",
          main: "./src/index.ts",
          exports: { ".": "./src/index.ts", "./plugin": "./src/plugin.ts" },
          files: ["src", "components", "assets", "README.md"],
          scripts: {
            test: "bun test",
            typecheck: "tsc --noEmit",
            check: "bun run typecheck && bun run test",
          },
          dependencies: {
            "@wrnexus/core": "workspace:*",
            "@wrnexus/plugin": "workspace:*",
          },
          devDependencies: { "@types/bun": "latest", typescript: "^5.9.2" },
          wrnexus: {
            plugin: { plugin: "./src/plugin.ts", export: "default", factory: true },
          },
        },
        null,
        2,
      ) + "\n",
    "src/index.ts": `export interface ${className}Options {\n  enabled?: boolean;\n}\n\nexport function create${className}(options: ${className}Options = {}) {\n  return { enabled: options.enabled !== false };\n}\n`,
    "src/plugin.ts": `import { dirname, join } from "node:path";\nimport { fileURLToPath } from "node:url";\nimport { definePlugin } from "@wrnexus/plugin";\n\nconst root = dirname(dirname(fileURLToPath(import.meta.url)));\n\nexport function ${functionName}Plugin() {\n  return definePlugin({\n    name: "@wrnexus/${name}",\n    version: "0.4.0",\n    componentDirs: [join(root, "components")],\n    clientRuntimes: [\n      {\n        id: "${name}",\n        entry: join(root, "assets", "client", "runtime.js"),\n        type: "script",\n        load: "defer",\n        singleton: true,\n        bundle: false,\n      },\n    ],\n    styleSources: [{ id: "${name}-components", source: join(root, "components") }],\n  });\n}\n\nexport default ${functionName}Plugin;\n`,
    [`components/${className}.wrn`]: `component ${className} {\n  props {\n    class = ""\n    color = "primary"\n    size = "normal"\n  }\n\n  view {\n    <div\n      {...attrs}\n      data-wrnexus-runtime="${name}"\n      class='{class}'\n    >\n      ${className}\n    </div>\n  }\n}\n`,
    "assets/client/runtime.js": `(function () {\n  var runtimeId = ${JSON.stringify(name)};\n\n  function mount(root) {\n    (root || document)\n      .querySelectorAll('[data-wrnexus-runtime="${name}"]')\n      .forEach(function (node) {\n        if (node.dataset.wrnexusMounted === runtimeId) return;\n        node.dataset.wrnexusMounted = runtimeId;\n      });\n  }\n\n  function unmount(root) {\n    (root || document)\n      .querySelectorAll('[data-wrnexus-runtime="${name}"]')\n      .forEach(function (node) {\n        if (node.dataset.wrnexusMounted === runtimeId) delete node.dataset.wrnexusMounted;\n      });\n  }\n\n  window.__wrnexusRuntimes = window.__wrnexusRuntimes || {};\n  window.__wrnexusRuntimes[runtimeId] = { mount: mount, unmount: unmount };\n\n  if (document.readyState === "loading") {\n    document.addEventListener("DOMContentLoaded", function () { mount(document); }, { once: true });\n  } else {\n    mount(document);\n  }\n})();\n`,
    "test/system.test.ts": `import { expect, test } from "bun:test";\nimport { create${className} } from "../src/index.ts";\n\ntest("${name} system initializes", () => {\n  expect(create${className}().enabled).toBe(true);\n});\n`,
    "README.md": `# @wrnexus/${name}\n\nFramework-native WRNexusJS system package generated by \`wrnexus generate system ${name}\`.\n\nThe component directory and browser runtime are discovered automatically when the package is present in an application's dependencies. No public asset copy or manual script tag is required.\n`,
  };

  const written: string[] = [];
  for (const [file, content] of Object.entries(files)) {
    const target = join(directory, file);
    mkdirSync(dirname(target), { recursive: true });
    writeFileSync(target, content, "utf8");
    written.push(target);
  }
  return written;
}
