import onboardingMetadata from "./onboarding-metadata.json";
import {
  INSPIREDESIGN_HANDOFF_COMMANDS,
  INSPIREDESIGN_HANDOFF_GUIDANCE
} from "../inspiredesign/handoff";
import type { CliCommand } from "./args";
import { CLI_COMMANDS, VALID_FLAGS } from "./args";
import {
  CLI_COMMAND_HELP_DETAILS,
  PUBLIC_CLI_COMMAND_GROUPS,
  TOOL_SURFACE_ENTRIES
} from "../public-surface/generated-manifest";

type HelpFlag = (typeof VALID_FLAGS)[number];

interface CommandGroup {
  title: string;
  summary: string;
  commands: readonly CliCommand[];
}

export type CommandHelpDetail = typeof CLI_COMMAND_HELP_DETAILS[CliCommand];

interface FlagEntry {
  flag: HelpFlag;
  alias?: string;
  description: string;
  example?: string;
}

interface FlagGroup {
  title: string;
  summary: string;
  flags: readonly FlagEntry[];
}

interface ReferenceEntry {
  label: string;
  description: string;
}

interface DetailRow {
  label: string;
  value: string;
}

interface FormattableRow {
  label: string;
  description: string;
  details?: readonly DetailRow[];
}

const LABEL_WIDTH = 42;
const DETAIL_LABEL_WIDTH = 9;
const COMMAND_SET = new Set<string>(CLI_COMMANDS);
const FLAG_SET = new Set<string>(VALID_FLAGS);
const TOOL_COUNT = TOOL_SURFACE_ENTRIES.length;
const CLI_EQUIVALENT_SET = new Set(
  TOOL_SURFACE_ENTRIES.flatMap((entry) => (entry.cliEquivalent ? [entry.cliEquivalent] : []))
);

const formatFlags = (flags: readonly HelpFlag[]): string => (flags.length > 0 ? flags.join(", ") : "none");

export const getCliOnlyCommands = (): CliCommand[] => (
  CLI_COMMANDS.filter((command) => !CLI_EQUIVALENT_SET.has(command))
);

export const getToolOnlyHelperNames = (): string[] => (
  TOOL_SURFACE_ENTRIES
    .filter((entry) => !entry.cliEquivalent)
    .map((entry) => entry.name)
);

export const HELP_COMMAND_GROUPS: readonly CommandGroup[] = PUBLIC_CLI_COMMAND_GROUPS.map((group) => ({
  title: group.title,
  summary: group.summary,
  commands: group.commands.map((command) => command.name) as CliCommand[]
}));

export const COMMAND_HELP_DETAILS: Record<CliCommand, CommandHelpDetail> = CLI_COMMAND_HELP_DETAILS as Record<CliCommand, CommandHelpDetail>;

