#!/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
 *   wrnexus authz <list|generate|init>    authorization catalog tooling
 */

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 dev [app-dir] --services        Start local production-service simulators with the app
  wrnexus dev [app-dir] --production-runtime
  wrnexus dev [app-dir] --services [--services-port=3099] [--services-http]
                                             Rebuild and reload the exact production artifact
  wrnexus build [app-dir]               Build a production server bundle + assets
  wrnexus preview [app-dir]             Serve the existing exact production output
  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 production [workspace-dir]    Build, migrate, and serve the complete production workspace
  wrnexus <environment> [workspace-dir] Run a configured workspace environment (development/staging/custom)
  wrnexus generate <type> <name>        Scaffold a page | component | api | schema
  wrnexus generate routes | docker | mobile
                                             Generate routes or scaffold deployment targets
  wrnexus generate types [app-dir]       Generate application-wide route/component/key types
  wrnexus routes [app-dir]               Generate typed named routes
  wrnexus typecheck [app-dir]            Generate types and check TypeScript plus every .wrn file
  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 authz <cmd>                    Authorization: list | generate | init [--dialect=sqlite|postgres|mysql]
  wrnexus test [level] [app-dir] [--watch]
                                             Run unit | component | api | browser | visual | accessibility | performance
  wrnexus profiles [app-dir]            List config profiles (dev/prod/uat/…) and their env files
  wrnexus doctor [app-dir] [--fix]      Check project health; optionally apply safe repairs
  wrnexus compatibility <check|explain|upgrade> [app-dir]
                                             Inspect or explicitly upgrade behavior defaults
  wrnexus contracts <check|snapshot> [app-dir]
                                             Detect breaking boundary contract changes
  wrnexus security <audit|headers|test> [app-dir]
                                             Audit ASVS controls, inspect headers, or run abuse tests
  wrnexus api <generate|docs> [app-dir] Generate OpenAPI, docs, Postman, examples and SDKs
  wrnexus sdk generate <language> [app-dir]
                                             Generate TypeScript, JavaScript, Java, Go, or Python SDK
  wrnexus deploy <target> [app-dir]       Generate docker | kubernetes | systemd | railway | render | fly
  wrnexus mcp [app-dir]                   Start the WRNexus MCP server over stdio
  wrnexus i18n <extract|validate> [app-dir]
                                             Extract and audit translation keys
  wrnexus report [app-dir] [--file=app/pages/page.wrn]
                                             Create a sanitized reproduction bundle
  wrnexus playground [--port=4173]         Start the shareable WRN compiler playground
  wrnexus config [app-dir] --explain    Print the fully resolved profile configuration
  wrnexus analyze [app-dir]             Inspect dist/build-report.json and performance budgets
  wrnexus explain <route|build|hydration|bundle> [subject] [app-dir]
                                             Explain compiler and production build decisions
  wrnexus explain <cache|permission> <subject> [app-dir]
                                             Explain route caching or permission enforcement
  wrnexus inspect <target> [app-dir]    Inspect packages | plugins | routes | assets | runtimes | styles | migrations | bundle
  wrnexus inspect component <name> [app-dir]
                                             Inspect a component's typed public contract
  wrnexus generate system <name>        Scaffold a complete framework-native package

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

