"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 pipeline_exports = {};
__export(pipeline_exports, {
  IndexPipeline: () => IndexPipeline
});
module.exports = __toCommonJS(pipeline_exports);
var path = __toESM(require("path"));
var fs = __toESM(require("fs"));
var import_sync = require("../sync/index");
var import_extractor = require("../extraction/extractor");
var import_grammars = require("../extraction/grammars");
var import_frameworks = require("../frameworks/index");
var import_utils = require("../utils");
const FILE_IO_BATCH_SIZE = 10;
class IndexPipeline {
  constructor(db, vectors, resolver, arch, lock, config, projectRoot) {
    this.db = db;
    this.vectors = vectors;
    this.resolver = resolver;
    this.arch = arch;
    this.lock = lock;
    this.config = config;
    this.projectRoot = projectRoot;
    this.mutex = new import_utils.Mutex();
  }
  async indexAll(opts) {
    const release = await this.mutex.acquire();
    this.lock.acquire();
    const start = Date.now();
    const errors = [];
    let filesIndexed = 0;
    let nodesCreated = 0;
    let edgesCreated = 0;
    const poisonedLanguages = /* @__PURE__ */ new Set();
    try {
      const files = await (0, import_sync.scanDirectory)(this.projectRoot, this.config, opts?.signal);
      opts?.onProgress?.({ phase: "scanning", current: files.length, total: files.length });
      const contentMap = /* @__PURE__ */ new Map();
      for (let b = 0; b < files.length; b += FILE_IO_BATCH_SIZE) {
        const batch = files.slice(b, b + FILE_IO_BATCH_SIZE);
        const results = await Promise.all(batch.map((f) => fs.promises.readFile(f).catch(() => null)));
        for (let i = 0; i < batch.length; i++) {
          if (results[i]) contentMap.set(batch[i], results[i]);
        }
      }
      for (let i = 0; i < files.length; i++) {
        const file = files[i];
        opts?.onProgress?.({ phase: "parsing", current: i + 1, total: files.length, currentFile: file });
        try {
          const content = contentMap.get(file);
          if (!content) continue;
          if (content.length > this.config.maxFileSize) continue;
          const relPath = path.relative(this.projectRoot, file).replace(/\\/g, "/");
          if (!opts?.force) {
            const existing = this.db.getFile(relPath);
            if (existing && (0, import_sync.hashContent)(content) === existing.contentHash) continue;
          }
          const { detectLanguage } = await Promise.resolve().then(() => require("../extraction/languages.js"));
          const lang = detectLanguage(file);
          if (poisonedLanguages.has(lang)) continue;
          const extracted = await (0, import_extractor.extractFile)(file, this.projectRoot, content);
          if (!extracted) continue;
          const oldNodes = this.db.getNodesByFile(extracted.filePath);
          if (oldNodes.length > 0) await this.vectors.deleteEmbeddings(oldNodes.map((n) => n.id));
          this.db.transaction(() => {
            this.db.deleteNodesByFile(extracted.filePath);
            this.db.deleteUnresolvedRefsByFile(extracted.filePath);
            this.db.upsertFile({
              path: extracted.filePath,
              contentHash: extracted.contentHash,
              language: extracted.language,
              fileSize: extracted.fileSize,
              symbolCount: extracted.nodes.length,
              indexedAt: Date.now()
            });
            for (const node of extracted.nodes) {
              this.db.upsertNode(node);
              nodesCreated++;
            }
            for (const edge of extracted.edges) {
              this.db.insertEdge(edge);
              edgesCreated++;
            }
            for (const ref of extracted.unresolvedRefs) {
              this.db.insertUnresolvedRef(ref.sourceId, ref.refName, ref.refKind, extracted.filePath, ref.line, ref.column);
            }
          });
          filesIndexed++;
        } catch (err) {
          const msg = err instanceof Error ? err.message : String(err);
          errors.push(`${file}: ${msg}`);
          const isWasmCrash = err?.constructor?.name === "RuntimeError" || msg.includes("Aborted(") || msg.includes("RuntimeError") || msg.includes("WASM grammar exists but failed to load");
          if (isWasmCrash) {
            const { detectLanguage } = await Promise.resolve().then(() => require("../extraction/languages.js"));
            const lang = detectLanguage(file);
            poisonedLanguages.add(lang);
            (0, import_grammars.clearParserCache)();
            try {
              await (0, import_grammars.initGrammars)();
              poisonedLanguages.clear();
            } catch {
              errors.push("WASM runtime unrecoverable after crash \u2014 aborting batch");
              break;
            }
          }
        }
      }
      const emptyFiles = this.db.getAllFiles().filter(
        (f) => f.symbolCount === 0 && (0, import_grammars.hasWasmGrammar)(f.language)
      );
      if (emptyFiles.length > 0) {
        opts?.onProgress?.({ phase: "retrying", current: 0, total: emptyFiles.length });
        for (let i = 0; i < emptyFiles.length; i++) {
          const ef = emptyFiles[i];
          const absPath = path.join(this.projectRoot, ef.path);
          opts?.onProgress?.({ phase: "retrying", current: i + 1, total: emptyFiles.length, currentFile: absPath });
          try {
            const extracted = await (0, import_extractor.extractFile)(absPath, this.projectRoot);
            if (!extracted || extracted.nodes.length === 0) continue;
            const oldNodes = this.db.getNodesByFile(extracted.filePath);
            if (oldNodes.length > 0) await this.vectors.deleteEmbeddings(oldNodes.map((n) => n.id));
            this.db.transaction(() => {
              this.db.deleteNodesByFile(extracted.filePath);
              this.db.deleteUnresolvedRefsByFile(extracted.filePath);
              this.db.upsertFile({
                path: extracted.filePath,
                contentHash: extracted.contentHash,
                language: extracted.language,
                fileSize: extracted.fileSize,
                symbolCount: extracted.nodes.length,
                indexedAt: Date.now()
              });
              for (const node of extracted.nodes) {
                this.db.upsertNode(node);
                nodesCreated++;
              }
              for (const edge of extracted.edges) {
                this.db.insertEdge(edge);
                edgesCreated++;
              }
              for (const ref of extracted.unresolvedRefs) {
                this.db.insertUnresolvedRef(ref.sourceId, ref.refName, ref.refKind, extracted.filePath, ref.line, ref.column);
              }
            });
            filesIndexed++;
          } catch {
          }
        }
      }
      opts?.onProgress?.({ phase: "resolving", current: 0, total: 1 });
      await this.resolver.resolveAll((current, total) => {
        opts?.onProgress?.({ phase: "resolving", current, total });
      });
      opts?.onProgress?.({ phase: "detecting frameworks", current: 0, total: 1 });
      const detectedFrameworks = await (0, import_frameworks.detectFrameworks)(this.projectRoot, this.db);
      const languages = [...new Set(this.db.getAllFiles().map((f) => f.language).filter(Boolean))];
      opts?.onProgress?.({
        phase: "detecting frameworks",
        current: 1,
        total: 1,
        meta: { frameworks: detectedFrameworks.map((f) => f.name), languages }
      });
      if (this.vectors.isInitialized()) {
        opts?.onProgress?.({ phase: "embeddings", current: 0, total: 1 });
        await this.vectors.embedAll(
          (current, total) => opts?.onProgress?.({ phase: "embeddings", current, total })
        );
      }
      if (this.config.enableArchitecture) {
        opts?.onProgress?.({ phase: "architecture", current: 0, total: 1 });
        await this.arch.analyze(
          (msg) => opts?.onProgress?.({ phase: "architecture", current: 0, total: 1, meta: { msg } })
        );
        opts?.onProgress?.({ phase: "architecture", current: 1, total: 1 });
      }
      if (this.config.enableDocs) {
        try {
          const { DocsIndexer } = await Promise.resolve().then(() => require("../docs/indexer.js"));
          this.db.applyDocsSchema();
          const docsIndexer = new DocsIndexer(this.db.getRawDb(), this.config, this.projectRoot);
          await docsIndexer.indexAll({
            force: opts?.force,
            onProgress: (msg) => opts?.onProgress?.({ phase: "architecture", current: 0, total: 1, meta: { msg } })
          });
        } catch {
        }
      }
      this.lock.clearDirty();
      return { success: errors.length === 0, filesIndexed, nodesCreated, edgesCreated, errors, duration: Date.now() - start };
    } finally {
      this.lock.release();
      release();
    }
  }
  async sync(opts) {
    const release = await this.mutex.acquire();
    this.lock.acquire();
    const start = Date.now();
    const changedFiles = opts?.changedFiles;
    const onProgress = opts?.onProgress;
    const result = {
      added: [],
      modified: [],
      removed: [],
      nodesCreated: 0,
      nodesUpdated: 0,
      nodesRemoved: 0,
      edgesCreated: 0,
      edgesRemoved: 0,
      filesScanned: 0,
      errors: [],
      duration: 0
    };
    try {
      const removeFile = async (rel) => {
        const oldNodes = this.db.getNodesByFile(rel);
        const oldEdgeCount = this.db.getEdgesForNodes(oldNodes.map((n) => n.id)).length;
        result.nodesRemoved += oldNodes.length;
        result.edgesRemoved += oldEdgeCount;
        await this.vectors.deleteEmbeddings(oldNodes.map((n) => n.id));
        this.db.deleteFile(rel);
        this.db.deleteUnresolvedRefsByFile(rel);
        result.removed.push(rel);
      };
      const allIndexed = this.db.getAllFiles();
      for (const f of allIndexed) {
        if (!(0, import_sync.shouldIncludeFile)(f.path, this.config)) {
          onProgress?.({ phase: "scanning", current: 0, total: 0, meta: { excludeCleanup: true, file: f.path } });
          await removeFile(f.path);
        }
      }
      let filesToProcess;
      onProgress?.({ phase: "scanning", current: 0, total: 0 });
      if (changedFiles) {
        filesToProcess = changedFiles.map((f) => path.resolve(this.projectRoot, f));
      } else {
        const gitChanged = await (0, import_sync.getChangedFiles)(this.projectRoot, this.config);
        const hasChanges = gitChanged.added.length > 0 || gitChanged.modified.length > 0 || gitChanged.removed.length > 0;
        if (hasChanges) {
          for (const p of gitChanged.removed) {
            await removeFile(path.relative(this.projectRoot, p).replace(/\\/g, "/"));
          }
          filesToProcess = [...gitChanged.added, ...gitChanged.modified];
        } else {
          const indexed = new Set(this.db.getAllFiles().map((f) => f.path));
          const current = new Set(
            (await (0, import_sync.scanDirectory)(this.projectRoot, this.config)).map((f) => path.relative(this.projectRoot, f).replace(/\\/g, "/"))
          );
          result.filesScanned = current.size;
          for (const p of indexed) {
            if (!current.has(p)) await removeFile(p);
          }
          filesToProcess = await (0, import_sync.scanDirectory)(this.projectRoot, this.config);
        }
      }
      result.filesScanned = result.filesScanned || filesToProcess.length;
      onProgress?.({ phase: "scanning", current: result.filesScanned, total: result.filesScanned });
      const poisonedLanguages = /* @__PURE__ */ new Set();
      for (let i = 0; i < filesToProcess.length; i++) {
        const file = filesToProcess[i];
        onProgress?.({ phase: "parsing", current: i + 1, total: filesToProcess.length, currentFile: file });
        if (!fs.existsSync(file)) {
          const rel = path.relative(this.projectRoot, file).replace(/\\/g, "/");
          await removeFile(rel);
          continue;
        }
        try {
          const { detectLanguage } = await Promise.resolve().then(() => require("../extraction/languages.js"));
          const lang = detectLanguage(file);
          if (poisonedLanguages.has(lang)) continue;
          const extracted = await (0, import_extractor.extractFile)(file, this.projectRoot);
          if (!extracted) continue;
          const existing = this.db.getFile(extracted.filePath);
          const isNew = !existing;
          if (!isNew && existing.contentHash === extracted.contentHash) continue;
          const oldNodes = this.db.getNodesByFile(extracted.filePath);
          const oldEdgeCount = this.db.getEdgesForNodes(oldNodes.map((n) => n.id)).length;
          await this.vectors.deleteEmbeddings(oldNodes.map((n) => n.id));
          this.db.transaction(() => {
            result.nodesRemoved += oldNodes.length;
            result.edgesRemoved += oldEdgeCount;
            this.db.deleteNodesByFile(extracted.filePath);
            this.db.deleteUnresolvedRefsByFile(extracted.filePath);
            this.db.upsertFile({
              path: extracted.filePath,
              contentHash: extracted.contentHash,
              language: extracted.language,
              fileSize: extracted.fileSize,
              symbolCount: extracted.nodes.length,
              indexedAt: Date.now()
            });
            for (const node of extracted.nodes) {
              this.db.upsertNode(node);
              result.nodesCreated++;
            }
            for (const edge of extracted.edges) {
              this.db.insertEdge(edge);
              result.edgesCreated++;
            }
            for (const ref of extracted.unresolvedRefs) {
              this.db.insertUnresolvedRef(ref.sourceId, ref.refName, ref.refKind, extracted.filePath, ref.line, ref.column);
            }
          });
          this.resolver.invalidateFile(extracted.filePath);
          if (isNew) result.added.push(extracted.filePath);
          else {
            result.modified.push(extracted.filePath);
            result.nodesUpdated += extracted.nodes.length;
          }
        } catch (err) {
          const msg = err instanceof Error ? err.message : String(err);
          result.errors.push(`${file}: ${msg}`);
          const isWasmCrash = err?.constructor?.name === "RuntimeError" || msg.includes("Aborted(") || msg.includes("RuntimeError") || msg.includes("WASM grammar exists but failed to load");
          if (isWasmCrash) {
            const { detectLanguage } = await Promise.resolve().then(() => require("../extraction/languages.js"));
            const lang = detectLanguage(file);
            poisonedLanguages.add(lang);
            (0, import_grammars.clearParserCache)();
            try {
              await (0, import_grammars.initGrammars)();
              poisonedLanguages.clear();
            } catch {
              result.errors.push("WASM runtime unrecoverable after crash \u2014 aborting sync");
              break;
            }
          }
        }
      }
      onProgress?.({ phase: "resolving", current: 0, total: 1 });
      await this.resolver.resolveAll((current, total) => {
        onProgress?.({ phase: "resolving", current, total });
      });
      await (0, import_frameworks.detectFrameworks)(this.projectRoot);
      if (this.vectors.isInitialized()) {
        onProgress?.({ phase: "embeddings", current: 0, total: 1 });
        await this.vectors.embedAll(
          (current, total) => onProgress?.({ phase: "embeddings", current, total })
        );
      }
      if (this.config.enableArchitecture) {
        onProgress?.({ phase: "architecture", current: 0, total: 1 });
        await this.arch.analyze(
          (msg) => onProgress?.({ phase: "architecture", current: 0, total: 1, meta: { msg } })
        );
        onProgress?.({ phase: "architecture", current: 1, total: 1 });
      }
      if (this.config.enableDocs) {
        try {
          const { DocsIndexer } = await Promise.resolve().then(() => require("../docs/indexer.js"));
          this.db.applyDocsSchema();
          const docsIndexer = new DocsIndexer(this.db.getRawDb(), this.config, this.projectRoot);
          await docsIndexer.indexAll({
            onProgress: (msg) => onProgress?.({ phase: "architecture", current: 0, total: 1, meta: { msg } })
          });
        } catch {
        }
      }
      this.lock.clearDirty();
      result.duration = Date.now() - start;
      return result;
    } finally {
      this.lock.release();
      release();
    }
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  IndexPipeline
});
//# sourceMappingURL=pipeline.js.map
