import { v4 as uuidv4 } from "uuid";
import {
  TraceType,
  DEFAULT_TRACE_CONFIG,
  TRACE_PATTERNS,
  CompressionStrategy
} from "./types.js";
import { ConfigManager } from "../config/config-manager.js";
import { TraceStore } from "./trace-store.js";
class TraceDetector {
  config;
  activeTrace = [];
  lastToolTime = 0;
  traces = [];
  configManager;
  traceStore;
  constructor(config = {}, configManager, db) {
    this.config = { ...DEFAULT_TRACE_CONFIG, ...config };
    this.configManager = configManager || new ConfigManager();
    if (db) {
      this.traceStore = new TraceStore(db);
      this.loadTracesFromStore();
    }
  }
  /**
   * Load traces from the database
   */
  loadTracesFromStore() {
    if (!this.traceStore) return;
    try {
      const recentTraces = this.traceStore.getAllTraces();
      const cutoff = Date.now() - 24 * 60 * 60 * 1e3;
      this.traces = recentTraces.filter((t) => t.metadata.startTime >= cutoff);
    } catch (error) {
      console.error("Failed to load traces from store:", error);
      this.traces = [];
    }
  }
  /**
   * Add a tool call and check if it belongs to current trace
   */
  addToolCall(tool) {
    const now = Date.now();
    if (this.shouldStartNewTrace(tool)) {
      if (this.activeTrace.length > 0) {
        this.finalizeTrace();
      }
      this.activeTrace = [tool];
    } else {
      this.activeTrace.push(tool);
    }
    this.lastToolTime = tool.timestamp;
    if (this.activeTrace.length >= this.config.maxTraceSize) {
      this.finalizeTrace();
    }
  }
  /**
   * Determine if a tool call should start a new trace
   */
  shouldStartNewTrace(tool) {
    if (this.activeTrace.length === 0) {
      return false;
    }
    const lastTool = this.activeTrace[this.activeTrace.length - 1];
    const timeDiff = tool.timestamp - lastTool.timestamp;
    if (timeDiff > this.config.timeProximityMs) {
      return true;
    }
    if (this.config.sameDirThreshold) {
      const lastFiles = lastTool.filesAffected || [];
      const currentFiles = tool.filesAffected || [];
      if (lastFiles.length > 0 && currentFiles.length > 0) {
        const lastDirs = lastFiles.map((f) => this.getDirectory(f));
        const currentDirs = currentFiles.map((f) => this.getDirectory(f));
        const hasCommonDir = lastDirs.some((d) => currentDirs.includes(d));
        if (!hasCommonDir) {
          return true;
        }
      }
    }
    if (this.config.causalRelationship) {
      if (lastTool.error && !this.isFixAttempt(tool, lastTool)) {
        return true;
      }
    }
    return false;
  }
  /**
   * Check if a tool is attempting to fix an error from previous tool
   */
  isFixAttempt(current, previous) {
    if (previous.error && (current.tool === "edit" || current.tool === "write")) {
      return true;
    }
    if (current.tool === "test" || current.tool === "bash") {
      return true;
    }
    return false;
  }
  /**
   * Finalize current trace and add to traces list
   */
  finalizeTrace() {
    if (this.activeTrace.length === 0) return;
    const trace = this.createTrace(this.activeTrace);
    this.traces.push(trace);
    if (this.traceStore) {
      try {
        this.traceStore.saveTrace(trace);
      } catch (error) {
        console.error("Failed to persist trace:", error);
      }
    }
    this.activeTrace = [];
  }
  /**
   * Create a trace from a sequence of tool calls
   */
  createTrace(tools) {
    const id = uuidv4();
    const type = this.detectTraceType(tools);
    const metadata = this.extractMetadata(tools);
    const score = this.calculateTraceScore(tools, metadata);
    const summary = this.generateSummary(tools, type, metadata);
    const trace = {
      id,
      type,
      tools,
      score,
      summary,
      metadata
    };
    const ageHours = (Date.now() - metadata.startTime) / (1e3 * 60 * 60);
    if (ageHours > this.config.compressionThreshold) {
      trace.compressed = this.compressTrace(trace);
    }
    return trace;
  }
  /**
   * Detect the type of trace based on tool patterns
   */
  detectTraceType(tools) {
    const toolSequence = tools.map((t) => t.tool);
    for (const pattern of TRACE_PATTERNS) {
      if (this.matchesPattern(toolSequence, pattern.pattern)) {
        return pattern.type;
      }
    }
    if (toolSequence.includes("search") || toolSequence.includes("grep")) {
      if (toolSequence.includes("edit")) {
        return TraceType.SEARCH_DRIVEN;
      }
      return TraceType.EXPLORATION;
    }
    if (tools.some((t) => t.error)) {
      return TraceType.ERROR_RECOVERY;
    }
    if (toolSequence.includes("test")) {
      return TraceType.TESTING;
    }
    if (toolSequence.includes("write")) {
      return TraceType.FEATURE_IMPLEMENTATION;
    }
    return TraceType.UNKNOWN;
  }
  /**
   * Check if tool sequence matches a pattern
   */
  matchesPattern(sequence, pattern) {
    if (pattern instanceof RegExp) {
      return pattern.test(sequence.join("\u2192"));
    }
    if (Array.isArray(pattern)) {
      let patternIndex = 0;
      for (const tool of sequence) {
        if (tool === pattern[patternIndex]) {
          patternIndex++;
          if (patternIndex >= pattern.length) {
            return true;
          }
        }
      }
    }
    return false;
  }
  /**
   * Extract metadata from tool calls
   */
  extractMetadata(tools) {
    const startTime = tools[0].timestamp;
    const endTime = tools[tools.length - 1].timestamp;
    const filesModified = /* @__PURE__ */ new Set();
    const errorsEncountered = [];
    const decisionsRecorded = [];
    let hasCausalChain = false;
    for (let i = 0; i < tools.length; i++) {
      const tool = tools[i];
      if (tool.filesAffected) {
        tool.filesAffected.forEach((f) => filesModified.add(f));
      }
      if (tool.error) {
        errorsEncountered.push(tool.error);
        if (i < tools.length - 1) {
          const nextTool = tools[i + 1];
          if (this.isFixAttempt(nextTool, tool)) {
            hasCausalChain = true;
          }
        }
      }
      if (tool.tool === "decision_recording" && tool.arguments?.decision) {
        decisionsRecorded.push(tool.arguments.decision);
      }
    }
    return {
      startTime,
      endTime,
      filesModified: Array.from(filesModified),
      errorsEncountered,
      decisionsRecorded,
      causalChain: hasCausalChain
    };
  }
  /**
   * Calculate importance score for a trace
   */
  calculateTraceScore(tools, metadata) {
    const toolScores = tools.map(
      (t) => this.configManager.calculateScore(t.tool, {
        filesAffected: t.filesAffected?.length || 0,
        isPermanent: this.isPermanentChange(t),
        referenceCount: 0
        // Would need to track references
      })
    );
    const maxScore = Math.max(...toolScores);
    let score = maxScore;
    if (metadata.causalChain) {
      score = Math.min(score + 0.1, 1);
    }
    if (metadata.decisionsRecorded.length > 0) {
      score = Math.min(score + 0.05 * metadata.decisionsRecorded.length, 1);
    }
    if (metadata.errorsEncountered.length > 0 && !metadata.causalChain) {
      score = Math.max(score - 0.1, 0);
    }
    return score;
  }
  /**
   * Check if a tool call represents a permanent change
   */
  isPermanentChange(tool) {
    const permanentTools = ["write", "edit", "decision_recording"];
    return permanentTools.includes(tool.tool);
  }
  /**
   * Generate a summary for the trace
   */
  generateSummary(tools, type, metadata) {
    const toolChain = tools.map((t) => t.tool).join("\u2192");
    switch (type) {
      case TraceType.SEARCH_DRIVEN:
        return `Search-driven modification: ${toolChain}`;
      case TraceType.ERROR_RECOVERY:
        const error = metadata.errorsEncountered[0] || "unknown error";
        return `Error recovery: ${error} via ${toolChain}`;
      case TraceType.FEATURE_IMPLEMENTATION:
        const files = metadata.filesModified.length;
        return `Feature implementation: ${files} files via ${toolChain}`;
      case TraceType.REFACTORING:
        return `Code refactoring: ${toolChain}`;
      case TraceType.TESTING:
        return `Test execution: ${toolChain}`;
      case TraceType.EXPLORATION:
        return `Codebase exploration: ${toolChain}`;
      case TraceType.DEBUGGING:
        return `Debugging session: ${toolChain}`;
      case TraceType.BUILD_DEPLOY:
        return `Build and deploy: ${toolChain}`;
      default:
        return `Tool sequence: ${toolChain}`;
    }
  }
  /**
   * Compress a trace for long-term storage using strategy
   */
  compressTrace(trace, strategy = CompressionStrategy.PATTERN_BASED) {
    switch (strategy) {
      case CompressionStrategy.SUMMARY_ONLY:
        return this.compressSummaryOnly(trace);
      case CompressionStrategy.PATTERN_BASED:
        return this.compressPatternBased(trace);
      case CompressionStrategy.SELECTIVE:
        return this.compressSelective(trace);
      case CompressionStrategy.FULL_COMPRESSION:
        return this.compressMaximal(trace);
      default:
        return this.compressPatternBased(trace);
    }
  }
  /**
   * Summary-only compression - minimal data retention
   */
  compressSummaryOnly(trace) {
    return {
      pattern: "",
      // No pattern stored
      summary: trace.summary.substring(0, 100),
      // Limit summary
      score: trace.score,
      toolCount: trace.tools.length,
      duration: trace.metadata.endTime - trace.metadata.startTime,
      timestamp: trace.metadata.startTime
    };
  }
  /**
   * Pattern-based compression - keep tool sequence
   */
  compressPatternBased(trace) {
    const pattern = trace.tools.map((t) => t.tool).join("\u2192");
    const duration = trace.metadata.endTime - trace.metadata.startTime;
    return {
      pattern,
      summary: trace.summary,
      score: trace.score,
      toolCount: trace.tools.length,
      duration,
      timestamp: trace.metadata.startTime
    };
  }
  /**
   * Selective compression - keep high-score tools only
   */
  compressSelective(trace, threshold = 0.5) {
    const significantTools = trace.tools.filter((tool) => {
      const score = this.configManager.calculateScore(tool.tool, {
        filesAffected: tool.filesAffected?.length || 0,
        isPermanent: this.isPermanentChange(tool),
        referenceCount: 0
      });
      return score >= threshold;
    });
    const pattern = significantTools.length > 0 ? significantTools.map((t) => t.tool).join("\u2192") : trace.tools.map((t) => t.tool).join("\u2192");
    return {
      pattern,
      summary: `${trace.summary} [${significantTools.length}/${trace.tools.length} significant]`,
      score: trace.score,
      toolCount: significantTools.length,
      duration: trace.metadata.endTime - trace.metadata.startTime,
      timestamp: trace.metadata.startTime
    };
  }
  /**
   * Maximal compression - absolute minimum data
   */
  compressMaximal(trace) {
    const typeAbbrev = this.getTraceTypeAbbreviation(trace.type);
    const pattern = `${typeAbbrev}:${trace.tools.length}`;
    return {
      pattern,
      summary: trace.type,
      // Just the type
      score: Math.round(trace.score * 10) / 10,
      // Round to 1 decimal
      toolCount: trace.tools.length,
      duration: Math.round((trace.metadata.endTime - trace.metadata.startTime) / 1e3) * 1e3,
      // Round to seconds
      timestamp: trace.metadata.startTime
    };
  }
  /**
   * Get abbreviated trace type
   */
  getTraceTypeAbbreviation(type) {
    const abbreviations = {
      [TraceType.SEARCH_DRIVEN]: "SD",
      [TraceType.ERROR_RECOVERY]: "ER",
      [TraceType.FEATURE_IMPLEMENTATION]: "FI",
      [TraceType.REFACTORING]: "RF",
      [TraceType.TESTING]: "TS",
      [TraceType.EXPLORATION]: "EX",
      [TraceType.DEBUGGING]: "DB",
      [TraceType.DOCUMENTATION]: "DC",
      [TraceType.BUILD_DEPLOY]: "BD",
      [TraceType.UNKNOWN]: "UN"
    };
    return abbreviations[type] || "UN";
  }
  /**
   * Choose compression strategy based on trace age and importance
   */
  selectCompressionStrategy(trace) {
    const ageHours = (Date.now() - trace.metadata.startTime) / (1e3 * 60 * 60);
    const score = trace.score;
    if (ageHours < 24 && score > 0.7) {
      return CompressionStrategy.PATTERN_BASED;
    }
    if (ageHours < 24) {
      return CompressionStrategy.SELECTIVE;
    }
    if (ageHours < 168 && score > 0.5) {
      return CompressionStrategy.SELECTIVE;
    }
    if (ageHours < 720) {
      return CompressionStrategy.SUMMARY_ONLY;
    }
    return CompressionStrategy.FULL_COMPRESSION;
  }
  /**
   * Get directory from file path
   */
  getDirectory(filePath) {
    const parts = filePath.split("/");
    parts.pop();
    return parts.join("/");
  }
  /**
   * Flush any pending trace
   */
  flush() {
    if (this.activeTrace.length > 0) {
      this.finalizeTrace();
    }
  }
  /**
   * Get all detected traces
   */
  getTraces() {
    return this.traces;
  }
  /**
   * Get traces by type
   */
  getTracesByType(type) {
    return this.traces.filter((t) => t.type === type);
  }
  /**
   * Get high-importance traces
   */
  getHighImportanceTraces(threshold = 0.7) {
    return this.traces.filter((t) => t.score >= threshold);
  }
  /**
   * Compress old traces with intelligent strategy selection
   */
  compressOldTraces(ageHours = 24) {
    let compressed = 0;
    const now = Date.now();
    for (const trace of this.traces) {
      const age = (now - trace.metadata.startTime) / (1e3 * 60 * 60);
      if (age > ageHours && !trace.compressed) {
        const strategy = this.selectCompressionStrategy(trace);
        trace.compressed = this.compressTrace(trace, strategy);
        if (strategy === CompressionStrategy.FULL_COMPRESSION || strategy === CompressionStrategy.SUMMARY_ONLY) {
          trace.tools = [];
        } else if (strategy === CompressionStrategy.SELECTIVE) {
          trace.tools = trace.tools.filter((tool) => {
            const score = this.configManager.calculateScore(tool.tool, {
              filesAffected: tool.filesAffected?.length || 0,
              isPermanent: this.isPermanentChange(tool),
              referenceCount: 0
            });
            return score >= 0.5;
          });
        }
        compressed++;
        if (this.traceStore) {
          try {
            this.traceStore.updateCompression(
              trace.id,
              trace.compressed,
              strategy
            );
          } catch (error) {
            console.error(
              "Failed to update trace compression in store:",
              error
            );
          }
        }
      }
    }
    return compressed;
  }
  /**
   * Export traces for analysis
   */
  exportTraces() {
    return JSON.stringify(this.traces, null, 2);
  }
  /**
   * Get statistics about traces
   */
  getStatistics() {
    const stats = {
      totalTraces: this.traces.length,
      tracesByType: {},
      averageScore: 0,
      averageLength: 0,
      compressedCount: 0,
      highImportanceCount: 0
    };
    if (this.traces.length === 0) return stats;
    let totalScore = 0;
    let totalLength = 0;
    for (const trace of this.traces) {
      stats.tracesByType[trace.type] = (stats.tracesByType[trace.type] || 0) + 1;
      totalScore += trace.score;
      totalLength += trace.tools.length;
      if (trace.compressed) {
        stats.compressedCount++;
      }
      if (trace.score >= 0.7) {
        stats.highImportanceCount++;
      }
    }
    stats.averageScore = totalScore / this.traces.length;
    stats.averageLength = totalLength / this.traces.length;
    return stats;
  }
}
export {
  TraceDetector
};
//# sourceMappingURL=trace-detector.js.map
