"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 pglite_index_exports = {};
__export(pglite_index_exports, {
  PGliteIndex: () => PGliteIndex
});
module.exports = __toCommonJS(pglite_index_exports);
var path = __toESM(require("path"));
var import_errors = require("../errors");
const DEFAULT_DIM = 768;
const DB_DIR = "pglite";
const VECTOR_WEIGHT = 0.7;
const FTS_WEIGHT = 0.3;
class PGliteIndex {
  constructor(kirographDir, dim = DEFAULT_DIM) {
    this.kirographDir = kirographDir;
    this.dim = dim;
    this.db = null;
    this._available = false;
    this.dbPath = path.join(kirographDir, DB_DIR);
  }
  isAvailable() {
    return this._available;
  }
  /**
   * Load @electric-sql/pglite, open the file-persisted database, enable pgvector,
   * and apply the schema (idempotent — safe to call on every startup).
   * Silent no-op when the optional dep is missing.
   */
  async initialize() {
    if (this._available) return;
    let PGlite;
    let vector;
    try {
      const mod = require("@electric-sql/pglite");
      PGlite = mod.PGlite;
    } catch {
      (0, import_errors.logDebug)("PGliteIndex: @electric-sql/pglite not installed \u2014 PGlite engine unavailable");
      return;
    }
    try {
      const vectorMod = require("@electric-sql/pglite/vector");
      vector = vectorMod.vector ?? vectorMod;
    } catch {
      (0, import_errors.logDebug)("PGliteIndex: @electric-sql/pglite/vector not available \u2014 PGlite engine unavailable");
      return;
    }
    try {
      this.db = new PGlite(`file://${this.dbPath}`, { extensions: { vector } });
      await this.db.waitReady;
      await this.db.exec(`
        CREATE EXTENSION IF NOT EXISTS vector;

        CREATE TABLE IF NOT EXISTS kg_nodes (
          node_id   TEXT PRIMARY KEY,
          name      TEXT NOT NULL DEFAULT '',
          kind      TEXT NOT NULL DEFAULT '',
          file_path TEXT NOT NULL DEFAULT '',
          signature TEXT NOT NULL DEFAULT '',
          embedding vector(${this.dim}) NOT NULL
        );

        CREATE INDEX IF NOT EXISTS kg_nodes_hnsw_idx
          ON kg_nodes USING hnsw (embedding vector_cosine_ops);

        CREATE INDEX IF NOT EXISTS kg_nodes_name_idx
          ON kg_nodes USING GIN (to_tsvector('english', name || ' ' || signature));
      `);
      this._available = true;
      (0, import_errors.logDebug)("PGliteIndex: ready", { path: this.dbPath, dim: this.dim });
    } catch (err) {
      (0, import_errors.logError)("PGliteIndex: initialization failed", { error: String(err) });
    }
  }
  /**
   * Insert or update a node's record using PostgreSQL's native ON CONFLICT upsert.
   */
  async upsert(node, embedding) {
    if (!this._available || !this.db) return;
    try {
      await this.db.query(
        `INSERT INTO kg_nodes (node_id, name, kind, file_path, signature, embedding)
         VALUES ($1, $2, $3, $4, $5, $6)
         ON CONFLICT (node_id) DO UPDATE SET
           name      = EXCLUDED.name,
           kind      = EXCLUDED.kind,
           file_path = EXCLUDED.file_path,
           signature = EXCLUDED.signature,
           embedding = EXCLUDED.embedding`,
        [
          node.id,
          node.name,
          node.kind,
          node.filePath,
          node.signature ?? "",
          `[${Array.from(embedding).join(",")}]`
        ]
      );
    } catch (err) {
      (0, import_errors.logWarn)("PGliteIndex: upsert failed", { nodeId: node.id, error: String(err) });
    }
  }
  /**
   * Remove a node's record from the index.
   */
  async delete(nodeId) {
    if (!this._available || !this.db) return;
    try {
      await this.db.query("DELETE FROM kg_nodes WHERE node_id = $1", [nodeId]);
    } catch (err) {
      (0, import_errors.logWarn)("PGliteIndex: delete failed", { nodeId, error: String(err) });
    }
  }
  /**
   * Hybrid search: combines pgvector cosine distance with PostgreSQL full-text
   * ranking in a single SQL query. Returns node IDs ordered by combined score.
   *
   * Score = VECTOR_WEIGHT × (1 - cosine_distance) + FTS_WEIGHT × ts_rank
   */
  async search(queryText, queryVec, topN = 10) {
    if (!this._available || !this.db) return [];
    try {
      const vecLiteral = `[${Array.from(queryVec).join(",")}]`;
      const result = await this.db.query(
        `SELECT node_id,
                (${VECTOR_WEIGHT} * (1 - (embedding <=> $1::vector))
                 + ${FTS_WEIGHT}  * ts_rank(
                     to_tsvector('english', name || ' ' || signature),
                     plainto_tsquery('english', $2)
                   )
                ) AS score
         FROM kg_nodes
         ORDER BY score DESC
         LIMIT $3`,
        [vecLiteral, queryText, topN]
      );
      return (result.rows ?? []).map((row) => row.node_id);
    } catch (err) {
      (0, import_errors.logWarn)("PGliteIndex: search failed", { error: String(err) });
      return [];
    }
  }
  /** Return all node IDs currently stored in the index. */
  async getEmbeddedNodeIds() {
    if (!this._available || !this.db) return [];
    try {
      const result = await this.db.query("SELECT node_id FROM kg_nodes");
      return (result.rows ?? []).map((row) => row.node_id);
    } catch {
      return [];
    }
  }
  /** Number of documents currently in the index. */
  async count() {
    if (!this._available || !this.db) return 0;
    try {
      const result = await this.db.query("SELECT COUNT(*) AS count FROM kg_nodes");
      return parseInt(result.rows[0]?.count ?? "0", 10);
    } catch {
      return 0;
    }
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  PGliteIndex
});
//# sourceMappingURL=pglite-index.js.map
