// ../metrics/dist/dora/calculate-dora-metrics.js
import { quantile } from "simple-statistics";

// ../metrics/dist/dora/constants.js
var DEPLOYMENT_FREQUENCY = {
  /** Elite: >5 deploys per week */
  ELITE: 5,
  /** High: 1-5 deploys per week */
  HIGH: 1,
  /** Medium: ~1 deploy per month (0.25/week) */
  MEDIUM: 0.25
  // Low: <0.25 deploys per week (implicit)
};
var LEAD_TIME_HOURS = {
  /** Elite: <24 hours (<1 day) */
  ELITE: 24,
  /** High: <168 hours (<7 days) */
  HIGH: 168,
  /** Medium: <720 hours (<30 days) */
  MEDIUM: 720
  // Low: >720 hours (implicit)
};
var CFR_PERCENT = {
  /** Elite: <15% failures */
  ELITE: 15,
  /** High: 15-30% failures */
  HIGH: 30,
  /** Medium: 30-45% failures */
  MEDIUM: 45
  // Low: >45% failures (implicit)
};
var MTTR_HOURS = {
  /** Elite: <1 hour */
  ELITE: 1,
  /** High: <24 hours (<1 day) */
  HIGH: 24,
  /** Medium: <168 hours (<7 days) */
  MEDIUM: 168
  // Low: >168 hours (implicit)
};
var STATISTICS = {
  /** 90th percentile for p90Hours calculation */
  P90_PERCENTILE: 0.9,
  /** Decimal places for rounding (10 = 1 decimal place) */
  ROUNDING_FACTOR: 10,
  /** 95th percentile for p95 calculations */
  P95_PERCENTILE: 0.95,
  /** 99th percentile for p99 calculations */
  P99_PERCENTILE: 0.99,
  /** Median percentile */
  MEDIAN_PERCENTILE: 0.5,
  /** Milliseconds per hour */
  MS_PER_HOUR: 36e5,
  /** Percentage multiplier */
  PERCENTAGE_MULTIPLIER: 100
};

