import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { z } from "zod";
import { v4 as uuidv4 } from "uuid";
import { logger } from "../monitoring/logger.js";
import {
  ValidationError,
  ErrorCode,
  StackMemoryError,
  SystemError
} from "../errors/index.js";
const ToolNameSchema = z.string().min(3, "Tool name must be at least 3 characters").max(64, "Tool name must be at most 64 characters").regex(
  /^[a-z][a-z0-9_]*$/,
  "Tool name must start with lowercase letter and contain only lowercase letters, numbers, and underscores"
);
const JSONSchemaPropertySchema = z.lazy(
  () => z.object({
    type: z.enum(["string", "number", "boolean", "array", "object", "null"]),
    description: z.string().optional(),
    enum: z.array(z.union([z.string(), z.number(), z.boolean()])).optional(),
    default: z.unknown().optional(),
    minimum: z.number().optional(),
    maximum: z.number().optional(),
    minLength: z.number().optional(),
    maxLength: z.number().optional(),
    pattern: z.string().optional(),
    items: JSONSchemaPropertySchema.optional(),
    properties: z.record(JSONSchemaPropertySchema).optional(),
    required: z.array(z.string()).optional(),
    format: z.string().optional()
  })
);
const JSONSchemaSchema = z.object({
  type: z.literal("object"),
  properties: z.record(JSONSchemaPropertySchema).optional(),
  required: z.array(z.string()).optional(),
  additionalProperties: z.boolean().optional(),
  description: z.string().optional(),
  default: z.unknown().optional()
});
const ToolMetadataSchema = z.object({
  version: z.string().optional(),
  author: z.string().optional(),
  category: z.string().optional(),
  tags: z.array(z.string()).optional(),
  timeout: z.number().min(100).max(3e5).optional(),
  permissions: z.array(z.string()).optional()
}).optional();
class ToolRegistry {
  tools = /* @__PURE__ */ new Map();
  eventListeners = /* @__PURE__ */ new Map();
  extensionStorage = /* @__PURE__ */ new Map();
  defaultTimeout = 3e4;
  // 30 seconds
  constructor(options) {
    if (options?.defaultTimeout) {
      this.defaultTimeout = options.defaultTimeout;
    }
    logger.info("ToolRegistry initialized");
  }
  // ============================================
  // Registration
  // ============================================
  /**
   * Register a new custom tool
   * @throws ValidationError if tool definition is invalid
   * @throws StackMemoryError if tool with same name already exists
   */
  register(definition) {
    this.validateToolDefinition(definition);
    if (this.tools.has(definition.name)) {
      throw new StackMemoryError({
        code: ErrorCode.VALIDATION_FAILED,
        message: `Tool '${definition.name}' is already registered`,
        context: { toolName: definition.name }
      });
    }
    const toolId = uuidv4();
    const registeredTool = {
      ...definition,
      id: toolId,
      registeredAt: /* @__PURE__ */ new Date(),
      usageCount: 0,
      enabled: true
    };
    this.tools.set(definition.name, registeredTool);
    logger.info("Tool registered", {
      toolId,
      name: definition.name,
      category: definition.metadata?.category
    });
    return toolId;
  }
  /**
   * Unregister a tool by name
   */
  unregister(name) {
    const tool = this.tools.get(name);
    if (!tool) {
      return false;
    }
    this.tools.delete(name);
    this.extensionStorage.delete(name);
    logger.info("Tool unregistered", { name, toolId: tool.id });
    return true;
  }
  /**
   * Update an existing tool definition
   */
  update(name, updates) {
    const tool = this.tools.get(name);
    if (!tool) {
      return false;
    }
    if (updates.parameters) {
      const result = JSONSchemaSchema.safeParse(updates.parameters);
      if (!result.success) {
        throw new ValidationError(
          `Invalid parameters schema: ${result.error.message}`,
          ErrorCode.VALIDATION_FAILED,
          { errors: result.error.errors }
        );
      }
    }
    Object.assign(tool, updates);
    logger.info("Tool updated", { name, toolId: tool.id });
    return true;
  }
  // ============================================
  // Discovery
  // ============================================
  /**
   * Get a tool by name
   */
  get(name) {
    return this.tools.get(name);
  }
  /**
   * List all registered tools
   */
  list(filter) {
    let tools = Array.from(this.tools.values());
    if (filter?.category) {
      tools = tools.filter((t) => t.metadata?.category === filter.category);
    }
    if (filter?.tags && filter.tags.length > 0) {
      tools = tools.filter(
        (t) => filter.tags.some((tag) => t.metadata?.tags?.includes(tag))
      );
    }
    if (filter?.enabled !== void 0) {
      tools = tools.filter((t) => t.enabled === filter.enabled);
    }
    return tools;
  }
  /**
   * Get tool definitions in MCP format
   */
  getMCPToolDefinitions() {
    return Array.from(this.tools.values()).filter((t) => t.enabled).map((t) => ({
      name: t.name,
      description: t.description,
      inputSchema: t.parameters
    }));
  }
  /**
   * Check if a tool exists
   */
  has(name) {
    return this.tools.has(name);
  }
  /**
   * Get tool count
   */
  get size() {
    return this.tools.size;
  }
  // ============================================
  // Execution
  // ============================================
  /**
   * Execute a tool with sandboxed context
   */
  async execute(name, params, options) {
    const tool = this.tools.get(name);
    if (!tool) {
      return {
        success: false,
        error: `Tool '${name}' not found`
      };
    }
    if (!tool.enabled) {
      return {
        success: false,
        error: `Tool '${name}' is disabled`
      };
    }
    const validationResult = this.validateParams(params, tool.parameters);
    if (!validationResult.valid) {
      return {
        success: false,
        error: `Invalid parameters: ${validationResult.errors.join(", ")}`
      };
    }
    const context = this.createSandboxedContext(name, options?.context);
    const timeout = options?.timeout ?? tool.metadata?.timeout ?? this.defaultTimeout;
    try {
      const result = await this.executeWithSandbox(
        () => tool.execute(params, context),
        timeout,
        options?.sandboxed ?? true
      );
      tool.lastUsedAt = /* @__PURE__ */ new Date();
      tool.usageCount++;
      logger.info("Tool executed successfully", {
        name,
        toolId: tool.id,
        duration: Date.now() - tool.lastUsedAt.getTime()
      });
      return result;
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      logger.error("Tool execution failed", {
        name,
        toolId: tool.id,
        error: message
      });
      return {
        success: false,
        error: message,
        metadata: { toolId: tool.id }
      };
    }
  }
  // ============================================
  // Private Methods
  // ============================================
  /**
   * Validate a tool definition before registration
   */
  validateToolDefinition(definition) {
    const nameResult = ToolNameSchema.safeParse(definition.name);
    if (!nameResult.success) {
      throw new ValidationError(
        `Invalid tool name: ${nameResult.error.message}`,
        ErrorCode.VALIDATION_FAILED,
        { name: definition.name, errors: nameResult.error.errors }
      );
    }
    if (!definition.description || definition.description.length < 10) {
      throw new ValidationError(
        "Tool description must be at least 10 characters",
        ErrorCode.VALIDATION_FAILED,
        { description: definition.description }
      );
    }
    if (definition.description.length > 500) {
      throw new ValidationError(
        "Tool description must be at most 500 characters",
        ErrorCode.VALIDATION_FAILED,
        { descriptionLength: definition.description.length }
      );
    }
    const schemaResult = JSONSchemaSchema.safeParse(definition.parameters);
    if (!schemaResult.success) {
      throw new ValidationError(
        `Invalid parameters schema: ${schemaResult.error.message}`,
        ErrorCode.VALIDATION_FAILED,
        { errors: schemaResult.error.errors }
      );
    }
    if (typeof definition.execute !== "function") {
      throw new ValidationError(
        "Tool execute must be a function",
        ErrorCode.VALIDATION_FAILED
      );
    }
    if (definition.metadata) {
      const metaResult = ToolMetadataSchema.safeParse(definition.metadata);
      if (!metaResult.success) {
        throw new ValidationError(
          `Invalid tool metadata: ${metaResult.error.message}`,
          ErrorCode.VALIDATION_FAILED,
          { errors: metaResult.error.errors }
        );
      }
    }
  }
  /**
   * Validate parameters against JSON Schema
   */
  validateParams(params, schema) {
    const errors = [];
    if (schema.type !== "object") {
      errors.push('Parameters schema must have type "object"');
      return { valid: false, errors };
    }
    if (params === null || params === void 0) {
      if (schema.required && schema.required.length > 0) {
        errors.push(
          `Missing required parameters: ${schema.required.join(", ")}`
        );
      }
      return { valid: errors.length === 0, errors };
    }
    if (typeof params !== "object" || Array.isArray(params)) {
      errors.push("Parameters must be an object");
      return { valid: false, errors };
    }
    const paramObj = params;
    if (schema.required) {
      for (const field of schema.required) {
        if (!(field in paramObj)) {
          errors.push(`Missing required parameter: ${field}`);
        }
      }
    }
    if (schema.properties) {
      for (const [key, propSchema] of Object.entries(schema.properties)) {
        if (key in paramObj) {
          const propErrors = this.validateProperty(
            paramObj[key],
            propSchema,
            key
          );
          errors.push(...propErrors);
        }
      }
      if (schema.additionalProperties === false) {
        for (const key of Object.keys(paramObj)) {
          if (!(key in schema.properties)) {
            errors.push(`Unknown parameter: ${key}`);
          }
        }
      }
    }
    return { valid: errors.length === 0, errors };
  }
  /**
   * Validate a single property value
   */
  validateProperty(value, schema, path) {
    const errors = [];
    const actualType = this.getJSONType(value);
    if (actualType !== schema.type && value !== void 0 && value !== null) {
      if (!(schema.type === "null" && value === null)) {
        errors.push(
          `Parameter '${path}' must be of type ${schema.type}, got ${actualType}`
        );
      }
    }
    if (schema.type === "string" && typeof value === "string") {
      if (schema.minLength !== void 0 && value.length < schema.minLength) {
        errors.push(
          `Parameter '${path}' must be at least ${schema.minLength} characters`
        );
      }
      if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
        errors.push(
          `Parameter '${path}' must be at most ${schema.maxLength} characters`
        );
      }
      if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
        errors.push(
          `Parameter '${path}' does not match pattern ${schema.pattern}`
        );
      }
      if (schema.enum && !schema.enum.includes(value)) {
        errors.push(
          `Parameter '${path}' must be one of: ${schema.enum.join(", ")}`
        );
      }
    }
    if (schema.type === "number" && typeof value === "number") {
      if (schema.minimum !== void 0 && value < schema.minimum) {
        errors.push(`Parameter '${path}' must be at least ${schema.minimum}`);
      }
      if (schema.maximum !== void 0 && value > schema.maximum) {
        errors.push(`Parameter '${path}' must be at most ${schema.maximum}`);
      }
      if (schema.enum && !schema.enum.includes(value)) {
        errors.push(
          `Parameter '${path}' must be one of: ${schema.enum.join(", ")}`
        );
      }
    }
    if (schema.type === "array" && Array.isArray(value) && schema.items) {
      value.forEach((item, index) => {
        const itemErrors = this.validateProperty(
          item,
          schema.items,
          `${path}[${index}]`
        );
        errors.push(...itemErrors);
      });
    }
    return errors;
  }
  /**
   * Get JSON type of a value
   */
  getJSONType(value) {
    if (value === null) return "null";
    if (Array.isArray(value)) return "array";
    return typeof value;
  }
  /**
   * Create a sandboxed extension context
   */
  createSandboxedContext(toolName, overrides) {
    if (!this.extensionStorage.has(toolName)) {
      this.extensionStorage.set(toolName, /* @__PURE__ */ new Map());
    }
    const storage = this.extensionStorage.get(toolName);
    const context = {
      projectId: overrides?.projectId ?? "sandbox",
      sessionId: overrides?.sessionId ?? uuidv4(),
      runId: overrides?.runId ?? uuidv4(),
      // Sandboxed fetch (could add URL allowlist here)
      fetch: globalThis.fetch,
      // Extension storage
      storage: {
        async get(key) {
          return storage.get(key);
        },
        async set(key, value) {
          storage.set(key, value);
        },
        async delete(key) {
          storage.delete(key);
        },
        async clear() {
          storage.clear();
        }
      },
      // Event communication
      emit: (event, data) => {
        const listeners = this.eventListeners.get(event);
        if (listeners) {
          listeners.forEach((handler) => {
            try {
              handler(data);
            } catch (error) {
              logger.error("Event handler error", {
                event,
                error: error instanceof Error ? error.message : String(error)
              });
            }
          });
        }
      },
      on: (event, handler) => {
        if (!this.eventListeners.has(event)) {
          this.eventListeners.set(event, /* @__PURE__ */ new Set());
        }
        this.eventListeners.get(event).add(handler);
        return () => {
          this.eventListeners.get(event)?.delete(handler);
        };
      },
      // Sandboxed logging
      log: {
        info: (message, meta) => {
          logger.info(`[Tool:${toolName}] ${message}`, meta);
        },
        warn: (message, meta) => {
          logger.warn(`[Tool:${toolName}] ${message}`, meta);
        },
        error: (message, meta) => {
          logger.error(`[Tool:${toolName}] ${message}`, meta);
        }
      }
    };
    return context;
  }
  /**
   * Execute tool with sandbox wrapper and timeout
   */
  async executeWithSandbox(executor, timeout, sandboxed) {
    const timeoutPromise = new Promise((_, reject) => {
      setTimeout(() => {
        reject(
          new SystemError(
            `Tool execution timed out after ${timeout}ms`,
            ErrorCode.OPERATION_TIMEOUT
          )
        );
      }, timeout);
    });
    const executionPromise = (async () => {
      try {
        if (sandboxed) {
          return await Promise.resolve(executor());
        } else {
          return await Promise.resolve(executor());
        }
      } catch (error) {
        if (error instanceof StackMemoryError) {
          throw error;
        }
        throw new SystemError(
          `Tool execution error: ${error instanceof Error ? error.message : String(error)}`,
          ErrorCode.MCP_EXECUTION_FAILED,
          { error: String(error) }
        );
      }
    })();
    return Promise.race([executionPromise, timeoutPromise]);
  }
  /**
   * Clear all event listeners
   */
  clearEventListeners() {
    this.eventListeners.clear();
  }
  /**
   * Get statistics about registered tools
   */
  getStats() {
    const tools = Array.from(this.tools.values());
    const byCategory = {};
    for (const tool of tools) {
      const category = tool.metadata?.category ?? "uncategorized";
      byCategory[category] = (byCategory[category] ?? 0) + 1;
    }
    return {
      totalTools: tools.length,
      enabledTools: tools.filter((t) => t.enabled).length,
      totalExecutions: tools.reduce((sum, t) => sum + t.usageCount, 0),
      byCategory
    };
  }
  /**
   * Enable or disable a tool
   */
  setEnabled(name, enabled) {
    const tool = this.tools.get(name);
    if (!tool) {
      return false;
    }
    tool.enabled = enabled;
    logger.info(`Tool ${enabled ? "enabled" : "disabled"}`, { name });
    return true;
  }
}
function createToolRegistry(options) {
  return new ToolRegistry(options);
}
function defineTool(name, description, parameters, execute, metadata) {
  return {
    name,
    description,
    parameters,
    execute,
    metadata
  };
}
var custom_tools_default = ToolRegistry;
export {
  ToolRegistry,
  createToolRegistry,
  custom_tools_default as default,
  defineTool
};
//# sourceMappingURL=custom-tools.js.map
