#!/usr/bin/env node
import { execSync } from "node:child_process";
import {
  existsSync
} from "node:fs";
import { basename, join, resolve } from "node:path";
import { parseArgs } from "node:util";
import { createLocalWorkspaceRegistry } from "./localWorkspaces.js";
import {
  MODE_MAP,
  createFolderModeApp,
  createWorkspacesModeApp
} from "./modeApps.js";
import {
  createBoringUiCliRuntimePlugin,
  createFolderModeApp as createFolderModeApp2,
  createWorkspacesModeApp as createWorkspacesModeApp2,
  provisionCliWorkspaceRuntime as provisionCliWorkspaceRuntime2,
  resolveBoringUiPluginCliPackageRoot
} from "./modeApps.js";
import { resolveBoringUiCliPackageRoot as resolveBoringUiCliPackageRoot2 } from "./pluginDiscovery.js";
function openBrowser(url) {
  try {
    const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
    execSync(`${opener} ${url}`, { stdio: "ignore" });
  } catch {
  }
}
function ensureFrontendBuilt(publicDir) {
  if (existsSync(join(publicDir, "index.html"))) return;
  console.error("\nError: boring-ui frontend not found.");
  console.error("Run `pnpm build:full` in packages/cli to build it first.\n");
  process.exit(1);
}
async function registerStatic(app, publicDir) {
  ensureFrontendBuilt(publicDir);
  const { default: fastifyCompress } = await import("@fastify/compress");
  await app.register(fastifyCompress, { global: true, encodings: ["br", "gzip"], threshold: 1024 });
  const { default: fastifyStatic } = await import("@fastify/static");
  await app.register(fastifyStatic, {
    root: publicDir,
    prefix: "/",
    // @fastify/send writes its own Cache-Control after setHeaders runs, so
    // disable it and set the header explicitly for both cases below.
    cacheControl: false,
    setHeaders(res, filePath) {
      res.setHeader(
        "cache-control",
        /[\\/]assets[\\/]/.test(filePath) ? "public, max-age=31536000, immutable" : "public, max-age=0"
      );
    }
  });
  app.setNotFoundHandler(async (req, reply) => {
    if (req.url.startsWith("/api/")) {
      return reply.code(404).send({ error: "Not found" });
    }
    return reply.sendFile("index.html", publicDir);
  });
}
const HELP_TEXT = [
  "Usage: boring-ui [workspace] [options]",
  "",
  "Commands:",
  "  boring-ui [workspace]                 Start the workspace UI for a folder",
  "  boring-ui workspaces <subcommand>     Manage saved local workspaces",
  "  boring-ui plugin <subcommand>         Install, list, and remove plugin sources",
  "",
  "Options:",
  "  -p, --port <port>       HTTP port (default: 5200)",
  "      --host <host>       Listen host (default: 0.0.0.0)",
  "  -m, --mode <mode>       local-sandbox or local (default: local)",
  "  -h, --help              Show this help"
].join("\n");
const AUTH_GUIDE = [
  "",
  "  \u26A0  No LLM provider configured.",
  "",
  "  In another terminal, launch pi and run /login to set up an API key or",
  "  sign in to a subscription (Claude Pro/Max, ChatGPT Plus, Copilot):",
  "",
  "       pi                                    # if installed globally",
  "       npx @earendil-works/pi-coding-agent  # otherwise",
  "",
  "  Then at pi's prompt:  /login",
  "",
  "  Credentials are saved at ~/.pi/agent/auth.json. Then refresh the browser.",
  ""
].join("\n");
async function checkAuth() {
  const { AuthStorage, ModelRegistry } = await import("@mariozechner/pi-coding-agent");
  const authStorage = AuthStorage.create();
  const registry = ModelRegistry.create(authStorage);
  return registry.getAvailable().length;
}
async function startFolderMode(opts) {
  const workspaceRoot = process.env.BORING_AGENT_WORKSPACE_ROOT ?? resolve(opts.folderArg ?? process.cwd());
  const projectName = basename(resolve(workspaceRoot)) || "workspace";
  const modelCount = await checkAuth();
  const url = `http://localhost:${opts.port}`;
  console.log(`
${projectName}`);
  console.log(`  workspace  ${workspaceRoot}`);
  console.log(`  mode       ${opts.cliMode}`);
  console.log(`  port       ${opts.port}`);
  console.log(`  host       ${opts.host}`);
  if (modelCount === 0) console.log(AUTH_GUIDE);
  console.log(`
  starting ${url} \u2026`);
  const app = await createFolderModeApp({
    workspaceRoot,
    mode: opts.mode,
    projectName
  });
  await registerStatic(app, opts.publicDir);
  await app.listen({ port: opts.port, host: opts.host });
  console.log(`  ${url}  ready
`);
  openBrowser(url);
}
async function startWorkspacesMode(opts) {
  const app = await createWorkspacesModeApp({ mode: opts.mode });
  const registry = createLocalWorkspaceRegistry();
  await registerStatic(app, opts.publicDir);
  await app.listen({ port: opts.port, host: opts.host });
  const initialWorkspace = (await registry.list()).find((workspace) => workspace.available);
  const initialUrl = initialWorkspace ? `http://localhost:${opts.port}/workspace/${encodeURIComponent(initialWorkspace.id)}` : `http://localhost:${opts.port}`;
  console.log(`  workspaces ${registry.path}`);
  console.log(`  ${initialUrl}  ready
`);
  openBrowser(initialUrl);
}
async function handleWorkspacesCommand(opts) {
  const registry = createLocalWorkspaceRegistry();
  const subcommand = opts.positionals[1];
  if (subcommand === "add") {
    const target = opts.positionals[2];
    if (!target) throw new Error("usage: boring-ui workspaces add <folder>");
    const workspace = await registry.add(target, { name: opts.args.name });
    console.log(`${workspace.name}
  id    ${workspace.id}
  path  ${workspace.path}`);
    return;
  }
  if (subcommand === "list") {
    const workspaces = await registry.list();
    if (workspaces.length === 0) {
      console.log("No workspaces. Add one with `boring-ui workspaces add <folder>`.");
      return;
    }
    for (const workspace of workspaces) {
      console.log(`${workspace.available ? "\u2713" : "!"} ${workspace.name}  ${workspace.id}
  ${workspace.path}`);
    }
    return;
  }
  if (subcommand === "remove") {
    const id = opts.positionals[2];
    if (!id) throw new Error("usage: boring-ui workspaces remove <id>");
    await registry.remove(id);
    console.log(`removed ${id}`);
    return;
  }
  if (subcommand === "rename") {
    const id = opts.positionals[2];
    const name = opts.positionals.slice(3).join(" ");
    if (!id || !name) throw new Error("usage: boring-ui workspaces rename <id> <name>");
    const workspace = await registry.rename(id, name);
    console.log(`renamed ${workspace.id} -> ${workspace.name}`);
    return;
  }
  await startWorkspacesMode(opts);
}
async function runCli(options) {
  if (options.argv?.[0] === "plugin") {
    const { runBoringUiPluginCli } = await import("@hachej/boring-ui-plugin-cli");
    await runBoringUiPluginCli(options.argv.slice(1));
    return;
  }
  const { values: args, positionals } = parseArgs({
    args: options.argv,
    options: {
      port: { type: "string", short: "p" },
      host: { type: "string" },
      mode: { type: "string", short: "m" },
      name: { type: "string", short: "n" },
      path: { type: "string" },
      json: { type: "boolean" },
      url: { type: "string" },
      workspace: { type: "string" },
      "panel-id": { type: "string" },
      "timeout-ms": { type: "string" },
      help: { type: "boolean", short: "h" }
    },
    allowPositionals: true,
    strict: false
  });
  if (args.help) {
    console.log(HELP_TEXT);
    return;
  }
  const port = Number(args.port ?? process.env.PORT) || 5200;
  const host = args.host ?? process.env.HOST ?? "0.0.0.0";
  const rawMode = args.mode ?? process.env.BORING_MODE;
  let cliMode;
  let mode;
  if (rawMode) {
    if (!(rawMode in MODE_MAP)) {
      throw new Error(`invalid --mode "${rawMode}". Valid options: ${Object.keys(MODE_MAP).join(", ")}`);
    }
    cliMode = rawMode;
    mode = MODE_MAP[cliMode];
  } else {
    cliMode = "local";
    mode = "direct";
  }
  const base = {
    publicDir: options.publicDir,
    port,
    host,
    cliMode,
    mode
  };
  if (positionals[0] === "workspaces") {
    await handleWorkspacesCommand({
      ...base,
      args: { name: args.name },
      positionals
    });
    return;
  }
  await startFolderMode({
    ...base,
    folderArg: positionals[0]
  });
}
export {
  createBoringUiCliRuntimePlugin,
  createFolderModeApp2 as createFolderModeApp,
  createWorkspacesModeApp2 as createWorkspacesModeApp,
  provisionCliWorkspaceRuntime2 as provisionCliWorkspaceRuntime,
  registerStatic,
  resolveBoringUiCliPackageRoot2 as resolveBoringUiCliPackageRoot,
  resolveBoringUiPluginCliPackageRoot,
  runCli
};