export const HELP_FLAG_GROUPS: readonly FlagGroup[] = [
  {
    title: "Install / Global Flags",
    summary: "Control install scope, prompting, and bundled skill setup.",
    flags: [
      { flag: "--global", alias: "-g", description: "Install into ~/.config/opencode/opencode.json.", example: "npx opendevbrowser --global --with-config" },
      { flag: "--local", alias: "-l", description: "Install into ./opencode.json for this project.", example: "npx opendevbrowser --local --skills-local" },
      { flag: "--update", alias: "-u", description: "Repair OpenCode package caches, plugin pins, and managed skill packs." },
      { flag: "--uninstall", description: "Alias for the uninstall command." },
      { flag: "--with-config", description: "Also create opendevbrowser.jsonc defaults.", example: "npx opendevbrowser --global --with-config" },
      { flag: "--full", alias: "-f", description: "Install config and pre-extract extension assets.", example: "npx opendevbrowser --full" },
      { flag: "--no-prompt", description: "Run non-interactively using defaults." },
      { flag: "--no-interactive", description: "Alias of --no-prompt." },
      { flag: "--quiet", description: "Suppress non-error text output." },
      { flag: "--skills-global", description: "Install bundled skills into global agent directories." },
      { flag: "--skills-local", description: "Install bundled skills into local project agent directories." },
      { flag: "--no-skills", description: "Skip bundled skill installation." }
    ]
  },
  {
    title: "Help / Output Flags",
    summary: "Inspect help or version and control output transport.",
    flags: [
      { flag: "--help", alias: "-h", description: "Show CLI help output.", example: "npx opendevbrowser --help" },
      { flag: "--version", alias: "-v", description: "Show CLI version.", example: "npx opendevbrowser --version" },
      { flag: "--output-format", description: "Output mode: text, json, or stream-json.", example: "opendevbrowser status --daemon --output-format json" },
      { flag: "--transport", description: "Transport selector for transport-aware commands. `status` uses relay/native; `annotate` uses auto/direct/relay.", example: "opendevbrowser status --session-id s1 --transport native" }
    ]
  },
  {
    title: "Daemon / Session / Launch Flags",
    summary: "Control daemon binding, browser connect, and launch behavior. If ext=on but handshake=off, reopen the extension popup and click Connect again to re-establish a clean daemon-extension handshake.",
    flags: [
      { flag: "--port", description: "Daemon or relay port override.", example: "opendevbrowser serve --port 8788" },
      { flag: "--token", description: "Relay or daemon auth token override.", example: "opendevbrowser serve --token local-dev-token" },
      { flag: "--stop", description: "Stop a running daemon.", example: "opendevbrowser serve --stop" },
      { flag: "--daemon", description: "Target daemon status mode where supported.", example: "opendevbrowser status --daemon --output-format json" },
      { flag: "--script", description: "Path to a run-script JSON file.", example: "opendevbrowser run --script ./workflow.json" },
      { flag: "--session-id", description: "Target an existing browser or daemon session.", example: "opendevbrowser snapshot --session-id s1" },
      { flag: "--close-browser", description: "Close the managed browser on disconnect." },
      { flag: "--ws-endpoint", description: "Connect using an explicit CDP WebSocket endpoint.", example: "opendevbrowser connect --ws-endpoint ws://127.0.0.1:9222/devtools/browser/..." },
      { flag: "--host", description: "CDP host for host/port connect mode.", example: "opendevbrowser connect --host 127.0.0.1 --cdp-port 9222" },
      { flag: "--cdp-port", description: "CDP port for host/port connect mode." },
      { flag: "--headless", description: "Launch a managed browser in headless mode.", example: "opendevbrowser launch --no-extension --headless" },
      { flag: "--profile", description: "Use a named browser profile directory." },
      { flag: "--persist-profile", description: "Keep the generated profile directory after exit. `run` uses a temporary profile by default unless this flag is enabled." },
      { flag: "--chrome-path", description: "Use a specific Chrome, Chromium, or CfT binary." },
      { flag: "--start-url", description: "Open this URL immediately after launch or connect." },
      { flag: "--flag", description: "Pass one or more extra Chrome CLI flags." },
      { flag: "--no-extension", description: "Force managed mode without extension relay." },
      { flag: "--extension-only", description: "Fail unless the extension relay websocket is connected and the daemon-extension handshake is complete." },
      { flag: "--extension-legacy", description: "Use the legacy /cdp relay mode instead of /ops." },
      { flag: "--wait-for-extension", description: "Wait for a clean daemon-extension handshake before returning." },
      { flag: "--wait-timeout-ms", description: "Handshake wait timeout in milliseconds." }
    ]
  },
  {
    title: "Navigation / Interaction / Diagnostics Flags",
    summary: "Command-specific flags for page actions, reads, and diagnostics.",
    flags: [
      { flag: "--url", description: "Target URL for navigation, connect, or workflow commands; repeat for multi-reference inspiredesign run or harvest inputs.", example: "opendevbrowser goto --session-id s1 --url https://example.com" },
      { flag: "--wait-until", description: "Navigation wait strategy such as load or domcontentloaded." },
      { flag: "--timeout-ms", description: "Operation timeout in milliseconds.", example: "opendevbrowser canvas --timeout-ms 120000 --command canvas.session.open ..." },
      { flag: "--ref", description: "Snapshot ref id for element-targeted commands.", example: "opendevbrowser click --session-id s1 --ref r12" },
      { flag: "--state", description: "Wait state selector for wait-style commands." },
      { flag: "--until", description: "Wait condition selector for wait-style commands." },
      { flag: "--mode", description: "Mode selector for commands that accept variants." },
      { flag: "--max-chars", description: "Maximum text characters to return for DOM reads." },
      { flag: "--cursor", description: "Cursor token for paginated list commands." },
      { flag: "--text", description: "Text payload for type and related commands." },
      { flag: "--clear", description: "Clear the existing input value before typing." },
      { flag: "--submit", description: "Submit the form or input after typing." },
      { flag: "--values", description: "CSV values for select commands." },
      { flag: "--files", description: "CSV file paths for upload commands." },
      { flag: "--dy", description: "Vertical scroll delta for scroll commands." },
      { flag: "--key", description: "Keyboard key for the press command." },
      { flag: "--attr", description: "DOM attribute name for dom-attr." },
      { flag: "--x", description: "Viewport x coordinate for pointer commands." },
      { flag: "--y", description: "Viewport y coordinate for pointer commands." },
      { flag: "--from-x", description: "Pointer drag start x coordinate." },
      { flag: "--from-y", description: "Pointer drag start y coordinate." },
      { flag: "--to-x", description: "Pointer drag end x coordinate." },
      { flag: "--to-y", description: "Pointer drag end y coordinate." },
      { flag: "--steps", description: "Interpolation step count for pointer move or drag commands." },
      { flag: "--button", description: "Pointer button selector for pointer down or up." },
      { flag: "--click-count", description: "Associated click count for pointer down or up." },
      { flag: "--name", description: "Named page identifier for page commands." },
      { flag: "--target-id", description: "Browser target id for target commands.", example: "opendevbrowser target-use --session-id s1 --target-id page-2" },
      { flag: "--window-id", description: "Desktop window id for direct window capture commands." },
      { flag: "--tab-id", description: "Browser tab id override for extension and annotation commands." },
      { flag: "--include-urls", description: "Include page URLs in list output where supported." },
      { flag: "--path", description: "Filesystem path for command output or artifacts.", example: "opendevbrowser screenshot --session-id s1 --path ./shot.png" },
      { flag: "--screencast-id", description: "Recorded screencast id returned by screencast-start for the same session." },
      { flag: "--reason", description: "Audit reason for desktop observation commands." },
      { flag: "--full-page", description: "Capture the full scrollable page instead of the current viewport." },
      { flag: "--action", description: "Dialog action: status, accept, or dismiss." },
      { flag: "--prompt-text", description: "Prompt text to submit when accepting a prompt dialog." },
      { flag: "--since-seq", description: "Poll from a sequence id across diagnostics streams." },
      { flag: "--max", description: "Maximum number of records or items to return." },
      { flag: "--interval-ms", description: "Frame capture interval for screencast recording." },
      { flag: "--max-frames", description: "Maximum screencast frame count before auto-stop." },
      { flag: "--since-console-seq", description: "Console sequence cursor for debug-trace snapshots." },
      { flag: "--since-network-seq", description: "Network sequence cursor for debug-trace snapshots." },
      { flag: "--since-exception-seq", description: "Exception sequence cursor for debug-trace snapshots." },
      { flag: "--request-id", description: "Attach or lookup a request id for correlated output." },
      { flag: "--cookies", description: "Inline cookie payload for cookie-import." },
      { flag: "--cookies-file", description: "File path containing cookies for cookie-import." },
      { flag: "--strict", description: "Fail cookie import on invalid entries." },
      { flag: "--screenshot-mode", description: "Annotation screenshot mode: visible, full, or none." },
      { flag: "--debug", description: "Enable debug-level annotation capture extras." },
      { flag: "--context", description: "Free-form annotation context for reviewers or agents." },
      { flag: "--stored", description: "Return the last stored annotation payload instead of starting a new capture." }
    ]
  },
  {
    title: "Macro / Provider / Power Flags",
    summary: "Workflow filters, provider selectors, and unsafe RPC or /canvas options.",
    flags: [
      { flag: "--expression", description: "Macro expression to resolve or execute.", example: "opendevbrowser macro-resolve --expression '@web.search(\"openai\")'" },
      { flag: "--default-provider", description: "Provider fallback for shorthand macro expressions." },
      { flag: "--include-catalog", description: "Include macro catalog metadata in the response." },
      { flag: "--command", description: "Canvas command name for the canvas CLI command.", example: "opendevbrowser canvas --command canvas.session.open --params '{...}'" },
      { flag: "--execute", description: "Execute a resolved macro action after planning." },
      { flag: "--params", description: "Inline JSON params for canvas or rpc commands." },
      { flag: "--params-file", description: "Path to a JSON params file for canvas or rpc commands.", example: "opendevbrowser canvas --command canvas.plan.set --params-file ./plan.json" },
      { flag: "--unsafe-internal", description: "Required safety gate for the rpc command." },
      { flag: "--topic", description: "Research topic input." },
      { flag: "--days", description: "Lookback window in days for research commands." },
      { flag: "--from", description: "Start date boundary for research commands." },
      { flag: "--to", description: "End date boundary for research commands." },
      { flag: "--source-selection", description: "Research source-family selector: auto, web, community, social, shopping, or all. This selects families only and is not a reliability guarantee." },
      { flag: "--sources", description: "Explicit source-family selectors such as web,community. Use shopping only when commercial intent is explicit." },
      { flag: "--include-engagement", description: "Include engagement metrics in research output." },
      { flag: "--limit-per-source", description: "Per-source result cap for research runs." },
      { flag: "--query", description: "Shopping query input or inspiredesign harvest discovery query." },
      { flag: "--providers", description: "Comma-separated provider ids for shopping or artifact commands." },
      { flag: "--provider", description: "Repeatable provider id for inspiredesign harvest discovery." },
      { flag: "--max-references", description: "Maximum inspiredesign harvest references to keep after explicit URL and query discovery merge." },
      { flag: "--visual-evidence", description: "Inspiredesign visual evidence mode: off, auto, or required. Harvest defaults to required." },
      { flag: "--budget", description: "Budget filter for shopping workflows." },
      { flag: "--region", description: "Region or country hint for provider selection. Treat it as advisory unless output metadata reports `region_authoritative=true`." },
      { flag: "--sort", description: "Sort mode for shopping results." },
      { flag: "--brief", description: "Inspiredesign brief describing the target design direction." },
      { flag: "--capture-mode", description: "Inspiredesign capture mode: off or deep. Any --url forces deep." },
      { flag: "--include-prototype-guidance", description: "Include inspiredesign prototype guidance in workflow output." },
      { flag: "--product-url", description: "Target product URL for product-video workflows." },
      { flag: "--product-name", description: "Product name override for product-video workflows." },
      { flag: "--provider-hint", description: "Provider hint override for product workflows." },
      { flag: "--include-screenshots", description: "Include screenshots in product presentation output, or prefer screenshots when fetching stored annotations." },
      { flag: "--include-all-images", description: "Include all discovered product images." },
      { flag: "--include-copy", description: "Include product marketing copy metadata." },
      { flag: "--use-cookies", description: "Enable or disable provider cookie injection for workflow runs and macro-resolve execute; a bare flag means true.", example: "opendevbrowser macro-resolve --expression '@media.search(\"browser automation x\", \"x\", 5)' --execute --browser-mode extension --use-cookies" },
      { flag: "--browser-mode", description: "Provider browser transport mode: auto, extension, or managed. extension reuses relay-backed browser state; managed runs a deterministic managed browser.", example: "opendevbrowser research run --topic 'browser automation on X' --source-selection social --browser-mode extension --use-cookies --challenge-automation-mode browser_with_helper" },
      { flag: "--challenge-automation-mode", description: "Per-run challenge automation mode for workflow runs and macro-resolve execute: off, browser, or browser_with_helper. Precedence is run > session > config, and the helper remains browser-scoped only.", example: "opendevbrowser macro-resolve --expression '@community.search(\"openai\")' --execute --challenge-automation-mode browser_with_helper" },
      { flag: "--cookie-policy-override", description: "Per-run workflow and macro-resolve execute cookie policy override: off, auto, or required.", example: "opendevbrowser macro-resolve --expression '@community.search(\"browser automation failures\", 4)' --execute --browser-mode extension --use-cookies --cookie-policy required" },
      { flag: "--cookie-policy", description: "Alias of --cookie-policy-override." },
      { flag: "--output-dir", description: "Directory where generated artifacts are written, including screencast replay output." },
      { flag: "--ttl-hours", description: "Artifact cache time-to-live in hours." },
      { flag: "--expired-only", description: "List only expired artifacts in artifacts commands." }
    ]
  }
];