// ../metrics/dist/dora/calculate-dora-metrics.js
function roundToOneDecimal(value) {
  return Math.round(value * STATISTICS.ROUNDING_FACTOR) / STATISTICS.ROUNDING_FACTOR;
}
function classifyDeploymentFrequency(deploysPerWeek) {
  if (deploysPerWeek > DEPLOYMENT_FREQUENCY.ELITE)
    return "elite";
  if (deploysPerWeek >= DEPLOYMENT_FREQUENCY.HIGH)
    return "high";
  if (deploysPerWeek >= DEPLOYMENT_FREQUENCY.MEDIUM)
    return "medium";
  return "low";
}
function classifyLeadTime(averageHours) {
  if (averageHours < LEAD_TIME_HOURS.ELITE)
    return "elite";
  if (averageHours < LEAD_TIME_HOURS.HIGH)
    return "high";
  if (averageHours < LEAD_TIME_HOURS.MEDIUM)
    return "medium";
  return "low";
}
function classifyCFR(failurePercentage) {
  if (failurePercentage < CFR_PERCENT.ELITE)
    return "elite";
  if (failurePercentage < CFR_PERCENT.HIGH)
    return "high";
  if (failurePercentage < CFR_PERCENT.MEDIUM)
    return "medium";
  return "low";
}
function classifyMTTR(averageHours) {
  if (averageHours === 0 || averageHours < MTTR_HOURS.ELITE)
    return "elite";
  if (averageHours < MTTR_HOURS.HIGH)
    return "high";
  if (averageHours < MTTR_HOURS.MEDIUM)
    return "medium";
  return "low";
}
function calculateDeploymentFrequency(commits, weekStart, weekEnd) {
  const weekCommits = commits.filter((c) => c.timestamp >= weekStart && c.timestamp <= weekEnd);
  const deploysPerWeek = weekCommits.length;
  const status = classifyDeploymentFrequency(deploysPerWeek);
  return { deploysPerWeek, status };
}
function calculateLeadTime(wuMetrics) {
  const cycleTimes = wuMetrics.map((wu) => wu.cycleTimeHours).filter((t) => typeof t === "number");
  if (cycleTimes.length === 0) {
    return {
      averageHours: 0,
      medianHours: 0,
      p90Hours: 0,
      status: "low"
    };
  }
  const averageHours = cycleTimes.reduce((sum, t) => sum + t, 0) / cycleTimes.length;
  const medianHours = quantile(cycleTimes, STATISTICS.MEDIAN_PERCENTILE);
  const p90Hours = quantile(cycleTimes, STATISTICS.P90_PERCENTILE);
  const status = classifyLeadTime(averageHours);
  return {
    averageHours: roundToOneDecimal(averageHours),
    medianHours: roundToOneDecimal(medianHours),
    p90Hours: roundToOneDecimal(p90Hours),
    status
  };
}
function calculateCFR(commits, skipGatesEntries) {
  const totalDeployments = commits.length;
  const failures = skipGatesEntries.length;
  const failurePercentage = totalDeployments > 0 ? failures / totalDeployments * STATISTICS.PERCENTAGE_MULTIPLIER : 0;
  const status = classifyCFR(failurePercentage);
  return {
    failurePercentage: roundToOneDecimal(failurePercentage),
    totalDeployments,
    failures,
    status
  };
}
function identifyEmergencyFixes(commits) {
  return commits.filter((c) => c.message.includes("EMERGENCY") || /fix\(EMERGENCY\)/.test(c.message));
}
function calculateRecoveryTime(emergencyFixes) {
  let totalRecoveryTimeMs = 0;
  let pairCount = 0;
  for (let i = 0; i < emergencyFixes.length - 1; i += 2) {
    const startFix = emergencyFixes.at(i);
    const endFix = emergencyFixes.at(i + 1);
    if (startFix && endFix) {
      totalRecoveryTimeMs += endFix.timestamp.getTime() - startFix.timestamp.getTime();
      pairCount++;
    }
  }
  if (pairCount === 0)
    return 0;
  return totalRecoveryTimeMs / (STATISTICS.MS_PER_HOUR * pairCount);
}
function calculateMTTR(commits) {
  const emergencyFixes = identifyEmergencyFixes(commits);
  if (emergencyFixes.length === 0) {
    return {
      averageHours: 0,
      incidents: 0,
      status: "elite"
    };
  }
  const incidents = Math.floor(emergencyFixes.length / 2);
  if (incidents === 0) {
    return {
      averageHours: 0,
      incidents: emergencyFixes.length,
      status: "elite"
    };
  }
  const averageHours = calculateRecoveryTime(emergencyFixes);
  const status = classifyMTTR(averageHours);
  return {
    averageHours: roundToOneDecimal(averageHours),
    incidents,
    status
  };
}
function calculateDORAMetrics(commits, skipGatesEntries, wuMetrics, weekStart, weekEnd) {
  return {
    deploymentFrequency: calculateDeploymentFrequency(commits, weekStart, weekEnd),
    leadTimeForChanges: calculateLeadTime(wuMetrics),
    changeFailureRate: calculateCFR(commits, skipGatesEntries),
    meanTimeToRecovery: calculateMTTR(commits)
  };
}

// ../metrics/dist/flow/calculate-flow-state.js
function calculateFlowState(wuMetrics) {
  const ready = wuMetrics.filter((wu) => wu.status === "ready").length;
  const inProgress = wuMetrics.filter((wu) => wu.status === "in_progress").length;
  const blocked = wuMetrics.filter((wu) => wu.status === "blocked").length;
  const waiting = wuMetrics.filter((wu) => wu.status === "waiting").length;
  const done = wuMetrics.filter((wu) => wu.status === "done").length;
  const totalActive = ready + inProgress + blocked + waiting;
  return {
    ready,
    inProgress,
    blocked,
    waiting,
    done,
    totalActive
  };
}

