import type { ChatToolDefinition, ChatToolHandlers } from '@genesislcap/foundation-ai';

/**
 * Symbol used to attach ToolFold metadata to a facade handler function.
 * The ChatDriver inspects this to detect fold facades at runtime.
 *
 * @internal
 */
export const TOOL_FOLD_SYMBOL = Symbol('toolFold');

/**
 * Internal metadata for a tool fold. Attached to the facade handler via TOOL_FOLD_SYMBOL.
 *
 * @internal
 */
export interface ToolFold {
  /** Facade tool name. */
  name: string;
  /** Description shown to the model. */
  description: string;
  /** Optional guidance injected in the fold-open response. */
  usageNotes?: string;
  /** Tool definitions inside this fold. */
  tools: ChatToolDefinition[];
  /** Handlers for the inner tools (keyed by tool name). */
  handlers: ChatToolHandlers;
  /**
   * When true (default), all other tools are removed when this fold opens —
   * the model can only see this fold's tools until it closes.
   * When false, sibling tools remain alongside the inner tools.
   */
  exclusive: boolean;
}

/**
 * The value returned by `createToolFold`.
 *
 * @beta
 */
export interface ToolFoldResult {
  /**
   * The facade tool definition. Add this to your agent's `toolDefinitions`.
   * It is a regular `ChatToolDefinition` with an empty parameter schema.
   */
  definition: ChatToolDefinition;
  /**
   * The facade handler — a single-entry map keyed by the fold name.
   * Spread this into your agent's `toolHandlers`.
   * Inner tool handlers are NOT included here — they are encapsulated inside
   * the fold metadata and installed by the driver only when the fold opens.
   */
  handler: ChatToolHandlers;
}

/**
 * Creates a tool fold — a facade that hides a group of related tools behind a single
 * named entry, revealing them progressively when the model invokes the facade.
 *
 * The `tools` and `handlers` parameters accept the exact same types as `AgentConfig.toolDefinitions`
 * and `AgentConfig.toolHandlers`, so existing tool creation functions can be passed in unchanged.
 *
 * @example
 * ```typescript
 * const tradingTools = createToolFold({
 *   name: 'trading_tools',
 *   tools: createTradesToolDefinitions(),
 *   handlers: createTradesToolHandlers(connect),
 * });
 *
 * const agent: AgentConfig = {
 *   name: 'Trades',
 *   systemPrompt: '...',
 *   toolDefinitions: [getCurrentDateDef, tradingTools.definition],
 *   toolHandlers: { get_current_date: handler, ...tradingTools.handler },
 * };
 * ```
 *
 * @example Nesting folds
 * ```typescript
 * const insertFold = createToolFold({
 *   name: 'trade_insertion',
 *   tools: [validateDef, insertDef],
 *   handlers: { validate_trade: vHandler, insert_trade: iHandler },
 * });
 *
 * const tradingFold = createToolFold({
 *   name: 'trading_tools',
 *   tools: [searchDef, insertFold.definition],
 *   handlers: { search_trades: sHandler, ...insertFold.handler },
 * });
 * ```
 *
 * @beta
 */
export function createToolFold(config: {
  /** Facade tool name — used as the tool name the model calls to open this fold. */
  name: string;
  /**
   * Description shown to the model for the facade tool.
   * If omitted, auto-generated from the inner tool names:
   * "Contains: tool_a, tool_b. Invoke to access these tools."
   */
  description?: string;
  /**
   * Optional usage guidance injected into the fold-open response message.
   * Not included in the facade description — only visible once the fold is open.
   */
  usageNotes?: string;
  /** Inner tool definitions. Same type as `AgentConfig.toolDefinitions`. */
  tools: ChatToolDefinition[];
  /** Inner tool handlers. Same type as `AgentConfig.toolHandlers`. */
  handlers: ChatToolHandlers;
  /**
   * When true (default), all other tools are removed when this fold opens.
   * When false, sibling tools remain available alongside the inner tools.
   */
  exclusive?: boolean;
}): ToolFoldResult {
  const description =
    config.description ??
    `Contains: ${config.tools.map((t) => t.name).join(', ')}. Invoke to access these tools.`;

  const fold: ToolFold = {
    name: config.name,
    description,
    usageNotes: config.usageNotes,
    tools: config.tools,
    handlers: config.handlers,
    exclusive: config.exclusive ?? true,
  };

  const definition: ChatToolDefinition = {
    name: config.name,
    description,
    parameters: { type: 'object', properties: {} },
  };

  // The facade handler body is never actually called — the driver intercepts it
  // via TOOL_FOLD_SYMBOL before dispatch. The body is a no-op fallback.
  const facadeHandler = async (): Promise<string> => `Fold: ${config.name}`;
  (facadeHandler as any)[TOOL_FOLD_SYMBOL] = fold;

  return {
    definition,
    handler: {
      [config.name]: facadeHandler,
    },
  };
}

/**
 * A tool entry in the expanded debug tree.
 * Folds are represented as nodes with a `tools` array; regular tools have none.
 *
 * @beta
 */
export interface ToolTreeNode extends ChatToolDefinition {
  /** Present when this tool is a fold — contains its inner tools recursively expanded. */
  tools?: ToolTreeNode[];
}

/**
 * Expands a flat list of tool definitions into a nested tree by resolving fold
 * metadata from the corresponding handlers. Use this for debug output so the
 * full tool hierarchy is visible rather than just the top-level facade names.
 *
 * @beta
 */
export function expandToolTree(
  definitions: ChatToolDefinition[],
  handlers: ChatToolHandlers,
): ToolTreeNode[] {
  return definitions.map((def) => {
    const handler = handlers[def.name];
    const fold: ToolFold | undefined = handler ? (handler as any)[TOOL_FOLD_SYMBOL] : undefined;
    if (fold) {
      return { ...def, tools: expandToolTree(fold.tools, fold.handlers) };
    }
    return { ...def };
  });
}
