/**
 * tool-categories.ts — Single source of truth for tool→category mapping.
 *
 * Both /api/tools (tools-api.ts) and the workspace-tool-policy resolver
 * import from here so the two views can never diverge.
 */

// ─────────────────────────────────────────────────────────────────────────────
// Meta tools (catalog + workspace-policy subset)
// ─────────────────────────────────────────────────────────────────────────────

/**
 * Meta tools for two consumers:
 *  - tools-api.ts catalog: tags these as source='meta' in /api/tools.
 *  - workspace-tool-policy.ts: unions these into every workspace's resolved set.
 *
 * This is a SUBSET, not a mirror of index.ts. `src/engine/tools/index.ts`
 * keeps its own (larger) META_TOOLS list and injects it at dispatch in
 * getToolDefs(), so a tool's runtime availability is NOT gated by this list.
 * Adding a name here changes the /api/tools catalog tagging — do so
 * deliberately (see docs/maintenance-checklist.md).
 */
export const META_TOOLS = new Set<string>([
  'ReadToolDoc',
  'CreateChecklist',
  'CheckItem',
  'GetChecklist',
  'MissionUpdate',
  'SearchTaskConversation',
  'ReadTaskConversation',
  'ListUserAssets',
  'RunUserScript',
  'UpdateUserMemory',
  'ReadUserMemory',
  'ReadUserAgents',
  'UpdateUserAgents',
  'WriteUserScript',
  'Brainstorm',
  'ReadAppDoc',
  'ListAppDocs',
  'GetMyOrchestratorState',
  'GetFileProvenance',
  'ListWorkspaceFiles',
  'ReadSkill',
  'ListSkills',
  'InstallSkill',
  'RequestTool',
  'RequestPackage',
]);

// ─────────────────────────────────────────────────────────────────────────────
// Sensitive categories and tools
// ─────────────────────────────────────────────────────────────────────────────

/**
 * Categories whose tools require elevated scrutiny in the workspace tool
 * policy (e.g. opt-in rather than default-allowed).
 */
export const SENSITIVE_CATEGORIES: ReadonlySet<string> = new Set(['ssh', 'browser']);

/**
 * Individual tools that require elevated scrutiny regardless of their
 * category (e.g. Bash even though its category is 'core').
 *
 * SpawnSubTask: parallel separate-job decomposition. Each spawned child takes a
 * worker + its own workspace + a GPU slot, so a burst can starve other queued
 * work. Built-in pieces now decompose via the in-process serial `delegate` tool
 * (orchestration category, default-on), so SpawnSubTask is opt-in only — a
 * workspace must explicitly enable it when it genuinely needs parallel subtasks.
 * The code (SpawnSubTask / WAIT_SUBTASKS / WaitSubTask) stays; only the default
 * exposure flips. WaitSubTask stays in the default-on 'orchestration' category:
 * it's a no-op when there are no children, so it's harmless without SpawnSubTask.
 */
export const SENSITIVE_TOOLS: ReadonlySet<string> = new Set(['Bash', 'SpawnSubTask']);

// ─────────────────────────────────────────────────────────────────────────────
// Module specs — canonical category → module specifier mapping
//
// These paths are resolved relative to src/bridge/ (the tools-api.ts import
// context), which is why they use '../engine/tools/...'.
//
// NOTE: 'core' is handled separately (uses ALL_TOOL_DEFS not TOOL_DEFS) and
// is NOT listed here. 'ssh' appears twice because ssh-console.js is a second
// module in the same category — first-write-wins keeps the category stable.
// ─────────────────────────────────────────────────────────────────────────────