export const HELP_TOOL_ENTRIES = TOOL_SURFACE_ENTRIES;

export const HELP_CAPABILITY_ENTRIES: readonly FormattableRow[] = [
  {
    label: "screencast / browser replay",
    description: "Use the public browser replay lane when you need temporal browser evidence before or after a fragile flow.",
    details: [
      { label: "cli:", value: "screencast-start, screencast-stop" },
      { label: "example:", value: "npx opendevbrowser screencast-start --session-id <id> --output-dir ./artifacts/replay" }
    ]
  },
  {
    label: "desktop observation",
    description: "Use the public read-only desktop observation plane for sibling desktop evidence on macOS; availability, window inventory, and accessibility probes use the local swift command, while screenshots use macOS screencapture outside extension relay.",
    details: [
      {
        label: "cli:",
        value: "desktop-status, desktop-windows, desktop-active-window, desktop-capture-desktop, desktop-capture-window, desktop-accessibility-snapshot"
      },
      { label: "example:", value: "npx opendevbrowser desktop-status --output-format json" }
    ]
  },
  {
    label: "computer use / browser-scoped computer use",
    description: "Control the bounded browser-scoped computer-use challenge lane with --challenge-automation-mode; the optional helper is not a desktop agent.",
    details: [
      { label: "flag:", value: "--challenge-automation-mode off|browser|browser_with_helper" },
      { label: "works:", value: "research run, shopping run, product-video run, inspiredesign run, inspiredesign harvest, macro-resolve --execute" },
      { label: "entry:", value: onboardingMetadata.quickStartCommands.computerUseEntry },
      { label: "proof:", value: "review, session-inspector, workflow fallback metadata" }
    ]
  }
];

