"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var orama_index_exports = {};
__export(orama_index_exports, {
  OramaIndex: () => OramaIndex
});
module.exports = __toCommonJS(orama_index_exports);
var path = __toESM(require("path"));
var fs = __toESM(require("fs"));
var import_errors = require("../errors");
const DEFAULT_DIM = 768;
const INDEX_FILE = "orama.json";
class OramaIndex {
  constructor(kirographDir, dim = DEFAULT_DIM) {
    this.kirographDir = kirographDir;
    this.dim = dim;
    this.db = null;
    this._available = false;
    // Lazily-loaded Orama functions
    this.orama = null;
    this.persistence = null;
    this.indexPath = path.join(kirographDir, INDEX_FILE);
  }
  isAvailable() {
    return this._available;
  }
  /**
   * Load @orama/orama + @orama/plugin-data-persistence, then either restore
   * the persisted index from orama.json or create a fresh one.
   * Silent no-op when optional deps are missing.
   */
  async initialize() {
    if (this._available) return;
    try {
      const oramaModule = require("@orama/orama");
      this.orama = {
        create: oramaModule.create,
        insert: oramaModule.insert,
        remove: oramaModule.remove,
        search: oramaModule.search,
        count: oramaModule.count
      };
    } catch {
      (0, import_errors.logDebug)("OramaIndex: @orama/orama not installed \u2014 Orama engine unavailable");
      return;
    }
    try {
      const persistModule = require("@orama/plugin-data-persistence/server");
      this.persistence = {
        persistToFile: persistModule.persistToFile,
        restoreFromFile: persistModule.restoreFromFile
      };
    } catch {
      (0, import_errors.logDebug)("OramaIndex: @orama/plugin-data-persistence not installed \u2014 Orama engine unavailable");
      return;
    }
    try {
      if (fs.existsSync(this.indexPath)) {
        this.db = await this.persistence.restoreFromFile("json", this.indexPath);
        (0, import_errors.logDebug)("OramaIndex: restored from disk", { path: this.indexPath });
      } else {
        this.db = await this.orama.create({
          schema: {
            nodeId: "enum",
            // exact-match filterable field (not full-text indexed)
            name: "string",
            kind: "string",
            filePath: "string",
            signature: "string",
            embedding: `vector[${this.dim}]`
          }
        });
        (0, import_errors.logDebug)("OramaIndex: created fresh index", { dim: this.dim });
      }
      this._available = true;
    } catch (err) {
      (0, import_errors.logError)("OramaIndex: initialization failed", { error: String(err) });
    }
  }
  /**
   * Insert or replace a node's document in the index.
   * Orama has no native upsert, so we remove any existing doc first.
   */
  async upsert(node, embedding) {
    if (!this._available || !this.db) return;
    try {
      const existing = await this.orama.search(this.db, {
        term: "",
        where: { nodeId: { eq: node.id } },
        limit: 1
      });
      if (existing?.hits?.length > 0) {
        for (const hit of existing.hits) {
          await this.orama.remove(this.db, hit.id);
        }
      }
      await this.orama.insert(this.db, {
        nodeId: node.id,
        name: node.name,
        kind: node.kind,
        filePath: node.filePath,
        signature: node.signature ?? "",
        embedding: Array.from(embedding)
      });
    } catch (err) {
      (0, import_errors.logWarn)("OramaIndex: upsert failed", { nodeId: node.id, error: String(err) });
    }
  }
  /**
   * Remove a node's document from the index.
   */
  async delete(nodeId) {
    if (!this._available || !this.db) return;
    try {
      const existing = await this.orama.search(this.db, {
        term: "",
        where: { nodeId: { eq: nodeId } },
        limit: 1
      });
      if (existing?.hits?.length > 0) {
        for (const hit of existing.hits) {
          await this.orama.remove(this.db, hit.id);
        }
      }
    } catch (err) {
      (0, import_errors.logWarn)("OramaIndex: delete failed", { nodeId, error: String(err) });
    }
  }
  /**
   * Hybrid search: combines full-text relevance on name/kind/filePath/signature
   * with vector similarity on the embedding field.
   * Returns node IDs ordered by Orama's combined score (best first).
   */
  async search(queryText, queryVec, topN = 10) {
    if (!this._available || !this.db) return [];
    try {
      const results = await this.orama.search(this.db, {
        term: queryText,
        mode: "hybrid",
        vector: {
          value: Array.from(queryVec),
          property: "embedding"
        },
        limit: topN,
        similarity: 0.3
      });
      return (results?.hits ?? []).map((hit) => hit.document.nodeId);
    } catch (err) {
      (0, import_errors.logWarn)("OramaIndex: search failed", { error: String(err) });
      return [];
    }
  }
  /** Persist the in-memory index to .kirograph/orama.json. */
  async save() {
    if (!this._available || !this.db || !this.persistence) return;
    try {
      await this.persistence.persistToFile(this.db, "json", this.indexPath);
      (0, import_errors.logDebug)("OramaIndex: saved to disk", { path: this.indexPath });
    } catch (err) {
      (0, import_errors.logWarn)("OramaIndex: save failed", { error: String(err) });
    }
  }
  /** Return all node IDs currently stored in the index. */
  async getEmbeddedNodeIds() {
    if (!this._available || !this.db) return [];
    try {
      const results = await this.orama.search(this.db, { term: "", limit: 1e6, includeVectors: false });
      return (results?.hits ?? []).map((hit) => hit.document.nodeId);
    } catch {
      return [];
    }
  }
  /** Number of documents currently in the index. */
  async count() {
    if (!this._available || !this.db) return 0;
    try {
      return await this.orama.count(this.db);
    } catch {
      return 0;
    }
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  OramaIndex
});
//# sourceMappingURL=orama-index.js.map