export const MODULE_SPECS: ReadonlyArray<{ category: string; specifier: string }> = [
  { category: 'web', specifier: '../engine/tools/web.js' },
  { category: 'image', specifier: '../engine/tools/image.js' },
  { category: 'data', specifier: '../engine/tools/data.js' },
  { category: 'office', specifier: '../engine/tools/office.js' },
  { category: 'review', specifier: '../engine/tools/review.js' },
  { category: 'x', specifier: '../engine/tools/x.js' },
  { category: 'orchestration', specifier: '../engine/tools/orchestration.js' },
  { category: 'browser', specifier: '../engine/tools/browser.js' },
  { category: 'maps', specifier: '../engine/tools/maps.js' },
  { category: 'calendar', specifier: '../engine/tools/calendar.js' },
  { category: 'youtube', specifier: '../engine/tools/youtube.js' },
  { category: 'pieces', specifier: '../engine/tools/pieces.js' },
  { category: 'amazon', specifier: '../engine/tools/amazon.js' },
  { category: 'speech', specifier: '../engine/tools/speech.js' },
  { category: 'checklist', specifier: '../engine/tools/checklist.js' },
  { category: 'ms-learn', specifier: '../engine/tools/ms-learn.js' },
  { category: 'slide', specifier: '../engine/tools/slide.js' },
  { category: 'docs', specifier: '../engine/tools/docs.js' },
  { category: 'mission', specifier: '../engine/tools/mission.js' },
  { category: 'task-conversation', specifier: '../engine/tools/task-conversation.js' },
  { category: 'file-provenance', specifier: '../engine/tools/file-provenance.js' },
  { category: 'user-folder', specifier: '../engine/tools/user-folder.js' },
  { category: 'brainstorm', specifier: '../engine/tools/brainstorm.js' },
  { category: 'app-docs', specifier: '../engine/tools/app-docs.js' },
  { category: 'app-test', specifier: '../engine/tools/app-test.js' },
  { category: 'ssh', specifier: '../engine/tools/ssh.js' },
  { category: 'ssh', specifier: '../engine/tools/ssh-console.js' },
  { category: 'skills', specifier: '../engine/tools/skills.js' },
  { category: 'tool-request', specifier: '../engine/tools/tool-request.js' },
];

// ─────────────────────────────────────────────────────────────────────────────
// Shared module interface (used by tools-api.ts as well)
// ─────────────────────────────────────────────────────────────────────────────

export interface ToolModule {
  TOOL_DEFS?: Record<string, unknown>;
  ALL_TOOL_DEFS?: Record<string, unknown>;
}

// ─────────────────────────────────────────────────────────────────────────────
// getToolCategoryMap
// ─────────────────────────────────────────────────────────────────────────────

let _cachedCategoryMap: Map<string, string> | null = null;

/**
 * Returns a map of tool name → category string, built with the same
 * first-write-wins logic as tools-api.ts loadBuiltinEntries().
 *
 * Core tools (ALL_TOOL_DEFS) are loaded first and assigned 'core'. Each
 * MODULE_SPECS entry is then loaded in order; the first module to register a
 * given tool name wins. Failed module loads are silently skipped (matching
 * the runtime behaviour in tools/index.ts and tools-api.ts).
 *
 * The result is memoised for the lifetime of the process.
 */
export async function getToolCategoryMap(): Promise<Map<string, string>> {
  if (_cachedCategoryMap) return _cachedCategoryMap;

  const map = new Map<string, string>();

  // 1. Core tools — always 'core', loaded from the local path
  try {
    const coreMod = (await import('./core.js')) as ToolModule;
    const defs = coreMod.ALL_TOOL_DEFS ?? {};
    for (const name of Object.keys(defs)) {
      if (!map.has(name)) map.set(name, 'core');
    }
  } catch {
    // core should always be available; if not, skip gracefully
  }

  // 2. Non-core modules — use specifiers relative to src/bridge/ to match
  //    the import paths used by tools-api.ts (../engine/tools/xxx.js).
  //    From src/engine/tools/ the equivalent is ./xxx.js.
  for (const { category, specifier } of MODULE_SPECS) {
    // Convert bridge-relative path to engine/tools-relative path
    // '../engine/tools/foo.js' → './foo.js'
    const localSpecifier = specifier.replace('../engine/tools/', './');
    try {
      const mod = (await import(localSpecifier)) as ToolModule;
      const defs = mod.TOOL_DEFS ?? {};
      for (const name of Object.keys(defs)) {
        if (!map.has(name)) map.set(name, category);
      }
    } catch {
      // module not available — skip (same as tools-api.ts loadBuiltinEntries)
    }
  }

  _cachedCategoryMap = map;
  return map;
}

// ─────────────────────────────────────────────────────────────────────────────
// listToolCategories
// ─────────────────────────────────────────────────────────────────────────────

/**
 * Returns a de-duplicated list of all known builtin categories, including
 * 'core' and every category appearing in MODULE_SPECS.
 */
export async function listToolCategories(): Promise<string[]> {
  const seen = new Set<string>(['core']);
  for (const { category } of MODULE_SPECS) {
    seen.add(category);
  }
  return [...seen];
}

/** Test-only: reset the memoised category map. */
export function _resetToolCategoryCacheForTests(): void {
  _cachedCategoryMap = null;
}
