const require_constants = require('./constants.cjs');
const require_helpers = require('./helpers.cjs');

//#region src/journal.ts
/**
* Compare two field values, handling RegExp by source+flags rather than reference.
*/
function fieldEqual(a, b) {
	if (a instanceof RegExp && b instanceof RegExp) return a.source === b.source && a.flags === b.flags;
	return a === b;
}
/**
* Compare two systemMessage values. Handles string, string[], and RegExp.
* Both-undefined is treated as equal.
*/
function systemMessageEqual(a, b) {
	if (a === void 0 && b === void 0) return true;
	if (a === void 0 || b === void 0) return false;
	if (typeof a === "string" && typeof b === "string") return a === b;
	if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((v, i) => v === b[i]);
	if (a instanceof RegExp && b instanceof RegExp) return a.source === b.source && a.flags === b.flags;
	return false;
}
/**
* Check whether two fixture match objects have the same criteria
* (ignoring sequenceIndex). Used to group sequenced fixtures.
*/
function matchCriteriaEqual(a, b) {
	return fieldEqual(a.userMessage, b.userMessage) && systemMessageEqual(a.systemMessage, b.systemMessage) && fieldEqual(a.inputText, b.inputText) && fieldEqual(a.toolCallId, b.toolCallId) && fieldEqual(a.toolName, b.toolName) && fieldEqual(a.model, b.model) && fieldEqual(a.responseFormat, b.responseFormat) && fieldEqual(a.predicate, b.predicate) && fieldEqual(a.endpoint, b.endpoint) && fieldEqual(a.turnIndex, b.turnIndex) && fieldEqual(a.hasToolResult, b.hasToolResult);
}
var Journal = class {
	entries = [];
	fixtureMatchCountsByTestId = /* @__PURE__ */ new Map();
	maxEntries;
	fixtureCountsMaxTestIds;
	constructor(options = {}) {
		const cap = options.maxEntries;
		this.maxEntries = cap !== void 0 && cap > 0 ? cap : 0;
		const testIdCap = options.fixtureCountsMaxTestIds;
		this.fixtureCountsMaxTestIds = testIdCap !== void 0 && testIdCap > 0 ? testIdCap : 0;
	}
	/** Backwards-compatible accessor — returns the default (no testId) count map. */
	get fixtureMatchCounts() {
		return this.getFixtureMatchCountsForTest(require_constants.DEFAULT_TEST_ID);
	}
	add(entry) {
		const full = {
			id: require_helpers.generateId("req"),
			timestamp: Date.now(),
			...entry
		};
		this.entries.push(full);
		if (this.maxEntries > 0 && this.entries.length > this.maxEntries) this.entries.shift();
		return full;
	}
	getAll(opts) {
		if (opts?.limit !== void 0) return this.entries.slice(-opts.limit);
		return this.entries.slice();
	}
	getLast() {
		return this.entries.length > 0 ? this.entries[this.entries.length - 1] : null;
	}
	findByFixture(fixture) {
		return this.entries.filter((e) => e.response.fixture === fixture);
	}
	/**
	* READ-ONLY accessor. Returns the existing count map for `testId`, or an
	* empty transient Map if none exists. Does NOT insert into the cache and
	* does NOT trigger FIFO eviction — callers may read freely without
	* perturbing cache state. For the write path, see
	* `getOrCreateFixtureMatchCountsForTest`.
	*/
	getFixtureMatchCountsForTest(testId) {
		return this.fixtureMatchCountsByTestId.get(testId) ?? /* @__PURE__ */ new Map();
	}
	/**
	* WRITE path: get the count map for `testId`, inserting a fresh empty Map
	* if missing and running FIFO eviction when the testId cap is exceeded.
	* Only callers that intend to mutate the map (e.g. incrementing a count)
	* should use this.
	*/
	getOrCreateFixtureMatchCountsForTest(testId) {
		let counts = this.fixtureMatchCountsByTestId.get(testId);
		if (!counts) {
			counts = /* @__PURE__ */ new Map();
			this.fixtureMatchCountsByTestId.set(testId, counts);
			if (this.fixtureCountsMaxTestIds > 0 && this.fixtureMatchCountsByTestId.size > this.fixtureCountsMaxTestIds) {
				const oldest = this.fixtureMatchCountsByTestId.keys().next().value;
				if (oldest !== void 0) this.fixtureMatchCountsByTestId.delete(oldest);
			}
		}
		return counts;
	}
	getFixtureMatchCount(fixture, testId = require_constants.DEFAULT_TEST_ID) {
		return this.getFixtureMatchCountsForTest(testId).get(fixture) ?? 0;
	}
	incrementFixtureMatchCount(fixture, allFixtures, testId = require_constants.DEFAULT_TEST_ID) {
		const counts = this.getOrCreateFixtureMatchCountsForTest(testId);
		counts.set(fixture, (counts.get(fixture) ?? 0) + 1);
		if (fixture.match.sequenceIndex !== void 0 && allFixtures) for (const sibling of allFixtures) {
			if (sibling === fixture) continue;
			if (sibling.match.sequenceIndex === void 0) continue;
			if (matchCriteriaEqual(fixture.match, sibling.match)) counts.set(sibling, (counts.get(sibling) ?? 0) + 1);
		}
	}
	clearMatchCounts(testId) {
		if (testId !== void 0) this.fixtureMatchCountsByTestId.delete(testId);
		else this.fixtureMatchCountsByTestId.clear();
	}
	/**
	* Clear ONLY the request journal entries, preserving fixture match-counts.
	* Match-counts are fixture-matching/sequencing state, not journal data, so
	* clearing the journal must not silently rewind sequenced fixtures. Used by
	* `POST /__aimock/reset/journal`. For a full reset (entries + match-counts),
	* use `clear()` instead.
	*/
	clearEntries() {
		this.entries = [];
	}
	clear() {
		this.entries = [];
		this.fixtureMatchCountsByTestId.clear();
	}
	get size() {
		return this.entries.length;
	}
};

//#endregion
exports.Journal = Journal;
//# sourceMappingURL=journal.cjs.map