import {
  TOOL_ENTITY_ID,
  GLOBAL_TENANT_ID,
  TOOL_SEARCH_CONFIG,
  toolToIndexableRecord,
  computeToolsChecksum
} from "./tool-index-config.js";
class ToolSearchService {
  constructor(searchService, toolRegistry) {
    this.searchService = searchService;
    this.toolRegistry = toolRegistry;
    this.lastIndexChecksum = null;
  }
  /**
   * Get status of available search strategies.
   * This helps the AI understand which integrations are available.
   */
  async getStrategyStatus() {
    const [fulltext, vector, tokens] = await Promise.all([
      this.searchService.isStrategyAvailable("fulltext"),
      this.searchService.isStrategyAvailable("vector"),
      this.searchService.isStrategyAvailable("tokens")
    ]);
    const available = [];
    if (fulltext) available.push("fulltext");
    if (vector) available.push("vector");
    if (tokens) available.push("tokens");
    const defaultStrategies = this.searchService.getDefaultStrategies();
    return {
      fulltext,
      vector,
      tokens,
      available,
      default: defaultStrategies.filter((s) => available.includes(s))
    };
  }
  /**
   * Search for relevant tools using hybrid strategies.
   *
   * @param query - User's search query or message
   * @param options - Search options
   * @returns Array of matching tools sorted by relevance
   */
  async searchTools(query, options = {}) {
    const {
      limit = TOOL_SEARCH_CONFIG.defaultLimit,
      strategies,
      userFeatures = [],
      isSuperAdmin = false,
      minScore = TOOL_SEARCH_CONFIG.minScore
    } = options;
    const searchResults = await this.searchService.search(query, {
      tenantId: GLOBAL_TENANT_ID,
      organizationId: null,
      entityTypes: [TOOL_ENTITY_ID],
      strategies,
      limit: limit * 2
      // Get extra results to account for ACL filtering
    });
    const toolResults = [];
    for (const result of searchResults) {
      if (result.score < minScore) continue;
      const metadata = result.metadata;
      const requiredFeatures = metadata?.requiredFeatures ?? [];
      const moduleId = metadata?.moduleId;
      if (!this.hasFeatureAccess(requiredFeatures, userFeatures, isSuperAdmin)) {
        continue;
      }
      toolResults.push({
        toolName: result.recordId,
        score: result.score,
        moduleId,
        requiredFeatures
      });
      if (toolResults.length >= limit) break;
    }
    return toolResults;
  }
  /**
   * Index all tools from the registry using available strategies.
   * Uses checksum-based change detection to avoid unnecessary re-indexing.
   *
   * @param force - Force re-indexing even if checksum hasn't changed
   * @returns Indexing result with statistics
   */
  async indexTools(force = false) {
    const tools = Array.from(this.toolRegistry.getTools().values());
    const modules = this.getToolModules();
    const checksum = computeToolsChecksum(
      tools.map((t) => ({ name: t.name, description: t.description }))
    );
    if (!force && this.lastIndexChecksum === checksum) {
      const status2 = await this.getStrategyStatus();
      return {
        indexed: 0,
        skipped: tools.length,
        strategies: status2.available,
        checksum
      };
    }
    const records = [];
    for (const tool of tools) {
      const moduleId = modules.get(tool.name);
      records.push(toolToIndexableRecord(tool, moduleId));
    }
    if (records.length > 0) {
      try {
        await this.searchService.bulkIndex(records);
      } catch (error) {
        throw new Error(
          `Failed to index ${records.length} tools: ${error instanceof Error ? error.message : error}`
        );
      }
    }
    this.lastIndexChecksum = checksum;
    const status = await this.getStrategyStatus();
    return {
      indexed: records.length,
      skipped: 0,
      strategies: status.available,
      checksum
    };
  }
  /**
   * Purge all tool records from the search index.
   * Useful for cleanup or complete re-indexing.
   */
  async purgeIndex() {
    await this.searchService.purge(TOOL_ENTITY_ID, GLOBAL_TENANT_ID);
    this.lastIndexChecksum = null;
  }
  /**
   * Get tools by module ID from the registry.
   */
  getToolsByModule(moduleId) {
    return this.toolRegistry.listToolsByModule(moduleId);
  }
  /**
   * Build a map of tool name to module ID.
   */
  getToolModules() {
    const modules = /* @__PURE__ */ new Map();
    const modulePatterns = [
      { pattern: /^customers_/, moduleId: "customers" },
      { pattern: /^sales_/, moduleId: "sales" },
      { pattern: /^catalog_/, moduleId: "catalog" },
      { pattern: /^staff_/, moduleId: "staff" },
      { pattern: /^resources_/, moduleId: "resources" },
      { pattern: /^planner/, moduleId: "planner" },
      { pattern: /^search_/, moduleId: "search" },
      { pattern: /^context_/, moduleId: "context" },
      { pattern: /^auth_/, moduleId: "auth" },
      { pattern: /^directory_/, moduleId: "directory" },
      { pattern: /^dashboards_/, moduleId: "dashboards" },
      { pattern: /^entities_/, moduleId: "entities" },
      { pattern: /^dictionaries_/, moduleId: "dictionaries" },
      { pattern: /^workflows_/, moduleId: "workflows" },
      { pattern: /^business_rules_/, moduleId: "business_rules" },
      { pattern: /^audit_logs_/, moduleId: "audit_logs" },
      { pattern: /^attachments_/, moduleId: "attachments" },
      { pattern: /^feature_toggles_/, moduleId: "feature_toggles" },
      { pattern: /^currencies_/, moduleId: "currencies" },
      { pattern: /^example_/, moduleId: "example" },
      { pattern: /^cli_/, moduleId: "cli" }
    ];
    for (const toolName of this.toolRegistry.listToolNames()) {
      for (const { pattern, moduleId } of modulePatterns) {
        if (pattern.test(toolName)) {
          modules.set(toolName, moduleId);
          break;
        }
      }
    }
    return modules;
  }
  /**
   * Check if user has required features to access a tool.
   */
  hasFeatureAccess(requiredFeatures, userFeatures, isSuperAdmin) {
    if (isSuperAdmin) return true;
    if (!requiredFeatures?.length) return true;
    return requiredFeatures.every((required) => {
      if (userFeatures.includes(required)) return true;
      if (userFeatures.includes("*")) return true;
      return userFeatures.some((feature) => {
        if (feature.endsWith(".*")) {
          const prefix = feature.slice(0, -2);
          return required.startsWith(prefix + ".");
        }
        return false;
      });
    });
  }
}
export {
  ToolSearchService
};
//# sourceMappingURL=tool-search.js.map