export const HELP_ONBOARDING_ENTRIES: readonly FormattableRow[] = [
  {
    label: "prompting_guide",
    description: "Load local best-practice guidance before low-level browser commands.",
    details: [{ label: "tool:", value: onboardingMetadata.quickStartCommands.promptingGuide }]
  },
  {
    label: "skill_load",
    description: `Load ${onboardingMetadata.skillName} ${onboardingMetadata.skillTopic} directly when you want the canonical quick-start section.`,
    details: [{ label: "tool:", value: onboardingMetadata.quickStartCommands.skillLoad }]
  },
  {
    label: "validated_lanes",
    description: `Load ${onboardingMetadata.skillName} ${onboardingMetadata.validatedSkillTopic} when you need the current proven transcript, evidence-gated research, and shopping runbook.`,
    details: [{ label: "tool:", value: onboardingMetadata.quickStartCommands.validatedLanes }]
  },
  {
    label: "skill_list",
    description: "Inspect bundled and discovered skill packs when you need a different local lane.",
    details: [{ label: "tool:", value: onboardingMetadata.quickStartCommands.skillList }]
  },
  {
    label: "research_evidence_gated",
    description: "Load opendevbrowser-research first, run explicit source-family research, and inspect artifacts before final claims.",
    details: [{ label: "cli:", value: onboardingMetadata.quickStartCommands.validatedResearch }]
  },
  {
    label: "shopping_reliable",
    description: "Start deal hunting with explicit providers in managed mode and only trust regional comparisons when the result reports `region_authoritative=true`.",
    details: [{ label: "cli:", value: onboardingMetadata.quickStartCommands.validatedShopping }]
  },
  {
    label: "inspiredesign_followthrough",
    description: "After inspiredesign finishes, inspect nextStepGuidance.readiness, read required artifacts, and continue in Canvas only when guidance is ready.",
    details: [
      { label: "quick:", value: INSPIREDESIGN_HANDOFF_COMMANDS.loadBestPractices },
      { label: "design:", value: INSPIREDESIGN_HANDOFF_COMMANDS.loadDesignAgent },
      { label: "motion:", value: INSPIREDESIGN_HANDOFF_COMMANDS.loadMotionDesign },
      { label: "brief:", value: INSPIREDESIGN_HANDOFF_GUIDANCE.reviewAdvancedBrief },
      { label: "gate:", value: "Require nextStepGuidance.readiness=ready and no matching doNotProceedIf blockers before Canvas continuation." },
      { label: "prep:", value: INSPIREDESIGN_HANDOFF_GUIDANCE.prepareCanvasPlanRequest },
      { label: "run:", value: INSPIREDESIGN_HANDOFF_COMMANDS.continueInCanvas }
    ]
  },
  {
    label: "computer_use_entry",
    description: "Enter browser-scoped computer use from a workflow run, not from a separate desktop command family.",
    details: [{ label: "cli:", value: onboardingMetadata.quickStartCommands.computerUseEntry }]
  },
  {
    label: "happy_path",
    description: "After guidance, verify a minimal managed happy path before widening into multi-step automation.",
    details: [{ label: "cli:", value: onboardingMetadata.quickStartCommands.happyPath }]
  },
  {
    label: "surface_split",
    description: "Use the existing public-surface split when a workflow or helper only exists on one side of the CLI or tool surface.",
    details: [
      { label: "cli-only:", value: getCliOnlyCommands().join(", ") },
      { label: "tool-only:", value: getToolOnlyHelperNames().join(", ") }
    ]
  },
  {
    label: "docs",
    description: "Use the first-run checklist and canonical skill runbook for proof and deeper operating details.",
    details: [{
      label: "paths:",
      value: `${onboardingMetadata.referencePaths.onboardingDoc}, ${onboardingMetadata.referencePaths.skillDoc}`
    }]
  }
];

