import { performance } from "perf_hooks";
import { logger } from "../monitoring/logger.js";
import * as fs from "fs";
import * as path from "path";
import { v4 as uuidv4 } from "uuid";
function getEnv(key, defaultValue) {
  const value = process.env[key];
  if (value === void 0) {
    if (defaultValue !== void 0) return defaultValue;
    throw new Error(`Environment variable ${key} is required`);
  }
  return value;
}
function getOptionalEnv(key) {
  return process.env[key];
}
class TraceContext {
  static instance;
  config;
  currentTrace = null;
  traceStack = [];
  allTraces = [];
  outputFile;
  startTime = Date.now();
  sensitivePatterns = [
    /api[_-]?key/i,
    /token/i,
    /secret/i,
    /password/i,
    /bearer/i,
    /authorization/i,
    /client[_-]?id/i,
    /client[_-]?secret/i
  ];
  constructor() {
    this.config = this.loadConfig();
    if (this.config.output === "file" || this.config.output === "both") {
      this.initializeOutputFile();
    }
  }
  static getInstance() {
    if (!TraceContext.instance) {
      TraceContext.instance = new TraceContext();
    }
    return TraceContext.instance;
  }
  loadConfig() {
    return {
      enabled: process.env["DEBUG_TRACE"] === "true" || process.env["STACKMEMORY_DEBUG"] === "true",
      verbosity: process.env["TRACE_VERBOSITY"] || "full",
      output: process.env["TRACE_OUTPUT"] || "console",
      includeParams: process.env["TRACE_PARAMS"] !== "false",
      includeResults: process.env["TRACE_RESULTS"] !== "false",
      maskSensitive: process.env["TRACE_MASK_SENSITIVE"] !== "false",
      performanceThreshold: parseInt(
        process.env["TRACE_PERF_THRESHOLD"] || "100"
      ),
      maxDepth: parseInt(process.env["TRACE_MAX_DEPTH"] || "20"),
      captureMemory: process.env["TRACE_MEMORY"] === "true"
    };
  }
  initializeOutputFile() {
    const traceDir = path.join(
      process.env["HOME"] || ".",
      ".stackmemory",
      "traces"
    );
    if (!fs.existsSync(traceDir)) {
      fs.mkdirSync(traceDir, { recursive: true });
    }
    const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
    this.outputFile = path.join(traceDir, `trace-${timestamp}.jsonl`);
  }
  maskSensitiveData(obj) {
    if (!this.config.maskSensitive) return obj;
    if (typeof obj !== "object" || obj === null) return obj;
    const masked = Array.isArray(obj) ? [...obj] : { ...obj };
    for (const key in masked) {
      if (typeof key === "string") {
        const isSensitive = this.sensitivePatterns.some(
          (pattern) => pattern.test(key)
        );
        if (isSensitive) {
          masked[key] = "[MASKED]";
        } else if (typeof masked[key] === "object") {
          masked[key] = this.maskSensitiveData(masked[key]);
        } else if (typeof masked[key] === "string" && masked[key].length > 20) {
          if (/^[a-zA-Z0-9_-]{20,}$/.test(masked[key])) {
            masked[key] = masked[key].substring(0, 8) + "...[MASKED]";
          }
        }
      }
    }
    return masked;
  }
  captureMemory() {
    if (!this.config.captureMemory) return void 0;
    return process.memoryUsage();
  }
  formatDuration(ms) {
    if (ms < 1e3) return `${ms.toFixed(0)}ms`;
    if (ms < 6e4) return `${(ms / 1e3).toFixed(2)}s`;
    return `${(ms / 6e4).toFixed(2)}m`;
  }
  formatMemory(bytes) {
    const mb = bytes / 1024 / 1024;
    return `${mb.toFixed(2)}MB`;
  }
  getIndent(depth) {
    return "  ".repeat(depth);
  }
  formatTraceEntry(entry, includeChildren = true) {
    const indent = this.getIndent(entry.depth);
    const duration = entry.duration ? ` [${this.formatDuration(entry.duration)}]` : "";
    const memory = entry.memory?.delta ? ` (\u0394mem: ${this.formatMemory(entry.memory.delta.heapUsed)})` : "";
    let output = `${indent}\u2192 [${entry.type.toUpperCase()}:${entry.id.substring(0, 8)}] ${entry.name}${duration}${memory}`;
    if (entry.error) {
      output += `
${indent}  \u2717 ERROR: ${entry.error.message || entry.error}`;
      if (entry.error.stack && this.config.verbosity === "full") {
        output += `
${indent}    Stack: ${entry.error.stack.split("\n")[1]?.trim()}`;
      }
    }
    if (this.config.includeParams && entry.params && Object.keys(entry.params).length > 0) {
      const maskedParams = this.maskSensitiveData(entry.params);
      output += `
${indent}  \u25B8 Params: ${JSON.stringify(maskedParams, null, 2).replace(/\n/g, "\n" + indent + "    ")}`;
    }
    if (this.config.includeResults && entry.result !== void 0 && !entry.error) {
      const maskedResult = this.maskSensitiveData(entry.result);
      const resultStr = JSON.stringify(maskedResult, null, 2);
      if (resultStr.length < 200) {
        output += `
${indent}  \u25C2 Result: ${resultStr.replace(/\n/g, "\n" + indent + "    ")}`;
      } else {
        output += `
${indent}  \u25C2 Result: [${typeof maskedResult}] ${resultStr.substring(0, 100)}...`;
      }
    }
    if (entry.duration && entry.duration > this.config.performanceThreshold) {
      output += `
${indent}  WARNING: Exceeded ${this.config.performanceThreshold}ms threshold`;
    }
    if (includeChildren && entry.children.length > 0) {
      for (const child of entry.children) {
        output += "\n" + this.formatTraceEntry(child, true);
      }
    }
    if (entry.endTime && entry.depth > 0) {
      output += `
${indent}\u2190 [${entry.type.toUpperCase()}:${entry.id.substring(0, 8)}] completed`;
    }
    return output;
  }
  outputTrace(entry) {
    if (!this.config.enabled) return;
    const formatted = this.formatTraceEntry(entry, false);
    if (this.config.output === "console" || this.config.output === "both") {
      console.log(formatted);
    }
    if ((this.config.output === "file" || this.config.output === "both") && this.outputFile) {
      const jsonLine = JSON.stringify({
        ...entry,
        formatted,
        timestamp: (/* @__PURE__ */ new Date()).toISOString()
      }) + "\n";
      fs.appendFileSync(this.outputFile, jsonLine);
    }
  }
  startTrace(type, name, params, metadata) {
    if (!this.config.enabled) return "";
    const id = uuidv4();
    const parentId = this.currentTrace?.id;
    const depth = this.traceStack.length;
    if (depth > this.config.maxDepth) {
      return id;
    }
    const entry = {
      id,
      parentId,
      type,
      name,
      startTime: performance.now(),
      depth,
      params: this.config.includeParams ? params : void 0,
      metadata,
      children: [],
      memory: this.captureMemory() ? { before: this.captureMemory() } : void 0
    };
    if (this.currentTrace) {
      this.currentTrace.children.push(entry);
    } else {
      this.allTraces.push(entry);
    }
    this.traceStack.push(entry);
    this.currentTrace = entry;
    this.outputTrace(entry);
    return id;
  }
  endTrace(id, result, error) {
    if (!this.config.enabled) return;
    const index = this.traceStack.findIndex((t) => t.id === id);
    if (index === -1) return;
    const entry = this.traceStack[index];
    entry.endTime = performance.now();
    entry.duration = entry.endTime - entry.startTime;
    entry.result = this.config.includeResults && !error ? result : void 0;
    entry.error = error;
    if (entry.memory?.before) {
      entry.memory.after = this.captureMemory();
      if (entry.memory.after) {
        entry.memory.delta = {
          rss: entry.memory.after.rss - entry.memory.before.rss,
          heapUsed: entry.memory.after.heapUsed - entry.memory.before.heapUsed
        };
      }
    }
    this.outputTrace(entry);
    this.traceStack.splice(index);
    this.currentTrace = this.traceStack[this.traceStack.length - 1] || null;
  }
  async traceAsync(type, name, params, fn) {
    const id = this.startTrace(type, name, params);
    try {
      const result = await fn();
      this.endTrace(id, result);
      return result;
    } catch (error) {
      this.endTrace(id, void 0, error);
      throw error;
    }
  }
  traceSync(type, name, params, fn) {
    const id = this.startTrace(type, name, params);
    try {
      const result = fn();
      this.endTrace(id, result);
      return result;
    } catch (error) {
      this.endTrace(id, void 0, error);
      throw error;
    }
  }
  async command(name, options, fn) {
    return this.traceAsync("command", name, options, fn);
  }
  async step(name, fn) {
    return this.traceAsync("step", name, void 0, fn);
  }
  async query(sql, params, fn) {
    return this.traceAsync("query", sql.substring(0, 50), params, fn);
  }
  async api(method, url, body, fn) {
    return this.traceAsync("api", `${method} ${url}`, { body }, fn);
  }
  getExecutionSummary() {
    if (!this.config.enabled) return "Tracing disabled";
    const totalDuration = Date.now() - this.startTime;
    const errorCount = this.countErrors(this.allTraces);
    const slowCount = this.countSlowOperations(this.allTraces);
    let summary = `
${"=".repeat(80)}
`;
    summary += `EXECUTION SUMMARY
`;
    summary += `${"=".repeat(80)}
`;
    summary += `Total Duration: ${this.formatDuration(totalDuration)}
`;
    summary += `Total Operations: ${this.countOperations(this.allTraces)}
`;
    summary += `Errors: ${errorCount}
`;
    summary += `Slow Operations (>${this.config.performanceThreshold}ms): ${slowCount}
`;
    if (this.config.captureMemory) {
      const memUsage = process.memoryUsage();
      summary += `Final Memory: RSS=${this.formatMemory(memUsage.rss)}, Heap=${this.formatMemory(memUsage.heapUsed)}
`;
    }
    if (this.outputFile) {
      summary += `Trace Log: ${this.outputFile}
`;
    }
    summary += `${"=".repeat(80)}`;
    return summary;
  }
  countOperations(traces) {
    let count = traces.length;
    for (const trace2 of traces) {
      count += this.countOperations(trace2.children);
    }
    return count;
  }
  countErrors(traces) {
    let count = 0;
    for (const trace2 of traces) {
      if (trace2.error) count++;
      count += this.countErrors(trace2.children);
    }
    return count;
  }
  countSlowOperations(traces) {
    let count = 0;
    for (const trace2 of traces) {
      if (trace2.duration && trace2.duration > this.config.performanceThreshold)
        count++;
      count += this.countSlowOperations(trace2.children);
    }
    return count;
  }
  getLastError() {
    const findLastError = (traces) => {
      for (let i = traces.length - 1; i >= 0; i--) {
        const trace2 = traces[i];
        if (trace2.error) return trace2;
        const childError = findLastError(trace2.children);
        if (childError) return childError;
      }
      return null;
    };
    return findLastError(this.allTraces);
  }
  exportTraces() {
    return this.allTraces;
  }
  reset() {
    this.currentTrace = null;
    this.traceStack = [];
    this.allTraces = [];
    this.startTime = Date.now();
  }
}
const trace = TraceContext.getInstance();
function Trace(type = "function") {
  return function(target, propertyKey, descriptor) {
    const originalMethod = descriptor.value;
    const isAsync = originalMethod.constructor.name === "AsyncFunction";
    if (isAsync) {
      descriptor.value = async function(...args) {
        const className = target.constructor.name;
        const methodName = `${className}.${propertyKey}`;
        return trace.traceAsync(type, methodName, args, async () => {
          return originalMethod.apply(this, args);
        });
      };
    } else {
      descriptor.value = function(...args) {
        const className = target.constructor.name;
        const methodName = `${className}.${propertyKey}`;
        return trace.traceSync(type, methodName, args, () => {
          return originalMethod.apply(this, args);
        });
      };
    }
    return descriptor;
  };
}
function TraceClass(type = "function") {
  return function(constructor) {
    const prototype = constructor.prototype;
    const propertyNames = Object.getOwnPropertyNames(prototype);
    for (const propertyName of propertyNames) {
      if (propertyName === "constructor") continue;
      const descriptor = Object.getOwnPropertyDescriptor(
        prototype,
        propertyName
      );
      if (!descriptor || typeof descriptor.value !== "function") continue;
      Trace(type)(prototype, propertyName, descriptor);
      Object.defineProperty(prototype, propertyName, descriptor);
    }
    return constructor;
  };
}
function TraceCritical(target, propertyKey, descriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = async function(...args) {
    const className = target.constructor.name;
    const methodName = `${className}.${propertyKey} [CRITICAL]`;
    const contextBefore = {
      memory: process.memoryUsage(),
      timestamp: (/* @__PURE__ */ new Date()).toISOString(),
      args: trace["maskSensitiveData"](args)
    };
    try {
      return await trace.traceAsync(
        "function",
        methodName,
        contextBefore,
        async () => {
          return originalMethod.apply(this, args);
        }
      );
    } catch (error) {
      logger.error(`Critical operation failed: ${methodName}`, error, {
        context: contextBefore,
        stack: error.stack
      });
      throw error;
    }
  };
  return descriptor;
}
export {
  Trace,
  TraceClass,
  TraceContext,
  TraceCritical,
  trace
};
//# sourceMappingURL=debug-trace.js.map
