/**
 * `wrnexus generate mobile` — scaffold a WebView or fully native mobile app.
 *
 * WrNexus remains the hosted SSR/API/WebSocket server. Capacitor loads that
 * server in a native WebView and provides the bridge for native plugins.
 */

import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { basename, join, resolve } from "node:path";
import { loadAppConfig } from "@wrnexus/styles";

export type MobileMode = "webview" | "native";

export interface MobileOptions {
  appId?: string;
  appName?: string;
  serverUrl?: string;
  mode?: MobileMode;
}

function slug(value: string): string {
  const cleaned = value
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "")
    .replace(/^\d+/, "");
  return cleaned || "app";
}

function projectName(root: string): string {
  try {
    const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as { name?: string };
    if (pkg.name) return pkg.name.split("/").pop() || basename(root);
  } catch {
    // A package manifest is helpful but not required to generate the shell.
  }
  return basename(root);
}

function writeIfAbsent(path: string, content: string, label: string): void {
  if (existsSync(path)) {
    console.warn(`  • ${label} already exists — skipped`);
    return;
  }
  mkdirSync(resolve(path, ".."), { recursive: true });
  writeFileSync(path, content, "utf8");
  console.log(`  ✓ ${label}`);
}

/** Scaffold a Capacitor mobile wrapper under `<appRoot>/mobile`. */
export async function generateMobile(appRoot: string, options: MobileOptions = {}): Promise<void> {
  const root = resolve(appRoot);
  const appConfig = await loadAppConfig(root);
  const configured = appConfig.mobile ?? {};
  const mode = options.mode ?? configured.mode ?? "webview";
  if (mode !== "webview" && mode !== "native") {
    throw new Error('mobile.mode must be either "webview" or "native"');
  }
  const name = options.appName || configured.appName || projectName(root);
  const appId = options.appId || configured.appId || `com.example.${slug(name)}`;
  const serverUrl = options.serverUrl || configured.serverUrl || "http://localhost:3000";
  const mobile = join(root, "mobile");

  if (mode === "native") {
    generateNativeMobile(mobile, {
      name,
      appId,
      apiUrl: configured.apiUrl ?? serverUrl,
      configured,
    });
    return;
  }

  const pkg = {
    name: `${slug(name)}-mobile`,
    version: "0.1.0",
    private: true,
    wrnexus: { mode: "webview" },
    type: "module",
    scripts: {
      "add:ios": "cap add ios",
      "add:android": "cap add android",
      sync: "cap sync",
      "open:ios": "cap open ios",
      "open:android": "cap open android",
    },
    dependencies: {
      "@capacitor/core": "^8.0.0",
      "@capacitor/app": "^8.0.0",
      "@capacitor/ios": "^8.0.0",
      "@capacitor/android": "^8.0.0",
    },
    devDependencies: {
      "@capacitor/cli": "^8.0.0",
      "@capacitor/assets": "^3.0.0",
      typescript: "^5.5.0",
    },
  };

  const config = `import type { CapacitorConfig } from "@capacitor/cli";
import appConfig from "../wrnexus.config.ts";

const mobile = appConfig.mobile ?? {};
const serverUrl = process.env.WRNEXUS_MOBILE_URL ?? mobile.serverUrl ?? ${JSON.stringify(serverUrl)};

const config: CapacitorConfig = {
  appId: mobile.appId ?? ${JSON.stringify(appId)},
  appName: mobile.appName ?? ${JSON.stringify(name)},
  webDir: "web",
  appendUserAgent: mobile.userAgent ?? " WrNexusMobile",
  backgroundColor: mobile.backgroundColor,
  ...(mobile.capacitor ?? {}),
  server: {
    ...((mobile.capacitor?.server as CapacitorConfig["server"]) ?? {}),
    // Development bridge only: Capacitor does not recommend server.url in production.
    url: serverUrl,
    cleartext: serverUrl.startsWith("http://"),
  },
};

export default config;
`;

  const errorPage = `<!doctype html>
<html lang="en">
  <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Connection unavailable</title>
  <style>html{color-scheme:dark}body{margin:0;min-height:100vh;display:grid;place-items:center;font:16px system-ui;background:#0f172a;color:#e2e8f0}.card{max-width:28rem;padding:2rem;text-align:center}button{padding:.8rem 1.2rem;border:0;border-radius:.75rem;background:#6c8cff;color:white;font-weight:700}</style></head>
  <body><main class="card"><h1>Connection unavailable</h1><p>Check your Wi-Fi and make sure the WrNexus server is running.</p><button id="retry">Try again</button></main><script>document.getElementById("retry").onclick=()=>location.replace(${JSON.stringify(serverUrl)});</script></body>
</html>
`;

  const fallback = `<!doctype html>
<html lang="en">
  <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${name}</title></head>
  <body><p>Run the WrNexus server and then sync this mobile project.</p></body>
</html>
`;

  const readme = `# ${name} mobile

Capacitor development shell for the hosted WrNexus application. The Bun server,
SSR, APIs, database, and WebSockets continue to run on your server; this directory
contains the native iOS and Android projects and native plugin dependencies.

> Capacitor documents \`server.url\` as a live-reload option that is not intended
> for production. This shell is useful for native development and testing, but a
> store release needs a bundled client build/static export that WrNexus does not
> currently produce.

## Setup

\`\`\`bash
node --version          # Capacitor 8 requires Node.js 22+
bun install
bun run add:android
bun run add:ios       # macOS with Xcode is required
bun run sync
\`\`\`

Set \`WRNEXUS_MOBILE_URL\` to a reachable URL before syncing the development shell:

\`\`\`bash
WRNEXUS_MOBILE_URL=https://app.example.com bun run sync
\`\`\`

For a physical device, \`localhost\` refers to the device, not your computer.
Use your computer's LAN URL during development. Do not ship the generated
\`server.url\` configuration as a production store build.

Open the native projects with \`bun run open:android\` or \`bun run open:ios\`.
Add native features with Capacitor plugins and run \`bun run sync\` afterward.
`;

  console.log("Scaffolding Capacitor mobile app:");
  writeIfAbsent(
    join(mobile, "package.json"),
    `${JSON.stringify(pkg, null, 2)}\n`,
    "mobile/package.json",
  );
  writeIfAbsent(join(mobile, "capacitor.config.ts"), config, "mobile/capacitor.config.ts");
  writeIfAbsent(join(mobile, "web", "index.html"), fallback, "mobile/web/index.html");
  writeIfAbsent(join(mobile, "web", "error.html"), errorPage, "mobile/web/error.html");
  writeIfAbsent(
    join(mobile, ".gitignore"),
    "node_modules\nandroid/.gradle\nios/App/Pods\n",
    "mobile/.gitignore",
  );
  writeIfAbsent(join(mobile, "README.md"), readme, "mobile/README.md");
  console.log("\nNext: cd mobile && bun install && bun run add:android");
  console.log("iOS generation requires macOS with Xcode: bun run add:ios");
}