export const HELP_REFERENCE_ENTRIES: readonly ReferenceEntry[] = [
  { label: "src/cli/onboarding-metadata.json", description: "Canonical first-contact onboarding metadata shared by help, nudges, and proof lanes." },
  { label: "src/providers/workflow-handoff.ts", description: "Central next-step advisory builders for workflow follow-through." },
  { label: "src/inspiredesign/handoff.ts", description: "Shared inspiredesign follow-through commands, artifact names, and Canvas continuation guidance." },
  { label: "src/public-surface/source.ts", description: "Authoritative command, usage, flag, and tool surface metadata." },
  { label: "src/public-surface/generated-manifest.ts", description: "Checked-in generated public-surface snapshot consumed by help and parity tests." },
  { label: "src/public-surface/generated-manifest.json", description: "Checked-in generated public-surface snapshot consumed by inventory scripts." },
  { label: "src/cli/args.ts", description: "CLI argument parsing backed by the public-surface source." },
  { label: "src/cli/help.ts", description: "Human-facing CLI formatting layered on the public-surface source, including the first-contact Find It Fast lookup block." },
  { label: "src/tools/index.ts", description: "Code-level tool registry." },
  { label: "docs/CLI.md", description: "Detailed CLI guide and release-gate runbooks." },
  { label: onboardingMetadata.referencePaths.onboardingDoc, description: "First-run checklist for help-led onboarding and happy-path proof." },
  { label: onboardingMetadata.referencePaths.skillDoc, description: "Canonical bundled best-practices runbook and quick-start guidance." },
  { label: "docs/WORKFLOW_SURFACE_MAP.md", description: "Code-derived workflow and validation inventory, including CLI-only and tool-only surface splits." },
  { label: "docs/SURFACE_REFERENCE.md", description: "Canonical CLI, tool, and relay channel inventory." },
  { label: "opendevbrowser --help", description: "Primary full help invocation for quick discovery." },
  { label: "opendevbrowser help", description: "Alias that prints the same full help inventory." }
];

