import {
  readUtilityTelemetryEvents,
  resolveUtilityTelemetryDir
} from "./chunk-3QKK7QOS.js";
import {
  clamp01
} from "./chunk-TBBDFYXW.js";

// src/utility-learner.ts
import path from "path";
import { mkdir, readFile, rename, writeFile } from "fs/promises";
var UTILITY_LEARNING_SNAPSHOT_VERSION = 1;
var UTILITY_LEARNING_STATE_FILE = "learning-state.json";
function clampWeight(value, maxWeightMagnitude) {
  const limit = Number.isFinite(maxWeightMagnitude) && maxWeightMagnitude > 0 ? maxWeightMagnitude : 0;
  return Math.max(-limit, Math.min(limit, value));
}
function coerceLearningWindowDays(value) {
  if (!Number.isFinite(value)) return 14;
  return Math.max(1, Math.floor(value));
}
function coerceMinEventCount(value) {
  if (!Number.isFinite(value)) return 3;
  return Math.max(1, Math.floor(value));
}
function coerceMaxWeightMagnitude(value) {
  if (!Number.isFinite(value)) return 0.35;
  return Math.max(0, Math.min(1, value));
}
function roundWeight(value) {
  return Math.round(value * 1e3) / 1e3;
}
function outcomeCountsFor(events) {
  const counts = {};
  for (const event of events) {
    counts[event.outcome] = (counts[event.outcome] ?? 0) + 1;
  }
  return counts;
}
function selectRecentEvents(events, now, windowDays) {
  if (!Number.isFinite(windowDays) || windowDays <= 0) return [...events];
  const minTimestamp = now.getTime() - windowDays * 864e5;
  return events.filter((event) => {
    const ts = Date.parse(event.recordedAt);
    return Number.isFinite(ts) && ts >= minTimestamp;
  });
}
function confidenceFromEvents(eventCount, averageUtilityScore) {
  if (eventCount <= 0) return 0;
  return roundWeight(clamp01(Math.abs(averageUtilityScore) * Math.min(1, eventCount / 10)));
}
function validateUtilityLearningSnapshot(raw) {
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
    throw new Error("utility learning snapshot must be an object");
  }
  const record = raw;
  if (record.version !== UTILITY_LEARNING_SNAPSHOT_VERSION) {
    throw new Error("utility learning snapshot version must be 1");
  }
  if (typeof record.updatedAt !== "string" || record.updatedAt.length === 0) {
    throw new Error("utility learning snapshot updatedAt must be a string");
  }
  if (typeof record.windowDays !== "number" || !Number.isFinite(record.windowDays) || record.windowDays < 0) {
    throw new Error("utility learning snapshot windowDays must be a non-negative number");
  }
  if (typeof record.minEventCount !== "number" || !Number.isFinite(record.minEventCount) || record.minEventCount < 1) {
    throw new Error("utility learning snapshot minEventCount must be >= 1");
  }
  if (typeof record.maxWeightMagnitude !== "number" || !Number.isFinite(record.maxWeightMagnitude) || record.maxWeightMagnitude < 0) {
    throw new Error("utility learning snapshot maxWeightMagnitude must be >= 0");
  }
  if (!Array.isArray(record.weights)) {
    throw new Error("utility learning snapshot weights must be an array");
  }
  const weights = record.weights.map((entry) => {
    if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
      throw new Error("utility learning weight must be an object");
    }
    const weight = entry;
    const target = weight.target;
    const decision = weight.decision;
    if (target !== "promotion" && target !== "ranking") {
      throw new Error("utility learning weight target must be promotion|ranking");
    }
    if (!["promote", "demote", "hold", "boost", "suppress"].includes(String(decision))) {
      throw new Error("utility learning weight decision is invalid");
    }
    if (typeof weight.eventCount !== "number" || !Number.isFinite(weight.eventCount) || weight.eventCount < 0) {
      throw new Error("utility learning weight eventCount must be >= 0");
    }
    if (typeof weight.learnedWeight !== "number" || !Number.isFinite(weight.learnedWeight)) {
      throw new Error("utility learning weight learnedWeight must be finite");
    }
    if (typeof weight.averageUtilityScore !== "number" || !Number.isFinite(weight.averageUtilityScore)) {
      throw new Error("utility learning weight averageUtilityScore must be finite");
    }
    if (typeof weight.confidence !== "number" || !Number.isFinite(weight.confidence)) {
      throw new Error("utility learning weight confidence must be finite");
    }
    if (typeof weight.updatedAt !== "string" || weight.updatedAt.length === 0) {
      throw new Error("utility learning weight updatedAt must be a string");
    }
    const outcomeCounts = weight.outcomeCounts ?? {};
    return {
      target,
      decision,
      eventCount: weight.eventCount,
      learnedWeight: roundWeight(weight.learnedWeight),
      averageUtilityScore: roundWeight(weight.averageUtilityScore),
      confidence: roundWeight(clamp01(weight.confidence)),
      outcomeCounts,
      updatedAt: weight.updatedAt
    };
  });
  return {
    version: 1,
    updatedAt: record.updatedAt,
    windowDays: record.windowDays,
    minEventCount: record.minEventCount,
    maxWeightMagnitude: record.maxWeightMagnitude,
    weights
  };
}
function resolveUtilityLearningStatePath(memoryDir, utilityTelemetryDir) {
  return path.join(resolveUtilityTelemetryDir(memoryDir, utilityTelemetryDir), UTILITY_LEARNING_STATE_FILE);
}
async function readUtilityLearningSnapshot(memoryDir, utilityTelemetryDir) {
  const statePath = resolveUtilityLearningStatePath(memoryDir, utilityTelemetryDir);
  try {
    const raw = JSON.parse(await readFile(statePath, "utf8"));
    return validateUtilityLearningSnapshot(raw);
  } catch {
    return null;
  }
}
async function writeUtilityLearningSnapshot(statePath, snapshot) {
  const tempPath = `${statePath}.tmp`;
  await mkdir(path.dirname(statePath), { recursive: true });
  await writeFile(tempPath, `${JSON.stringify(snapshot, null, 2)}
`, "utf8");
  await rename(tempPath, statePath);
}
async function learnUtilityPromotionWeights(options) {
  const statePath = resolveUtilityLearningStatePath(options.memoryDir, options.utilityTelemetryDir);
  if (!options.enabled) {
    return {
      applied: false,
      reason: "disabled",
      statePath,
      snapshot: null
    };
  }
  const now = options.now ?? /* @__PURE__ */ new Date();
  const updatedAt = now.toISOString();
  const windowDays = coerceLearningWindowDays(options.learningWindowDays);
  const minEventCount = coerceMinEventCount(options.minEventCount);
  const maxWeightMagnitude = coerceMaxWeightMagnitude(options.maxWeightMagnitude);
  const recentEvents = selectRecentEvents(
    (await readUtilityTelemetryEvents({
      memoryDir: options.memoryDir,
      utilityTelemetryDir: options.utilityTelemetryDir
    })).events,
    now,
    windowDays
  );
  const grouped = /* @__PURE__ */ new Map();
  for (const event of recentEvents) {
    const key = `${event.target}:${event.decision}`;
    const existing = grouped.get(key);
    if (existing) {
      existing.push(event);
    } else {
      grouped.set(key, [event]);
    }
  }
  const weights = [];
  for (const events of grouped.values()) {
    if (events.length < minEventCount) continue;
    const target = events[0].target;
    const decision = events[0].decision;
    const averageUtilityScore = events.reduce((sum, event) => sum + event.utilityScore, 0) / events.length;
    const confidence = confidenceFromEvents(events.length, averageUtilityScore);
    const learnedWeight = roundWeight(
      clampWeight(averageUtilityScore * confidence, maxWeightMagnitude)
    );
    weights.push({
      target,
      decision,
      eventCount: events.length,
      learnedWeight,
      averageUtilityScore: roundWeight(averageUtilityScore),
      confidence,
      outcomeCounts: outcomeCountsFor(events),
      updatedAt
    });
  }
  weights.sort((left, right) => {
    const targetCompare = left.target.localeCompare(right.target);
    if (targetCompare !== 0) return targetCompare;
    return left.decision.localeCompare(right.decision);
  });
  const snapshot = {
    version: 1,
    updatedAt,
    windowDays,
    minEventCount,
    maxWeightMagnitude,
    weights
  };
  if (weights.length === 0) {
    return {
      applied: false,
      reason: "insufficient_events",
      statePath,
      snapshot
    };
  }
  await writeUtilityLearningSnapshot(statePath, snapshot);
  return {
    applied: true,
    reason: "learned",
    statePath,
    snapshot
  };
}
async function getUtilityLearningStatus(options) {
  const rootDir = resolveUtilityTelemetryDir(options.memoryDir, options.utilityTelemetryDir);
  const statePath = resolveUtilityLearningStatePath(options.memoryDir, options.utilityTelemetryDir);
  if (!options.enabled) {
    return {
      enabled: false,
      promotionByOutcomeEnabled: options.promotionByOutcomeEnabled === true,
      rootDir,
      statePath,
      snapshot: null,
      weights: {
        total: 0,
        positive: 0,
        negative: 0,
        zero: 0
      }
    };
  }
  const snapshot = await readUtilityLearningSnapshot(options.memoryDir, options.utilityTelemetryDir);
  const weights = snapshot?.weights ?? [];
  return {
    enabled: true,
    promotionByOutcomeEnabled: options.promotionByOutcomeEnabled === true,
    rootDir,
    statePath,
    snapshot,
    weights: {
      total: weights.length,
      positive: weights.filter((entry) => entry.learnedWeight > 0).length,
      negative: weights.filter((entry) => entry.learnedWeight < 0).length,
      zero: weights.filter((entry) => entry.learnedWeight === 0).length,
      latestUpdatedAt: snapshot?.updatedAt
    }
  };
}

export {
  resolveUtilityLearningStatePath,
  readUtilityLearningSnapshot,
  learnUtilityPromotionWeights,
  getUtilityLearningStatus
};
//# sourceMappingURL=chunk-PYXS46O7.js.map