import type { Tool, ToolInputSchema } from './types.js';
import type { ToolPolicy, ToolPolicyContext } from './policy.js';

export interface AnthropicToolDef {
  name: string;
  description: string;
  input_schema: ToolInputSchema;
}

export interface OpenAIToolDef {
  type: 'function';
  function: {
    name: string;
    description: string;
    parameters: ToolInputSchema;
  };
}

export class ToolRegistry {
  private tools: Map<string, Tool> = new Map();
  private _policy?: ToolPolicy;

  /** Sanitize a tool name for API compatibility (dots → underscores). */
  static sanitizeToolName(name: string): string {
    return name.replace(/\./g, '_');
  }

  register(tool: Tool): void {
    if (this.tools.has(tool.name)) {
      throw new Error(`Tool '${tool.name}' is already registered`);
    }
    this.tools.set(tool.name, tool);
  }

  unregister(name: string): boolean {
    return this.tools.delete(name);
  }

  /** Replace an existing tool with a new implementation. Throws if not registered. */
  replace(tool: Tool): void {
    if (!this.tools.has(tool.name)) {
      throw new Error(`Tool '${tool.name}' is not registered — cannot replace`);
    }
    this.tools.set(tool.name, tool);
  }

  /** Create a shallow clone of this registry (new Map, same Tool objects + policy). */
  clone(): ToolRegistry {
    const cloned = new ToolRegistry();
    for (const tool of this.tools.values()) {
      cloned.register(tool);
    }
    if (this._policy) {
      cloned.setPolicy(this._policy);
    }
    return cloned;
  }

  get(name: string): Tool | undefined {
    return this.tools.get(name);
  }

  /** Resolve a tool by its API-sanitized name (underscores → dots fallback). */
  getByApiName(name: string): Tool | undefined {
    return this.tools.get(name) ?? this.tools.get(name.replace(/_/g, '.'));
  }

  list(): Tool[] {
    return Array.from(this.tools.values());
  }

  /** Set the tool policy for filtering. */
  setPolicy(policy: ToolPolicy): void {
    this._policy = policy;
  }

  /** Get the tool policy (if set). */
  getPolicy(): ToolPolicy | undefined {
    return this._policy;
  }

  /** Return tools filtered by the policy for a given context. */
  filteredList(context?: ToolPolicyContext): Tool[] {
    if (!this._policy) {return this.list();}
    return this._policy.filterTools(this.list(), context);
  }

  toAnthropicFormat(): AnthropicToolDef[] {
    return this.list().map(t => ({
      name: ToolRegistry.sanitizeToolName(t.name),
      description: t.description,
      input_schema: t.inputSchema,
    }));
  }

  /** Return Anthropic-format tools filtered by policy. */
  filteredToAnthropicFormat(context?: ToolPolicyContext): AnthropicToolDef[] {
    return this.filteredList(context).map(t => ({
      name: ToolRegistry.sanitizeToolName(t.name),
      description: t.description,
      input_schema: t.inputSchema,
    }));
  }

  toOpenAIFormat(): OpenAIToolDef[] {
    return this.list().map(t => ({
      type: 'function' as const,
      function: {
        name: ToolRegistry.sanitizeToolName(t.name),
        description: t.description,
        parameters: t.inputSchema,
      },
    }));
  }

  /** Return OpenAI-format tools filtered by policy. */
  filteredToOpenAIFormat(context?: ToolPolicyContext): OpenAIToolDef[] {
    return this.filteredList(context).map(t => ({
      type: 'function' as const,
      function: {
        name: ToolRegistry.sanitizeToolName(t.name),
        description: t.description,
        parameters: t.inputSchema,
      },
    }));
  }
}