function formatRows(rows: readonly FormattableRow[]): string {
  return rows
    .map((row) => {
      const lines = [`  ${row.label.padEnd(LABEL_WIDTH)} ${row.description}`];
      for (const detail of row.details ?? []) {
        lines.push(`    ${detail.label.padEnd(DETAIL_LABEL_WIDTH)} ${detail.value}`);
      }
      return lines.join("\n");
    })
    .join("\n");
}

function assertCommandCoverage(): void {
  const seen = new Set<string>();

  for (const group of HELP_COMMAND_GROUPS) {
    for (const command of group.commands) {
      const detail = COMMAND_HELP_DETAILS[command];
      if (!COMMAND_SET.has(command)) {
        throw new Error(`Help references unknown CLI command: ${command}`);
      }
      if (seen.has(command)) {
        throw new Error(`Help command appears multiple times: ${command}`);
      }
      if (!detail || !detail.description.trim() || !detail.usage.trim()) {
        throw new Error(`Missing command help metadata: ${command}`);
      }
      if (detail.examples.length === 0) {
        throw new Error(`Missing command example metadata: ${command}`);
      }
      for (const flag of detail.flags) {
        if (!FLAG_SET.has(flag)) {
          throw new Error(`Command help metadata references unknown flag ${flag} for ${command}`);
        }
      }
      seen.add(command);
    }
  }

  if (seen.size !== CLI_COMMANDS.length) {
    const missing = CLI_COMMANDS.filter((command) => !seen.has(command));
    throw new Error(`Help command coverage mismatch; missing: ${missing.join(", ")}`);
  }
}

