const STOP_WORDS = /* @__PURE__ */ new Set([
  "the",
  "and",
  "for",
  "this",
  "that",
  "with",
  "from",
  "test",
  "task",
  "implement",
  "add",
  "fix",
  "bug",
  "issue",
  "update",
  "delete",
  "remove",
  "create",
  "make",
  "get",
  "set",
  "run",
  "code",
  "file",
  "project",
  "class",
  "function",
  "method",
  "interface",
  "type",
  "import",
  "export",
  "require",
  "include",
  "exclude"
]);
const SUFFIXES = /* @__PURE__ */ new Set(["controller", "service", "repository", "manager", "handler", "helper", "provider", "model"]);
class ContextBuilder {
  store;
  graph;
  constructor(store, graph) {
    this.store = store;
    this.graph = graph;
  }
  static extractKeywords(text) {
    const withSpaces = text.replace(/([a-z])([A-Z])/g, "$1 $2");
    const words = withSpaces.toLowerCase().split(/[^a-z0-9]+/);
    const keywords = [];
    for (const word of words) {
      if (word.length >= 3 && !STOP_WORDS.has(word)) {
        keywords.push(word);
        for (const suffix of SUFFIXES) {
          if (word.endsWith(suffix) && word.length > suffix.length) {
            keywords.push(word.slice(0, -suffix.length));
          }
        }
      }
    }
    return Array.from(new Set(keywords));
  }
  async buildContext(options) {
    const budget = options.tokens ?? 8192;
    const maxDepth = options.depth ?? 2;
    const repo = options.repo;
    const matchedSymbols = [];
    const seedFiles = /* @__PURE__ */ new Set();
    if (options.seeds) {
      for (const seed of options.seeds) {
        if (seed.includes(".") || seed.includes("/")) {
          if (this.store.getFile(seed)) {
            seedFiles.add(seed);
          }
        } else {
          const sym = this.store.getSymbolByName(seed, repo);
          if (sym) {
            seedFiles.add(sym.file_path);
            matchedSymbols.push({
              name: sym.name,
              kind: sym.kind,
              filePath: sym.file_path
            });
          }
        }
      }
    }
    const keywords = ContextBuilder.extractKeywords(options.task);
    for (const kw of keywords) {
      const syms = this.store.searchSymbolsFiltered({ term: kw, repo, limit: 10 });
      for (const sym of syms) {
        seedFiles.add(sym.file_path);
        if (!matchedSymbols.some((s) => s.name === sym.name && s.filePath === sym.file_path)) {
          matchedSymbols.push({
            name: sym.name,
            kind: sym.kind,
            filePath: sym.file_path
          });
        }
      }
    }
    const allFiles = this.store.getAllFiles(repo);
    for (const kw of keywords) {
      const kwLower = kw.toLowerCase();
      for (const f of allFiles) {
        if (f.path.toLowerCase().includes(kwLower)) {
          seedFiles.add(f.path);
        }
      }
    }
    if (seedFiles.size === 0) {
      const topFiles = this.graph.getRankedFiles().slice(0, 5);
      for (const tf of topFiles) {
        seedFiles.add(tf.path);
      }
    }
    const distances = /* @__PURE__ */ new Map();
    for (const sf of seedFiles) {
      distances.set(sf, 0);
    }
    const queue = Array.from(seedFiles);
    let head = 0;
    while (head < queue.length) {
      const file = queue[head++];
      const currentDepth = distances.get(file);
      if (currentDepth >= maxDepth) continue;
      const neighbors = [
        ...this.graph.getDependencies(file).map((d) => d.target),
        ...this.graph.getReverseDependencies(file).map((r) => r.source)
      ];
      for (const neighbor of neighbors) {
        if (!distances.has(neighbor)) {
          distances.set(neighbor, currentDepth + 1);
          queue.push(neighbor);
        }
      }
    }
    const rankedAll = this.graph.getRankedFiles();
    const rankedMap = /* @__PURE__ */ new Map();
    for (const f of rankedAll) {
      rankedMap.set(f.path, f.pagerank);
    }
    const candidates = Array.from(distances.keys());
    const seedClusters = /* @__PURE__ */ new Set();
    if (seedFiles.size > 0) {
      try {
        const placeholders = Array.from(seedFiles).map(() => "?").join(",");
        const sql = `SELECT cluster_name FROM cluster_membership WHERE file_path IN (${placeholders}) AND is_primary = 1`;
        const rows = this.store.raw.prepare(sql).all(...Array.from(seedFiles));
        for (const r of rows) {
          seedClusters.add(r.cluster_name);
        }
      } catch (e) {
      }
    }
    const candidateClustersMap = /* @__PURE__ */ new Map();
    if (candidates.length > 0) {
      try {
        const placeholders = candidates.map(() => "?").join(",");
        const sql = `SELECT file_path, cluster_name FROM cluster_membership WHERE file_path IN (${placeholders}) AND is_primary = 1`;
        const rows = this.store.raw.prepare(sql).all(...candidates);
        for (const r of rows) {
          candidateClustersMap.set(r.file_path, r.cluster_name);
        }
      } catch (e) {
      }
    }
    const roleKeywordMap = {
      api: ["api", "route", "router", "controller", "endpoint", "handler", "resolver", "graphql", "rest", "serve", "server"],
      middleware: ["middleware", "guard", "interceptor", "cors", "auth-middleware", "filter"],
      service: ["service", "logic", "usecase", "manager", "handler", "workflow", "processor"],
      data: ["data", "db", "database", "sql", "repository", "dao", "model", "schema", "entity", "migration", "seed", "query", "orm", "prisma", "mongoose"],
      integration: ["integration", "client", "sdk", "api-client", "http-client", "external", "fetch", "axios", "message-producer", "kafka", "rabbitmq", "sqs"],
      auth: ["auth", "login", "signup", "register", "signin", "logout", "identity", "jwt", "token", "passport", "session", "role", "permission"],
      pages: ["page", "pages", "view", "views", "screen", "screens", "route-page", "navigation"],
      components: ["component", "components", "widget", "widgets", "ui", "button", "input", "modal", "card", "dialog", "form", "control"],
      state: ["state", "store", "stores", "reducer", "redux", "vuex", "pinia", "context", "atom", "recoil", "zustand", "signal"],
      hooks: ["hook", "hooks", "composable", "composables", "mixin", "mixins", "use-effect", "use-state"],
      styles: ["style", "styles", "stylesheet", "css", "scss", "sass", "less", "theme", "themes", "tailwind", "bootstrap", "design-token"],
      assets: ["asset", "assets", "image", "images", "font", "fonts", "icon", "icons", "logo", "svg", "png", "jpg"],
      cli: ["cli", "command", "commands", "arg", "args", "argument", "arguments", "flag", "flags", "terminal", "console", "stdin", "stdout", "bin"],
      core: ["core", "engine", "kernel", "algorithm", "algorithms", "main"],
      parsers: ["parser", "parsers", "parse", "lexer", "tokenizer", "ast", "compiler", "interpreter", "deserializer", "serializer"],
      plugins: ["plugin", "plugins", "addon", "addons", "extension", "extensions", "adapter", "adapters"],
      test: ["test", "tests", "spec", "specs", "unit", "e2e", "integration-test", "mock", "mocks", "stub", "spy", "expect", "assert", "jest", "vitest", "mocha", "chai"],
      config: ["config", "configs", "configuration", "env", "dotenv", "package.json", "tsconfig", "manifest", "setting", "settings", "options"],
      docs: ["doc", "docs", "documentation", "readme", "changelog", "guide", "tutorial", "api-doc"],
      shared: ["shared", "util", "utils", "utility", "utilities", "helper", "helpers", "common", "lib", "library"]
    };
    const targetRoles = /* @__PURE__ */ new Set();
    for (const kw of keywords) {
      const kwLower = kw.toLowerCase();
      for (const [role, kwList] of Object.entries(roleKeywordMap)) {
        if (kwList.some((k) => kwLower.includes(k) || k.includes(kwLower))) {
          targetRoles.add(role);
        }
      }
    }
    const candidateScores = candidates.map((path) => {
      const depth = distances.get(path);
      const dbFile = this.store.getFile(path);
      const language = dbFile ? dbFile.language : "unknown";
      let score = 0;
      if (depth === 0) {
        score += 1e3;
      } else if (depth === 1) {
        score += 100;
      } else if (depth === 2) {
        score += 10;
      } else {
        score += 1;
      }
      const pathLower = path.toLowerCase();
      let keywordPathMatches = 0;
      for (const kw of keywords) {
        if (pathLower.includes(kw.toLowerCase())) {
          keywordPathMatches++;
        }
      }
      score += keywordPathMatches * 200;
      const fileSyms = this.store.getSymbolsForFile(path);
      let keywordSymbolMatches = 0;
      for (const sym of fileSyms) {
        const symNameLower = sym.name.toLowerCase();
        for (const kw of keywords) {
          if (symNameLower.includes(kw.toLowerCase())) {
            keywordSymbolMatches++;
          }
        }
      }
      score += keywordSymbolMatches * 50;
      const pr = rankedMap.get(path) || 0;
      score += pr * 20;
      const fileCluster = candidateClustersMap.get(path);
      if (fileCluster && seedClusters.has(fileCluster)) {
        score += 150;
      }
      const role = dbFile ? dbFile.role : "other";
      if (targetRoles.has(role)) {
        score += 250;
      }
      return {
        path,
        language,
        score,
        depth
      };
    });
    candidateScores.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));
    const includedFiles = [];
    const excludedFiles = [];
    let currentTokens = 0;
    for (const cand of candidateScores) {
      const dbFile = this.store.getFile(cand.path);
      if (!dbFile) continue;
      const syms = this.store.getSymbolsForFile(cand.path);
      const symbolCount = syms.length;
      let fileTokens = 150;
      if (symbolCount > 3) {
        fileTokens += (symbolCount - 3) * 20;
      }
      if (currentTokens + fileTokens <= budget) {
        includedFiles.push({
          path: cand.path,
          language: cand.language,
          lineCount: dbFile.lines || 0,
          sizeBytes: dbFile.size_bytes || 0,
          symbols: syms.map((s) => ({
            name: s.name,
            kind: s.kind,
            scope: s.scope,
            startLine: s.start_line,
            endLine: s.end_line
          }))
        });
        currentTokens += fileTokens;
      } else {
        excludedFiles.push(cand.path);
      }
    }
    const includedPaths = new Set(includedFiles.map((f) => f.path));
    const edges = [];
    for (const path of includedPaths) {
      const fileEdges = this.store.getEdgesForFile(path);
      for (const edge of fileEdges) {
        if (includedPaths.has(edge.target_file)) {
          edges.push({
            sourceFile: edge.source_file,
            targetFile: edge.target_file,
            sourceSymbol: edge.source_symbol,
            targetSymbol: edge.target_symbol,
            edgeType: edge.edge_type
          });
        }
      }
    }
    const simpleFiles = includedFiles.map((f) => ({
      path: f.path,
      language: f.language,
      lineCount: f.lineCount,
      sizeBytes: f.sizeBytes
    }));
    return {
      includedFiles,
      excludedFiles,
      edges,
      estimatedTokens: currentTokens,
      matchedSymbols,
      files: simpleFiles,
      symbols: matchedSymbols
    };
  }
}
export {
  ContextBuilder
};