/** Convert CLI flags into generator options. */
export function mobileOptions(args: string[]): MobileOptions {
  const value = (flag: string) =>
    args.find((arg) => arg.startsWith(`${flag}=`))?.slice(flag.length + 1);
  const modeValue = value("--mode");
  if (modeValue && modeValue !== "webview" && modeValue !== "native") {
    throw new Error('--mode must be either "webview" or "native"');
  }
  const mode: MobileMode | undefined =
    modeValue === "webview" || modeValue === "native" ? modeValue : undefined;
  return {
    appId: value("--app-id"),
    appName: value("--app-name"),
    serverUrl: value("--url"),
    mode,
  };
}

function generateNativeMobile(
  mobile: string,
  input: { name: string; appId: string; apiUrl: string; configured: { scheme?: string } },
): void {
  const { name, appId, apiUrl, configured } = input;
  const scheme = configured.scheme ?? slug(name);
  const pkg = {
    name: `${slug(name)}-mobile`,
    version: "0.1.0",
    private: true,
    main: "expo-router/entry",
    wrnexus: { mode: "native" },
    scripts: {
      compile: "cd .. && wrnexus mobile compile",
      prestart: "bun run compile",
      start: "expo start",
      android: "expo run:android",
      ios: "expo run:ios",
      web: "expo start --web",
      prebuild: "expo prebuild",
    },
    dependencies: {
      expo: "^57.0.0",
      "expo-router": "~57.0.4",
      "expo-status-bar": "~57.0.0",
      react: "19.2.3",
      "react-native": "0.86.0",
      "react-native-safe-area-context": "^5.6.0",
      "react-native-screens": "^4.23.0",
    },
    devDependencies: { "@types/react": "^19.2.0", typescript: "^5.9.0" },
  };
  const expo = `import type { ExpoConfig } from "expo/config";\nimport appConfig from "../wrnexus.config.ts";\n\nconst mobile = appConfig.mobile ?? {};\nconst config: ExpoConfig = {\n  name: mobile.appName ?? ${JSON.stringify(name)},\n  slug: ${JSON.stringify(slug(name))},\n  scheme: mobile.scheme ?? ${JSON.stringify(scheme)},\n  ios: { bundleIdentifier: mobile.appId ?? ${JSON.stringify(appId)} },\n  android: { package: mobile.appId ?? ${JSON.stringify(appId)} },\n  plugins: ["expo-router"],\n  ...(mobile.expo ?? {}),\n};\nexport default config;\n`;
  const env = `/** Shared connection settings for native screens. */\nexport const API_URL = process.env.EXPO_PUBLIC_WRNEXUS_URL ?? ${JSON.stringify(apiUrl)};\nexport async function api<T>(path: string, init?: RequestInit): Promise<T> {\n  const response = await fetch(new URL(path, API_URL), init);\n  if (!response.ok) throw new Error(\`WrNexus request failed: \${response.status}\`);\n  return response.json() as Promise<T>;\n}\nexport function realtimeUrl(path: string): string {\n  const url = new URL(path, API_URL);\n  url.protocol = url.protocol === "https:" ? "wss:" : "ws:";\n  return url.toString();\n}\n`;
  const screen = `import { StyleSheet, Text, View } from "react-native";\nimport { StatusBar } from "expo-status-bar";\n\nexport default function Home() {\n  return <View style={styles.container}><StatusBar style="auto" /><Text style={styles.title}>${name}</Text><Text>Fully native WrNexus client</Text></View>;\n}\nconst styles = StyleSheet.create({ container: { flex: 1, alignItems: "center", justifyContent: "center", padding: 24 }, title: { fontSize: 28, fontWeight: "700", marginBottom: 8 } });\n`;
  const readme = `# ${name} native mobile\n\nThis is a fully native Expo/React Native client: it does not use a WebView. Portable pages from \`../app/pages/**/*.wrn\` compile into Expo routes with \`bun run compile\` (also run automatically before \`start\`). You can edit or add native-only TSX screens in \`app/\`, and call the shared WrNexus backend through \`src/wrnexus.ts\`.\n\n\`\`\`bash\nbun install\nbun run compile\nbun run start\nbun run android\n# macOS/Xcode: bun run ios\n\`\`\`\n\nServer API routes, authentication endpoints, uploads, and WebSockets remain reusable. Unsupported DOM-only markup fails compilation with a specific error. Override the backend per environment with \`EXPO_PUBLIC_WRNEXUS_URL\`. Add native modules with \`bunx expo install <package>\`.\n`;
  console.log("Scaffolding fully native Expo mobile app:");
  writeIfAbsent(
    join(mobile, "package.json"),
    `${JSON.stringify(pkg, null, 2)}\n`,
    "mobile/package.json",
  );
  writeIfAbsent(join(mobile, "app.config.ts"), expo, "mobile/app.config.ts");
  writeIfAbsent(join(mobile, "app", "index.tsx"), screen, "mobile/app/index.tsx");
  writeIfAbsent(join(mobile, "src", "wrnexus.ts"), env, "mobile/src/wrnexus.ts");
  writeIfAbsent(
    join(mobile, "tsconfig.json"),
    `${JSON.stringify({ extends: "expo/tsconfig.base", compilerOptions: { strict: true } }, null, 2)}\n`,
    "mobile/tsconfig.json",
  );
  writeIfAbsent(
    join(mobile, ".gitignore"),
    "node_modules\n.expo\nandroid\nios\n",
    "mobile/.gitignore",
  );
  writeIfAbsent(join(mobile, "README.md"), readme, "mobile/README.md");
  console.log("\nNext: cd mobile && bun install && bun run start");
}