function assertFlagCoverage(): void {
  const seen = new Set<string>();

  for (const group of HELP_FLAG_GROUPS) {
    for (const entry of group.flags) {
      if (!FLAG_SET.has(entry.flag)) {
        throw new Error(`Help references unknown CLI flag: ${entry.flag}`);
      }
      if (seen.has(entry.flag)) {
        throw new Error(`Help flag appears multiple times: ${entry.flag}`);
      }
      seen.add(entry.flag);
    }
  }

  if (seen.size !== VALID_FLAGS.length) {
    const missing = VALID_FLAGS.filter((flag) => !seen.has(flag));
    throw new Error(`Help flag coverage mismatch; missing: ${missing.join(", ")}`);
  }
}

function assertToolCoverage(): void {
  const seen = new Set<string>();

  for (const entry of HELP_TOOL_ENTRIES) {
    if (!entry.name.startsWith("opendevbrowser_")) {
      throw new Error(`Invalid tool name in help inventory: ${entry.name}`);
    }
    if (!entry.description.trim()) {
      throw new Error(`Help tool is missing a description: ${entry.name}`);
    }
    if (!entry.example?.trim()) {
      throw new Error(`Help tool is missing an example: ${entry.name}`);
    }
    if (seen.has(entry.name)) {
      throw new Error(`Help tool appears multiple times: ${entry.name}`);
    }
    if (entry.cliEquivalent && !COMMAND_SET.has(entry.cliEquivalent)) {
      throw new Error(`Tool metadata references unknown CLI command: ${entry.cliEquivalent}`);
    }
    seen.add(entry.name);
  }

  if (HELP_TOOL_ENTRIES.length !== TOOL_COUNT) {
    throw new Error(`Help tool inventory must list ${TOOL_COUNT} tools; got ${HELP_TOOL_ENTRIES.length}`);
  }
}

