import {
  countRecallTokenOverlap,
  normalizeRecallTokens
} from "./chunk-DT5TVLJE.js";
import {
  assertIsoRecordedAt,
  assertSafePathSegment,
  assertString,
  isRecord,
  optionalString,
  optionalStringArray,
  recordStoreDay,
  validateStringRecord
} from "./chunk-DGXUHMOV.js";
import {
  listJsonFiles,
  readJsonFile
} from "./chunk-LPSF4OQH.js";

// src/causal-trajectory.ts
import path from "path";
import { mkdir, writeFile } from "fs/promises";
function validateMetadata(raw) {
  return validateStringRecord(raw, "metadata");
}
function resolveCausalTrajectoryStoreDir(memoryDir, overrideDir) {
  if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
    return overrideDir.trim();
  }
  return path.join(memoryDir, "state", "causal-trajectories");
}
function validateCausalTrajectoryRecord(raw) {
  if (!isRecord(raw)) throw new Error("causal trajectory record must be an object");
  if (raw.schemaVersion !== 1) throw new Error("schemaVersion must be 1");
  const outcomeKind = assertString(raw.outcomeKind, "outcomeKind");
  if (!["success", "failure", "partial", "unknown"].includes(outcomeKind)) {
    throw new Error("outcomeKind must be one of success|failure|partial|unknown");
  }
  return {
    schemaVersion: 1,
    trajectoryId: assertSafePathSegment(assertString(raw.trajectoryId, "trajectoryId"), "trajectoryId"),
    recordedAt: assertIsoRecordedAt(assertString(raw.recordedAt, "recordedAt")),
    sessionKey: assertString(raw.sessionKey, "sessionKey"),
    goal: assertString(raw.goal, "goal"),
    actionSummary: assertString(raw.actionSummary, "actionSummary"),
    observationSummary: assertString(raw.observationSummary, "observationSummary"),
    outcomeKind,
    outcomeSummary: assertString(raw.outcomeSummary, "outcomeSummary"),
    followUpSummary: optionalString(raw.followUpSummary),
    objectiveStateSnapshotRefs: optionalStringArray(raw.objectiveStateSnapshotRefs, "objectiveStateSnapshotRefs"),
    entityRefs: optionalStringArray(raw.entityRefs, "entityRefs"),
    tags: optionalStringArray(raw.tags, "tags"),
    metadata: validateMetadata(raw.metadata)
  };
}
async function recordCausalTrajectory(options) {
  const rootDir = resolveCausalTrajectoryStoreDir(options.memoryDir, options.causalTrajectoryStoreDir);
  const validated = validateCausalTrajectoryRecord(options.record);
  const day = recordStoreDay(validated.recordedAt);
  const trajectoriesDir = path.join(rootDir, "trajectories", day);
  const filePath = path.join(trajectoriesDir, `${validated.trajectoryId}.json`);
  await mkdir(trajectoriesDir, { recursive: true });
  await writeFile(filePath, JSON.stringify(validated, null, 2), "utf8");
  if (options.actionGraphRecallEnabled === true) {
    try {
      const { appendCausalTrajectoryGraphEdges } = await import("./causal-trajectory-graph.js");
      await appendCausalTrajectoryGraphEdges({
        memoryDir: options.memoryDir,
        record: validated
      });
    } catch (error) {
      const { log } = await import("./logger.js");
      log.warn(
        `[causal-trajectory] action-conditioned graph write failed for ${validated.trajectoryId}: ${error instanceof Error ? error.message : String(error)}`
      );
    }
  }
  if (options.cmcEnabled === true) {
    try {
      const { stitchCausalChain } = await import("./causal-chain.js");
      await stitchCausalChain({
        memoryDir: options.memoryDir,
        causalTrajectoryStoreDir: options.causalTrajectoryStoreDir,
        newTrajectory: validated,
        config: {
          lookbackDays: options.cmcStitchLookbackDays ?? 7,
          minScore: options.cmcStitchMinScore ?? 2.5,
          maxEdgesPerTrajectory: options.cmcStitchMaxEdgesPerTrajectory ?? 3
        }
      });
    } catch (error) {
      const { log } = await import("./logger.js");
      log.warn(
        `[cmc] causal chain stitching failed for ${validated.trajectoryId}: ${error instanceof Error ? error.message : String(error)}`
      );
    }
  }
  return filePath;
}
async function readCausalTrajectoryRecords(options) {
  const rootDir = resolveCausalTrajectoryStoreDir(options.memoryDir, options.causalTrajectoryStoreDir);
  const files = await listJsonFiles(path.join(rootDir, "trajectories"));
  const trajectories = [];
  const invalidTrajectories = [];
  for (const filePath of files) {
    try {
      trajectories.push(validateCausalTrajectoryRecord(await readJsonFile(filePath)));
    } catch (error) {
      invalidTrajectories.push({
        path: filePath,
        error: error instanceof Error ? error.message : String(error)
      });
    }
  }
  return { files, trajectories, invalidTrajectories };
}
function filterTrajectoriesByLookbackDays(trajectories, lookbackDays, nowMs = Date.now()) {
  const days = Math.max(1, Math.floor(lookbackDays));
  const cutoff = nowMs - days * 864e5;
  return trajectories.filter((t) => {
    const ms = Date.parse(t.recordedAt);
    return Number.isFinite(ms) && ms >= cutoff;
  });
}
async function getCausalTrajectoryStoreStatus(options) {
  const rootDir = resolveCausalTrajectoryStoreDir(options.memoryDir, options.causalTrajectoryStoreDir);
  const trajectoriesDir = path.join(rootDir, "trajectories");
  const { files, trajectories, invalidTrajectories } = await readCausalTrajectoryRecords(options);
  trajectories.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
  const byOutcome = {};
  for (const trajectory of trajectories) {
    byOutcome[trajectory.outcomeKind] = (byOutcome[trajectory.outcomeKind] ?? 0) + 1;
  }
  return {
    enabled: options.enabled,
    rootDir,
    trajectoriesDir,
    trajectories: {
      total: files.length,
      valid: trajectories.length,
      invalid: invalidTrajectories.length,
      byOutcome,
      latestTrajectoryId: trajectories[0]?.trajectoryId,
      latestRecordedAt: trajectories[0]?.recordedAt,
      latestSessionKey: trajectories[0]?.sessionKey
    },
    latestTrajectory: trajectories[0],
    invalidTrajectories
  };
}
function lexicalScoreCausalTrajectoryRecord(record, queryTokens) {
  const weightedFields = [
    ["goal", record.goal, 4],
    ["action", record.actionSummary, 3],
    ["observation", record.observationSummary, 3],
    ["outcome", record.outcomeSummary, 3],
    ["follow_up", record.followUpSummary, 2],
    ["outcome_kind", record.outcomeKind, 1],
    ["tags", record.tags?.join(" "), 2],
    ["entity_refs", record.entityRefs?.join(" "), 2],
    ["objective_state_refs", record.objectiveStateSnapshotRefs?.join(" "), 1]
  ];
  let score = 0;
  const matchedFields = [];
  for (const [field, value, weight] of weightedFields) {
    const matches = countRecallTokenOverlap(queryTokens, value, ["make"]);
    if (matches > 0) matchedFields.push(field);
    score += matches * weight;
  }
  return { score, matchedFields };
}
function scoreCausalTrajectoryRecord(record, lexicalScore, sessionKey) {
  let score = lexicalScore;
  if (sessionKey && record.sessionKey === sessionKey) score += 1.5;
  const recordedAtMs = Date.parse(record.recordedAt);
  if (Number.isFinite(recordedAtMs)) {
    const ageHours = Math.max(0, (Date.now() - recordedAtMs) / 36e5);
    score += 1 / (1 + ageHours);
  }
  return score;
}
async function searchCausalTrajectories(options) {
  const maxResults = Math.max(0, Math.floor(options.maxResults));
  if (maxResults === 0) return [];
  const { trajectories } = await readCausalTrajectoryRecords(options);
  if (trajectories.length === 0) return [];
  const queryTokens = new Set(normalizeRecallTokens(options.query, ["make"]));
  if (queryTokens.size === 0) return [];
  const scored = trajectories.map((record) => {
    const lexical = lexicalScoreCausalTrajectoryRecord(record, queryTokens);
    return {
      record,
      matchedFields: lexical.matchedFields,
      lexicalScore: lexical.score,
      score: scoreCausalTrajectoryRecord(record, lexical.score, options.sessionKey)
    };
  });
  const filtered = scored.filter((result) => result.lexicalScore > 0);
  filtered.sort((left, right) => {
    if (right.score !== left.score) return right.score - left.score;
    return right.record.recordedAt.localeCompare(left.record.recordedAt);
  });
  return filtered.slice(0, maxResults).map(({ record, score, matchedFields }) => ({
    record,
    score,
    matchedFields
  }));
}

export {
  resolveCausalTrajectoryStoreDir,
  validateCausalTrajectoryRecord,
  recordCausalTrajectory,
  readCausalTrajectoryRecords,
  filterTrajectoriesByLookbackDays,
  getCausalTrajectoryStoreStatus,
  searchCausalTrajectories
};
//# sourceMappingURL=chunk-G2WADRQ3.js.map