// ../metrics/dist/flow/analyze-bottlenecks.js
var WU_STATUS_DONE = "done";
function filterActiveGraph(graph) {
  const activeGraph = /* @__PURE__ */ new Map();
  for (const [id, node] of graph.entries()) {
    if (node.status !== WU_STATUS_DONE) {
      activeGraph.set(id, node);
    }
  }
  return activeGraph;
}
function buildInDegreeMap(activeGraph) {
  const inDegree = /* @__PURE__ */ new Map();
  for (const [id, node] of activeGraph.entries()) {
    let count = 0;
    for (const depId of node.blockedBy) {
      if (activeGraph.has(depId)) {
        count++;
      }
    }
    inDegree.set(id, count);
  }
  return inDegree;
}
function findStartingNodes(inDegree) {
  const queue = [];
  for (const [id, degree] of inDegree.entries()) {
    if (degree === 0) {
      queue.push(id);
    }
  }
  return queue;
}
function processNode(current, activeGraph, inDegree, workQueue) {
  const node = activeGraph.get(current);
  if (!node)
    return;
  for (const depId of node.blocks) {
    if (!activeGraph.has(depId))
      continue;
    const currentDegree = inDegree.get(depId) ?? 0;
    const newDegree = currentDegree - 1;
    inDegree.set(depId, newDegree);
    if (newDegree === 0) {
      workQueue.push(depId);
    }
  }
}
function findCycleNodes(inDegree) {
  const cycleNodes = [];
  for (const [id, degree] of inDegree.entries()) {
    if (degree > 0) {
      cycleNodes.push(id);
    }
  }
  return cycleNodes;
}
function processTopologicalSort(activeGraph, inDegree, queue) {
  const sorted = [];
  const workQueue = [...queue];
  while (workQueue.length > 0) {
    const current = workQueue.shift();
    if (current) {
      sorted.push(current);
      processNode(current, activeGraph, inDegree, workQueue);
    }
  }
  return { sorted, cycleNodes: findCycleNodes(inDegree) };
}
function topologicalSort(graph) {
  const activeGraph = filterActiveGraph(graph);
  if (activeGraph.size === 0) {
    return { order: [], hasCycle: false };
  }
  const inDegree = buildInDegreeMap(activeGraph);
  const queue = findStartingNodes(inDegree);
  const { sorted, cycleNodes } = processTopologicalSort(activeGraph, inDegree, queue);
  if (sorted.length < activeGraph.size) {
    return {
      order: sorted,
      hasCycle: true,
      warning: "Cycle detected: some WUs have circular dependencies",
      cycleNodes
    };
  }
  return { order: sorted, hasCycle: false };
}
function calculateDistances(activeGraph, topoOrder) {
  const distance = /* @__PURE__ */ new Map();
  const predecessor = /* @__PURE__ */ new Map();
  for (const id of topoOrder) {
    distance.set(id, 1);
    predecessor.set(id, null);
  }
  for (const current of topoOrder) {
    const node = activeGraph.get(current);
    if (!node)
      continue;
    for (const depId of node.blocks) {
      if (!activeGraph.has(depId))
        continue;
      const currentDist = distance.get(current) ?? 0;
      const newDistance = currentDist + 1;
      const existingDist = distance.get(depId) ?? 0;
      if (newDistance > existingDist) {
        distance.set(depId, newDistance);
        predecessor.set(depId, current);
      }
    }
  }
  return { distance, predecessor };
}
function reconstructPath(predecessor, endNode) {
  const path = [];
  let current = endNode;
  while (current !== null) {
    path.unshift(current);
    current = predecessor.get(current) ?? null;
  }
  return path;
}
function criticalPath(graph) {
  const activeGraph = filterActiveGraph(graph);
  if (activeGraph.size === 0) {
    return { path: [], length: 0 };
  }
  const topoResult = topologicalSort(graph);
  if (topoResult.hasCycle) {
    return {
      path: [],
      length: 0,
      warning: topoResult.warning,
      cycleNodes: topoResult.cycleNodes
    };
  }
  const { distance, predecessor } = calculateDistances(activeGraph, topoResult.order);
  let maxDistance = 0;
  let endNode = null;
  for (const [id, dist] of distance.entries()) {
    if (dist > maxDistance) {
      maxDistance = dist;
      endNode = id;
    }
  }
  const path = reconstructPath(predecessor, endNode);
  return {
    path,
    length: path.length
  };
}
function impactScore(graph, wuId) {
  const activeGraph = filterActiveGraph(graph);
  if (!activeGraph.has(wuId)) {
    return 0;
  }
  const visited = /* @__PURE__ */ new Set();
  const queue = [wuId];
  visited.add(wuId);
  while (queue.length > 0) {
    const current = queue.shift();
    if (!current)
      continue;
    const node = activeGraph.get(current);
    if (!node)
      continue;
    for (const depId of node.blocks) {
      if (!visited.has(depId) && activeGraph.has(depId)) {
        visited.add(depId);
        queue.push(depId);
      }
    }
  }
  return visited.size - 1;
}
function analyzeBottlenecks(graph, limit) {
  const activeGraph = filterActiveGraph(graph);
  if (activeGraph.size === 0) {
    return [];
  }
  const scores = [];
  for (const [id, node] of activeGraph.entries()) {
    const score = impactScore(graph, id);
    scores.push({
      id,
      score,
      title: node.title
    });
  }
  scores.sort((a, b) => b.score - a.score);
  return scores.slice(0, limit);
}
function getBottleneckAnalysis(graph, limit = 10) {
  return {
    bottlenecks: analyzeBottlenecks(graph, limit),
    criticalPath: criticalPath(graph)
  };
}