Workspace environments: configure protocol, rootDomain, port, hostname, runtime
(development|production), hmr, build, migrate, and profile in wrnexus.workspace.ts.
CLI overrides: --port, --host, --build/--no-build, --migrate/--no-migrate,
--profile, and --prepare-only.

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);
      let developmentCertificate:
        | { certFile: string; keyFile: string; cert: string; key: string; reused: boolean }
        | undefined;
      if (rest.includes("--services") && !rest.includes("--services-http")) {
        const { ensureLocalCertificate } = await import("./services.ts");
        developmentCertificate = await ensureLocalCertificate(appRoot);
      }
      if (rest.includes("--services")) {
        const { startLocalServices } = await import("./services.ts");
        await startLocalServices({
          appRoot,
          port: Number(
            rest.find((value) => value.startsWith("--services-port="))?.split("=")[1] ?? 3099,
          ),
          https: !rest.includes("--services-http"),
          origin: `${rest.includes("--services-http") ? "http" : "https"}://localhost:${port}`,
          certificate: developmentCertificate,
        });
      }
      if (rest.includes("--production-runtime")) {
        const { runProductionDev } = await import("./dev.ts");
        await runProductionDev(appRoot, port, host);
      } else {
        runDev(appRoot, port, host, developmentCertificate);
      }
      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 "preview": {
      const appRoot = rest.find((a) => !a.startsWith("--")) ?? ".";
      const port = Number(rest.find((a) => a.startsWith("--port="))?.split("=")[1] ?? 3000);
      const hostname = rest.find((a) => a.startsWith("--host="))?.split("=")[1] || "::";
      bootstrapProfile(appRoot, "production", rest);
      const { runPreview } = await import("./preview.ts");
      runPreview(appRoot, { port, hostname });
      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 "production": {
      const workspaceRoot = rest.find((a) => !a.startsWith("--")) ?? ".";
      const { runProduction } = await import("./workspace.ts");
      await runProduction(workspaceRoot, rest);
      break;
    }
    case "generate":
    case "g": {
      if (rest[0] === "routes") {
        const { regenerateRoutes } = await import("./routes.ts");
        const n = regenerateRoutes(join(resolve(rest[1] ?? "."), "app"));
        console.log(`✓ Generated app/routes.gen.ts (${n} routes)`);
        break;
      }
      if (rest[0] === "types") {
        const { generateApplicationTypesWithPlugins } = await import("./types.ts");
        const result = await generateApplicationTypesWithPlugins(rest[1] ?? ".");
        console.log(
          `✓ Generated ${result.file} (${result.routes} routes, ${result.components} components)`,
        );
        break;
      }
      if (rest[0] === "docker") {
        const { generateDocker } = await import("./docker.ts");
        generateDocker(process.cwd());
        break;
      }
      if (rest[0] === "system") {
        const { generateSystem } = await import("./system.ts");
        const files = generateSystem(process.cwd(), rest[1] ?? "");
        console.log(`✓ Created @wrnexus/${rest[1]} (${files.length} files)`);
        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 "routes": {
      const appRoot = resolve(rest.find((arg) => !arg.startsWith("--")) ?? ".");
      const { regenerateRoutes } = await import("./routes.ts");
      const count = regenerateRoutes(join(appRoot, "app"));
      console.log(`✓ Generated app/routes.gen.ts (${count} routes)`);
      break;
    }
    case "typecheck": {
      const { runTypecheck } = await import("./types.ts");
      const healthy = await runTypecheck(rest.find((arg) => !arg.startsWith("--")) ?? ".");
      if (!healthy) process.exitCode = 1;
      else console.log("✓ Application types are valid");
      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 "authz": {
      bootstrapProfile(".", "development", rest);
      const { runAuthzCommand } = await import("./authz.ts");
      const [sub, ...authzArgs] = rest.filter((a) => !a.startsWith("--profile="));
      await runAuthzCommand(".", sub, authzArgs);
      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 = await runDoctor(rest.find((a) => !a.startsWith("--")) ?? ".", {
        fix: rest.includes("--fix"),
      });
      if (!healthy) process.exitCode = 1;
      break;
    }
    case "compatibility": {
      const { runCompatibilityCommand } = await import("./compatibility-command.ts");
      const subcommand = rest.find((value) => !value.startsWith("--")) ?? "check";
      const appRoot = rest.filter((value) => !value.startsWith("--"))[1] ?? ".";
      if (!["check", "explain", "upgrade"].includes(subcommand))
        throw new Error(`WRN-COMPATIBILITY-COMMAND: unknown command '${subcommand}'.`);
      const current = await runCompatibilityCommand(appRoot, subcommand, rest);
      if (!current && subcommand !== "explain") process.exitCode = 1;
      break;
    }
    case "contracts": {
      const { runContractsCommand } = await import("./contracts-command.ts");
      const subcommand = rest.find((value) => !value.startsWith("--")) ?? "check";
      const appRoot = rest.filter((value) => !value.startsWith("--"))[1] ?? ".";
      const result = await runContractsCommand(appRoot, subcommand, rest);
      if (!result.ok) process.exitCode = 1;
      break;
    }
    case "security": {
      const values = rest.filter((value) => !value.startsWith("--"));
      const { runSecurityCommand } = await import("./security-command.ts");
      const healthy = await runSecurityCommand(values[1] ?? ".", values[0] ?? "audit", rest);
      if (!healthy) process.exitCode = 1;
      break;
    }
    case "api": {
      const values = rest.filter((value) => !value.startsWith("--"));
      if (!["generate", "docs"].includes(values[0] ?? ""))
        throw new Error("WRN-API-COMMAND: use api generate or api docs.");
      const { runApiCommand } = await import("./api-command.ts");
      runApiCommand(values[1] ?? ".", "api", rest);
      break;
    }
    case "sdk": {
      const values = rest.filter((value) => !value.startsWith("--"));
      if (values[0] !== "generate")
        throw new Error("WRN-SDK-COMMAND: use sdk generate <language>.");
      const { runApiCommand } = await import("./api-command.ts");
      runApiCommand(values[2] ?? ".", "sdk", rest);
      break;
    }
    case "deploy": {
      const values = rest.filter((value) => !value.startsWith("--"));
      const { generateDeployment } = await import("./deploy.ts");
      const files = generateDeployment(values[1] ?? ".", values[0] ?? "");
      console.log(`✓ Deployment preset '${values[0]}' ready (${files.length} new files)`);
      break;
    }
    case "mcp": {
      const { runMcpStdio } = await import("@wrnexus/mcp/stdio");
      await runMcpStdio(resolve(rest.find((value) => !value.startsWith("--")) ?? "."));
      break;
    }
    case "i18n": {
      const values = rest.filter((value) => !value.startsWith("--"));
      const { runI18nCommand } = await import("./i18n-command.ts");
      if (!runI18nCommand(values[1] ?? ".", values[0] ?? "validate")) process.exitCode = 1;
      break;
    }
    case "report": {
      const { runReport } = await import("./report.ts");
      runReport(rest.find((value) => !value.startsWith("--")) ?? ".", rest);
      break;
    }
    case "playground": {
      const { createPlaygroundHandler } = await import("@wrnexus/playground");
      const port = Number(rest.find((value) => value.startsWith("--port="))?.split("=")[1] ?? 4173);
      const server = Bun.serve({
        port,
        hostname: "127.0.0.1",
        fetch: createPlaygroundHandler(),
      });
      console.log(`▶ WRNexus playground: http://localhost:${server.port}`);
      break;
    }
    case "config": {
      const { runConfigCommand } = await import("./config-command.ts");
      await runConfigCommand(rest.find((a) => !a.startsWith("--")) ?? ".", rest);
      break;
    }
    case "inspect": {
      const target = rest.find((arg) => !arg.startsWith("--"));
      if (target === "component") {
        const values = rest.filter((arg) => !arg.startsWith("--"));
        const { runInspectComponent } = await import("./inspect.ts");
        runInspectComponent(values[2] ?? ".", values[1] ?? "", rest);
        break;
      }
      const appRoot = rest.filter((arg) => !arg.startsWith("--"))[1] ?? ".";
      const { runInspect } = await import("./inspect.ts");
      await runInspect(appRoot, target, rest);
      break;
    }
    case "analyze": {
      const { runAnalyze } = await import("./analyze.ts");
      const healthy = runAnalyze(rest.find((a) => !a.startsWith("--")) ?? ".", rest);
      if (!healthy) process.exitCode = 1;
      break;
    }
    case "explain": {
      const values = rest.filter((value) => !value.startsWith("--"));
      const target = values[0] ?? "build";
      const subject = target === "build" || target === "bundle" ? "" : (values[1] ?? "");
      const appRoot =
        target === "build" || target === "bundle" ? (values[1] ?? ".") : (values[2] ?? ".");
      const { runExplain } = await import("./explain.ts");
      runExplain(appRoot, target, subject, rest);
      break;
    }
    case "test": {
      const { TEST_LEVELS, runTests } = await import("./test.ts");
      const values = rest.filter((value) => !value.startsWith("--"));
      const hasLevel = TEST_LEVELS.includes(values[0] as (typeof TEST_LEVELS)[number]);
      const appRoot = (hasLevel ? values[1] : values[0]) ?? ".";
      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);
      runTests(appRoot, rest);
      break;
    }
    case undefined:
    case "help":
    case "--help":
    case "-h":
      help();
      break;
    default: {
      const { loadWorkspaceConfig, runGateway, runProduction } = await import("./workspace.ts");
      const workspaceRoot = rest.find((a) => !a.startsWith("--")) ?? ".";
      try {
        const workspace = await loadWorkspaceConfig(resolve(workspaceRoot));
        const knownEnvironments = new Set([
          "development",
          "production",
          workspace.defaultEnvironment,
          ...Object.keys(workspace.environments ?? {}),
        ]);
        if (!knownEnvironments.has(command)) throw new Error("unknown-environment-command");
        const environmentArgs = rest.filter((arg) => arg !== workspaceRoot);
        environmentArgs.push(`--environment=${command}`);
        const environmentConfig = workspace.environments?.[command];
        const runtime =
          environmentConfig?.runtime ?? (command === "development" ? "development" : "production");
        if (runtime === "development") {
          if (environmentArgs.includes("--prepare-only")) {
            console.log(
              `\n  ✓ Workspace environment "${command}" uses the development runtime (HMR ${environmentConfig?.hmr === false ? "disabled" : "enabled"}).\n`,
            );
            break;
          }
          await runGateway(workspaceRoot, environmentArgs);
        } else {
          await runProduction(workspaceRoot, environmentArgs);
        }
      } catch (error) {
        if (error instanceof Error && error.message !== "unknown-environment-command") throw error;
        const { runPluginCliCommand } = await import("./plugin-command.ts");
        if (await runPluginCliCommand(workspaceRoot, command, rest)) break;
        console.error(`Unknown command or workspace environment: ${command}\n`);
        help();
        process.exit(1);
      }
    }
  }
}

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