#!/usr/bin/env bun
/**
 * @wrnexus/cli — the `wrnexus` command line.
 *
 *   wrnexus dev [app-dir] [--port=3000]   start the dev server (live reload)
 *   wrnexus build [app-dir]               build a production server + assets
 *   wrnexus create <app-name>             scaffold a new app
 *   wrnexus eject <name...>               copy a Wire UI component into your app
 *   wrnexus db <migrate|rollback|status|new>   database migrations
 */

import { join, resolve } from "node:path";
import { resolveProfile, loadEnv } from "@wrnexus/styles";
import { runDev } from "./dev.ts";
import { createApp } from "./create.ts";
import { currentCliVersion, notifyIfUpdateAvailable } from "./update-notifier.ts";

/**
 * Resolve the active profile from `--profile=<name>` (or WRNEXUS_PROFILE / mode),
 * publish it as WRNEXUS_PROFILE (so config loaders + the dev child pick it up),
 * and load its `.env` cascade into process.env. Returns the profile name.
 */
function bootstrapProfile(
  appRoot: string,
  mode: "development" | "production",
  args: string[],
): string {
  const flag = args.find((a) => a.startsWith("--profile="));
  const profile = resolveProfile({ explicit: flag?.split("=")[1], mode });
  process.env.WRNEXUS_PROFILE = profile;
  const loaded = loadEnv(resolve(appRoot), profile);
  const count = Object.keys(loaded).length;
  console.log(`  ▸ profile: ${profile}${count ? ` (${count} env vars loaded)` : ""}`);
  return profile;
}

function help(): void {
  console.log(`wrnexus — WrNexus CLI

Usage:
  wrnexus dev [app-dir] [--port=3000] [--host=::]
                                             Start the development server (live reload)
  wrnexus build [app-dir]               Build a production server bundle + assets
  wrnexus create <app-name>             Scaffold a new app
  wrnexus workspace <name>              Scaffold a monorepo (apps/* + shared packages/*)
  wrnexus workspace add <name> [--domain=name.localhost]
                                             Add an app to the current workspace
  wrnexus gateway [--port=3000]         Serve every workspace app behind one port, routed by domain
  wrnexus generate <type> <name>        Scaffold a page | component | api | schema
  wrnexus generate routes | docker | mobile
                                             Generate routes or scaffold deployment targets
  wrnexus mobile add <package...>       Install Capacitor or Expo native packages
  wrnexus mobile compile                Compile .wrn pages into native Expo routes
  wrnexus native list                   List cross-platform native capabilities
  wrnexus native add <capability...>    Install capability packages for the configured mobile mode
  wrnexus eject <name...>               Copy a Wire UI component into app/components
  wrnexus update [dir] [--latest]       Upgrade deps, migrate project files, and verify the app
  wrnexus db <cmd>                      Migrations: migrate | rollback | status | seed | generate | new
  wrnexus test [app-dir] [--watch]      Run the app's tests (bun test, 'test' profile)
  wrnexus profiles [app-dir]            List config profiles (dev/prod/uat/…) and their env files
  wrnexus doctor [app-dir]              Check project structure, runtime, mobile config, and next fixes

Profiles: pass --profile=<name> to dev/build/db (or set WRNEXUS_PROFILE) to load
that profile's .env cascade and config overrides. e.g. wrnexus dev --profile=uat

Update options: --dry-run previews changes; --no-verify skips post-update check/build.
`);
}