// ../metrics/dist/flow/generate-flow-report.js
import { quantile as quantile2 } from "simple-statistics";
var PERCENTAGE_PRECISION = 1;
function round(value, precision = PERCENTAGE_PRECISION) {
  return value.toFixed(precision);
}
function calculateGatePassRate(events) {
  const total = events.length;
  const passed = events.filter((e) => e.passed).length;
  const failed = total - passed;
  const passRate = total > 0 ? round(passed / total * STATISTICS.PERCENTAGE_MULTIPLIER) : "0.0";
  return { passRate, total, passed, failed };
}
function calculateGateP95(events) {
  if (events.length === 0)
    return 0;
  const durations = events.map((e) => e.durationMs).sort((a, b) => a - b);
  return quantile2(durations, STATISTICS.P95_PERCENTILE);
}
function initGateStats() {
  return { total: 0, passed: 0, failed: 0, passRate: "0.0" };
}
function updateGateStats(stats, passed) {
  stats.total++;
  if (passed) {
    stats.passed++;
  } else {
    stats.failed++;
  }
}
function calculatePassRate(stats) {
  if (stats.total > 0) {
    stats.passRate = round(stats.passed / stats.total * STATISTICS.PERCENTAGE_MULTIPLIER);
  }
}
function mapToObject(map) {
  const result = {};
  for (const [key, value] of map.entries()) {
    Object.defineProperty(result, key, {
      value,
      writable: true,
      enumerable: true,
      configurable: true
    });
  }
  return result;
}
function groupGatesByName(events) {
  const byNameMap = /* @__PURE__ */ new Map();
  for (const event of events) {
    const name = event.gateName;
    if (!byNameMap.has(name)) {
      byNameMap.set(name, initGateStats());
    }
    const stats = byNameMap.get(name);
    if (stats) {
      updateGateStats(stats, event.passed);
    }
  }
  for (const stats of byNameMap.values()) {
    calculatePassRate(stats);
  }
  return mapToObject(byNameMap);
}
function calculateLLMRates(completeEvents, errorCount) {
  const total = completeEvents.length + errorCount;
  const errorRate = total > 0 ? round(errorCount / total * STATISTICS.PERCENTAGE_MULTIPLIER) : "0.0";
  const fallbackCount = completeEvents.filter((e) => e.fallbackUsed).length;
  const fallbackRate = completeEvents.length > 0 ? round(fallbackCount / completeEvents.length * STATISTICS.PERCENTAGE_MULTIPLIER) : "0.0";
  return { errorRate, fallbackRate };
}
function calculateLLMLatencies(completeEvents) {
  const durations = completeEvents.map((e) => e.durationMs).filter((d) => d !== void 0).sort((a, b) => a - b);
  if (durations.length === 0) {
    return { avgLatencyMs: 0, p50LatencyMs: 0, p95LatencyMs: 0, p99LatencyMs: 0 };
  }
  const avgLatencyMs = Math.round(durations.reduce((sum, d) => sum + d, 0) / durations.length);
  const p50LatencyMs = quantile2(durations, STATISTICS.MEDIAN_PERCENTILE);
  const p95LatencyMs = quantile2(durations, STATISTICS.P95_PERCENTILE);
  const p99LatencyMs = quantile2(durations, STATISTICS.P99_PERCENTILE);
  return { avgLatencyMs, p50LatencyMs, p95LatencyMs, p99LatencyMs };
}
function calculateLLMCosts(completeEvents) {
  const totalTokens = completeEvents.reduce((sum, e) => sum + (e.tokensUsed ?? 0), 0);
  const totalCostUsd = completeEvents.reduce((sum, e) => sum + (e.estimatedCostUsd ?? 0), 0);
  const confidences = completeEvents.map((e) => e.confidence).filter((c) => c !== void 0);
  const avgConfidence = confidences.length > 0 ? (confidences.reduce((sum, c) => sum + c, 0) / confidences.length).toFixed(2) : "0.00";
  return { totalTokens, totalCostUsd, avgConfidence };
}
function initLLMTypeStats() {
  return {
    count: 0,
    avgLatencyMs: 0,
    totalCostUsd: 0,
    fallbackRate: "0.0"
  };
}
function updateLLMTypeStats(stats, event) {
  stats.count++;
  stats.avgLatencyMs += event.durationMs ?? 0;
  stats.totalCostUsd += event.estimatedCostUsd ?? 0;
}
function calculateLLMTypeAverages(stats, type, completeEvents) {
  if (stats.count > 0) {
    stats.avgLatencyMs = Math.round(stats.avgLatencyMs / stats.count);
    const typeFallbacks = completeEvents.filter((e) => e.classificationType === type && e.fallbackUsed).length;
    stats.fallbackRate = round(typeFallbacks / stats.count * STATISTICS.PERCENTAGE_MULTIPLIER);
  }
}
function groupLLMByType(completeEvents) {
  const byTypeMap = /* @__PURE__ */ new Map();
  for (const event of completeEvents) {
    const type = event.classificationType;
    if (!byTypeMap.has(type)) {
      byTypeMap.set(type, initLLMTypeStats());
    }
    const stats = byTypeMap.get(type);
    if (stats) {
      updateLLMTypeStats(stats, event);
    }
  }
  for (const [type, stats] of byTypeMap.entries()) {
    calculateLLMTypeAverages(stats, type, completeEvents);
  }
  return mapToObject(byTypeMap);
}
function calculateLLMMetrics(events) {
  const completeEvents = events.filter((e) => e.eventType === "llm.classification.complete");
  const errorEvents = events.filter((e) => e.eventType === "llm.classification.error");
  if (completeEvents.length === 0) {
    return {
      totalClassifications: 0,
      errorRate: "0.0",
      fallbackRate: "0.0",
      avgLatencyMs: 0,
      p50LatencyMs: 0,
      p95LatencyMs: 0,
      p99LatencyMs: 0,
      totalTokens: 0,
      totalCostUsd: 0,
      avgConfidence: "0.00",
      byType: {}
    };
  }
  const { errorRate, fallbackRate } = calculateLLMRates(completeEvents, errorEvents.length);
  const latencies = calculateLLMLatencies(completeEvents);
  const costs = calculateLLMCosts(completeEvents);
  const byType = groupLLMByType(completeEvents);
  return {
    totalClassifications: completeEvents.length,
    errorRate,
    fallbackRate,
    ...latencies,
    ...costs,
    byType
  };
}
function generateFlowReport(input) {
  const { gateEvents, llmEvents, completedWUs, dateRange } = input;
  const gateStats = calculateGatePassRate(gateEvents);
  const gateP95 = calculateGateP95(gateEvents);
  const byName = groupGatesByName(gateEvents);
  const llmMetrics = calculateLLMMetrics(llmEvents);
  const wuList = completedWUs.map((wu) => ({
    wuId: wu.id,
    completedDate: wu.completedAt?.toISOString().split("T")[0] ?? "",
    lane: wu.lane,
    title: wu.title
  }));
  return {
    range: dateRange,
    gates: {
      ...gateStats,
      p95: gateP95,
      byName
    },
    wus: {
      completed: completedWUs.length,
      list: wuList
    },
    llm: llmMetrics
  };
}