function formatCommandGroups(): string {
  return HELP_COMMAND_GROUPS
    .map((group) => {
      const rows: FormattableRow[] = group.commands.map((command) => {
        const detail = COMMAND_HELP_DETAILS[command];
        return {
          label: command,
          description: detail.description,
          details: [
            { label: "usage:", value: detail.usage },
            { label: "flags:", value: formatFlags(detail.flags) },
            ...detail.examples.map((example) => ({ label: "example:", value: example })),
            ...detail.notes.map((note) => ({ label: "note:", value: note }))
          ]
        };
      });
      return `${group.title}: ${group.summary}\n${formatRows(rows)}`;
    })
    .join("\n\n");
}

function formatFlagGroups(): string {
  return HELP_FLAG_GROUPS
    .map((group) => {
      const rows: FormattableRow[] = group.flags.map((entry) => ({
        label: entry.alias ? `${entry.flag} (${entry.alias})` : entry.flag,
        description: entry.description,
        details: entry.example ? [{ label: "example:", value: entry.example }] : []
      }));
      return `${group.title}: ${group.summary}\n${formatRows(rows)}`;
    })
    .join("\n\n");
}

function formatToolEntries(): string {
  return formatRows(HELP_TOOL_ENTRIES.map((entry) => ({
    label: entry.name,
    description: entry.description,
    details: [
      ...(entry.cliEquivalent
        ? [{ label: "cli:", value: entry.cliEquivalent }]
        : [{ label: "scope:", value: "tool-only" }]),
      ...(entry.example ? [{ label: "example:", value: entry.example }] : []),
      ...((entry.notes ?? []).map((note) => ({ label: "note:", value: note })))
    ]
  })));
}

function formatOnboardingEntries(): string {
  return formatRows(HELP_ONBOARDING_ENTRIES);
}

function formatCapabilityEntries(): string {
  return formatRows(HELP_CAPABILITY_ENTRIES);
}

function formatReferenceEntries(): string {
  return formatRows(HELP_REFERENCE_ENTRIES.map((entry) => ({
    label: entry.label,
    description: entry.description
  })));
}

export function getHelpText(): string {
  assertCommandCoverage();
  assertFlagCoverage();
  assertToolCoverage();

  return [
    "OpenDevBrowser CLI",
    "",
    "Usage:",
    "  npx opendevbrowser <command> [options]",
    "",
    "Find It Fast:",
    "  Use these exact lookup terms when you need replay, desktop evidence, or browser-scoped computer use.",
    formatCapabilityEntries(),
    "",
    `${onboardingMetadata.sectionTitle}:`,
    `  ${onboardingMetadata.sectionSummary}`,
    formatOnboardingEntries(),
    "",
    `Command Inventory (all ${CLI_COMMANDS.length} commands):`,
    formatCommandGroups(),
    "",
    "Flag Inventory (all supported flags):",
    formatFlagGroups(),
    "",
    `Tool Inventory (all ${TOOL_COUNT} opendevbrowser_* tools):`,
    formatToolEntries(),
    "",
    "Reference Pointers:",
    formatReferenceEntries()
  ].join("\n");
}