async function main(): Promise<void> {
  const [command, ...rest] = process.argv.slice(2);

  notifyIfUpdateAvailable(currentCliVersion(), command);

  switch (command) {
    case "dev": {
      const appRoot = rest.find((a) => !a.startsWith("--")) ?? ".";
      const portArg = rest.find((a) => a.startsWith("--port="));
      const hostArg = rest.find((a) => a.startsWith("--host="));
      const port = portArg ? Number(portArg.split("=")[1]) : 3000;
      const host = hostArg?.split("=")[1] || "::";
      bootstrapProfile(appRoot, "development", rest);
      runDev(appRoot, port, host);
      break;
    }
    case "build": {
      const appRoot = rest.find((a) => !a.startsWith("--")) ?? ".";
      bootstrapProfile(appRoot, "production", rest);
      const { runBuild } = await import("./build.ts");
      await runBuild(appRoot);
      break;
    }
    case "create":
      createApp(rest[0] ?? "");
      break;
    case "workspace": {
      const { addWorkspaceApp, createWorkspace } = await import("./workspace.ts");
      if (rest[0] === "add") {
        await addWorkspaceApp(".", rest[1] ?? "", rest.slice(2));
      } else {
        createWorkspace(rest.find((a) => !a.startsWith("--")) ?? "");
      }
      break;
    }
    case "gateway": {
      const appRoot = rest.find((a) => !a.startsWith("--")) ?? ".";
      const { runGateway } = await import("./workspace.ts");
      await runGateway(appRoot, rest);
      break;
    }
    case "generate":
    case "g": {
      if (rest[0] === "routes") {
        const { regenerateRoutes } = await import("./routes.ts");
        const n = regenerateRoutes(join(process.cwd(), "app"));
        console.log(`✓ Generated app/routes.gen.ts (${n} routes)`);
        break;
      }
      if (rest[0] === "docker") {
        const { generateDocker } = await import("./docker.ts");
        generateDocker(process.cwd());
        break;
      }
      if (rest[0] === "mobile") {
        const { generateMobile, mobileOptions } = await import("./mobile.ts");
        await generateMobile(process.cwd(), mobileOptions(rest.slice(1)));
        break;
      }
      const { runGenerate } = await import("./generate.ts");
      runGenerate(".", rest[0], rest[1]);
      break;
    }
    case "eject": {
      const { runEject } = await import("./eject.ts");
      const args = rest.filter((a) => !a.startsWith("--"));
      // First arg may be an app dir; treat known component names as names.
      runEject(".", args);
      break;
    }
    case "mobile": {
      const { runMobileCommand } = await import("./mobile-command.ts");
      await runMobileCommand(".", rest[0], rest.slice(1));
      break;
    }
    case "native": {
      const { runNativeCommand } = await import("./native-command.ts");
      await runNativeCommand(".", rest[0], rest.slice(1));
      break;
    }
    case "update":
    case "upgrade": {
      const dir = rest.find((a) => !a.startsWith("--")) ?? ".";
      const { runUpdate } = await import("./update.ts");
      await runUpdate(dir, rest);
      break;
    }
    case "db": {
      bootstrapProfile(".", "development", rest);
      const { runDbCommand } = await import("./db.ts");
      const [sub, ...dbArgs] = rest.filter((a) => !a.startsWith("--profile="));
      await runDbCommand(".", sub, dbArgs);
      break;
    }
    case "profiles": {
      const { listProfiles } = await import("./profiles.ts");
      await listProfiles(rest.find((a) => !a.startsWith("--")) ?? ".");
      break;
    }
    case "doctor": {
      const { runDoctor } = await import("./doctor.ts");
      const healthy = runDoctor(rest.find((a) => !a.startsWith("--")) ?? ".");
      if (!healthy) process.exitCode = 1;
      break;
    }
    case "test": {
      const appRoot = rest.find((a) => !a.startsWith("--")) ?? ".";
      const flag = rest.find((a) => a.startsWith("--profile="));
      // Tests default to the `test` profile (config + .env.test), unless overridden.
      process.env.WRNEXUS_PROFILE = resolveProfile({ explicit: flag?.split("=")[1] ?? "test" });
      loadEnv(resolve(appRoot), process.env.WRNEXUS_PROFILE);
      const { runTests } = await import("./test.ts");
      runTests(appRoot, rest);
      break;
    }
    case undefined:
    case "help":
    case "--help":
    case "-h":
      help();
      break;
    default:
      console.error(`Unknown command: ${command}\n`);
      help();
      process.exit(1);
  }
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