// ../metrics/dist/flow/capture-metrics-snapshot.js
function determineLaneStatus(blocked, inProgress) {
  if (blocked === 0)
    return "healthy";
  if (inProgress > 0 && blocked <= inProgress)
    return "at-risk";
  return "blocked";
}
function createLaneAccumulator(lane) {
  return {
    lane,
    wusCompleted: 0,
    wusInProgress: 0,
    wusBlocked: 0,
    cycleTimes: []
  };
}
function updateLaneAccumulator(acc, wu) {
  switch (wu.status) {
    case "in_progress":
      acc.wusInProgress++;
      break;
    case "blocked":
      acc.wusBlocked++;
      break;
    case "done":
      acc.wusCompleted++;
      if (typeof wu.cycleTimeHours === "number") {
        acc.cycleTimes.push(wu.cycleTimeHours);
      }
      break;
  }
}
function medianFromSorted(sorted) {
  if (sorted.length === 0)
    return 0;
  const midIndex = Math.floor(sorted.length / 2);
  return sorted.at(midIndex) ?? 0;
}
function accumulatorToHealth(acc) {
  const avgCycleTime = acc.cycleTimes.length > 0 ? acc.cycleTimes.reduce((sum, t) => sum + t, 0) / acc.cycleTimes.length : 0;
  const sortedTimes = [...acc.cycleTimes].sort((a, b) => a - b);
  const medianCycleTime = medianFromSorted(sortedTimes);
  const status = determineLaneStatus(acc.wusBlocked, acc.wusInProgress);
  return {
    lane: acc.lane,
    wusCompleted: acc.wusCompleted,
    wusInProgress: acc.wusInProgress,
    wusBlocked: acc.wusBlocked,
    averageCycleTimeHours: Math.round(avgCycleTime * STATISTICS.ROUNDING_FACTOR) / STATISTICS.ROUNDING_FACTOR,
    medianCycleTimeHours: Math.round(medianCycleTime * STATISTICS.ROUNDING_FACTOR) / STATISTICS.ROUNDING_FACTOR,
    status
  };
}
function calculateLaneMetrics(wuMetrics) {
  const laneMap = /* @__PURE__ */ new Map();
  for (const wu of wuMetrics) {
    if (!laneMap.has(wu.lane)) {
      laneMap.set(wu.lane, createLaneAccumulator(wu.lane));
    }
    const laneData = laneMap.get(wu.lane);
    if (laneData) {
      updateLaneAccumulator(laneData, wu);
    }
  }
  const laneMetrics = Array.from(laneMap.values()).map(accumulatorToHealth);
  laneMetrics.sort((a, b) => a.lane.localeCompare(b.lane));
  const activeStatuses = ["ready", "in_progress", "blocked", "waiting"];
  const totalActive = wuMetrics.filter((wu) => activeStatuses.includes(wu.status)).length;
  const totalBlocked = wuMetrics.filter((wu) => wu.status === "blocked").length;
  const totalCompleted = wuMetrics.filter((wu) => wu.status === "done").length;
  return {
    lanes: laneMetrics,
    totalActive,
    totalBlocked,
    totalCompleted
  };
}
function captureMetricsSnapshot(input) {
  const { commits, wuMetrics, skipGatesEntries, weekStart, weekEnd, type } = input;
  const snapshot = {};
  if (type === "all" || type === "dora") {
    snapshot.dora = calculateDORAMetrics(commits, skipGatesEntries, wuMetrics, weekStart, weekEnd);
  }
  if (type === "all" || type === "lanes") {
    snapshot.lanes = calculateLaneMetrics(wuMetrics);
  }
  if (type === "all" || type === "flow") {
    snapshot.flow = calculateFlowState(wuMetrics);
  }
  return snapshot;
}

// ../metrics/dist/telemetry/emit-telemetry.js
var TELEMETRY_PATHS = {
  GATES: ".lumenflow/telemetry/gates.ndjson",
  LLM_CLASSIFICATION: ".lumenflow/telemetry/llm-classification.ndjson",
  FLOW_LOG: ".lumenflow/flow.log"
};

export {
  calculateDORAMetrics,
  calculateFlowState,
  getBottleneckAnalysis,
  generateFlowReport,
  captureMetricsSnapshot,
  TELEMETRY_PATHS
};
