import { t as createSubsystemLogger } from "./subsystem-qDUB67UD.js";
import { i as writeTextAtomic } from "./json-files-kvH2Eboc.js";
import { n as computeFragmentDecay, r as isFragmentExpired } from "./fragment-types-B8q-zg6v.js";
import { existsSync } from "node:fs";
import { join } from "node:path";
import { readFile, readdir, stat } from "node:fs/promises";
import { randomUUID } from "node:crypto";
//#region src/cognitive/insight/fragment-store.ts
const log = createSubsystemLogger("cognitive/fragment-store");
const FRAGMENTS_DIR = "cognitive/fragments";
var FragmentStore = class FragmentStore {
	constructor(configDir) {
		this.configDir = configDir;
		this.cache = /* @__PURE__ */ new Map();
	}
	static {
		this.CACHE_TTL_MS = 6e4;
	}
	filePath(agentId, userId) {
		return join(this.configDir, FRAGMENTS_DIR, agentId, `${userId}.json`);
	}
	cacheKey(agentId, userId) {
		return `${agentId}/${userId}`;
	}
	pruneAndDecay(fragments, userId) {
		const now = Date.now();
		const before = fragments.length;
		const result = fragments.filter((f) => !isFragmentExpired(f, now)).map((f) => ({
			...f,
			strength: computeFragmentDecay(f, now)
		}));
		const removed = before - result.length;
		if (removed > 0 && userId) log.info("fragment maintenance", {
			userId,
			before,
			after: result.length,
			removed
		});
		return result;
	}
	async load(agentId, userId) {
		const key = this.cacheKey(agentId, userId);
		const cached = this.cache.get(key);
		if (cached && Date.now() - cached.loadedAt < FragmentStore.CACHE_TTL_MS) return this.pruneAndDecay(cached.fragments, userId);
		const path = this.filePath(agentId, userId);
		if (!existsSync(path)) return [];
		try {
			const raw = await readFile(path, "utf-8");
			const parsed = JSON.parse(raw);
			if (typeof parsed !== "object" || parsed === null || parsed.version !== 1 || !Array.isArray(parsed.fragments)) return [];
			const fragments = this.pruneAndDecay(parsed.fragments, userId);
			this.cache.set(key, {
				fragments: parsed.fragments,
				loadedAt: Date.now()
			});
			return fragments;
		} catch (err) {
			log.warn("Failed to load fragments", {
				agentId,
				userId,
				error: String(err)
			});
			return [];
		}
	}
	async save(agentId, userId, fragments) {
		await writeTextAtomic(this.filePath(agentId, userId), JSON.stringify({
			version: 1,
			fragments
		}, null, 2));
		this.cache.set(this.cacheKey(agentId, userId), {
			fragments,
			loadedAt: Date.now()
		});
	}
	async addFragment(agentId, userId, fragment) {
		const existing = await this.load(agentId, userId);
		const domainKey = (domains) => [...domains].toSorted().join(",");
		const dupIdx = existing.findIndex((f) => f.structuralTag === fragment.structuralTag && domainKey(f.domains) === domainKey(fragment.domains));
		if (dupIdx >= 0) {
			const dup = existing[dupIdx];
			if (fragment.strength > dup.strength) existing[dupIdx] = {
				...fragment,
				evidence: dup.evidence.length >= fragment.evidence.length ? dup.evidence : fragment.evidence
			};
			else existing[dupIdx] = {
				...dup,
				evidence: dup.evidence.length >= fragment.evidence.length ? dup.evidence : fragment.evidence
			};
		} else existing.push(fragment);
		if (dupIdx >= 0) log.info("fragment dedup hit", {
			agentId,
			userId,
			structuralTag: fragment.structuralTag,
			existingStrength: existing[dupIdx].strength.toFixed(3),
			newStrength: fragment.strength.toFixed(3)
		});
		else log.info("fragment added", {
			agentId,
			userId,
			structuralTag: fragment.structuralTag,
			strength: fragment.strength.toFixed(3),
			domains: fragment.domains
		});
		await this.save(agentId, userId, existing);
		return existing;
	}
	async findClusters(agentId, userId) {
		const allFragments = await this.load(agentId, userId);
		if (allFragments.length === 0) return [];
		const CLUSTER_MIN_STRENGTH = .05;
		const fragments = allFragments.filter((f) => f.strength >= CLUSTER_MIN_STRENGTH);
		if (fragments.length === 0) return [];
		const parent = /* @__PURE__ */ new Map();
		const fragmentDomains = /* @__PURE__ */ new Map();
		const find = (id) => {
			let root = id;
			while (parent.get(root) !== root) {
				const p = parent.get(root);
				parent.set(root, parent.get(p) ?? p);
				root = p;
			}
			return root;
		};
		const union = (a, b) => {
			const ra = find(a);
			const rb = find(b);
			if (ra !== rb) parent.set(ra, rb);
		};
		const domainToFragments = /* @__PURE__ */ new Map();
		for (const f of fragments) {
			parent.set(f.id, f.id);
			const domains = new Set(f.domains);
			fragmentDomains.set(f.id, domains);
			for (const d of f.domains) {
				const list = domainToFragments.get(d);
				if (list) list.push(f.id);
				else domainToFragments.set(d, [f.id]);
			}
		}
		for (const [_domain, ids] of domainToFragments) for (let i = 1; i < ids.length; i++) union(ids[0], ids[i]);
		const groups = /* @__PURE__ */ new Map();
		for (const f of fragments) {
			const root = find(f.id);
			const group = groups.get(root);
			if (group) group.push(f.id);
			else groups.set(root, [f.id]);
		}
		const fragmentMap = new Map(fragments.map((f) => [f.id, f]));
		const clusters = [];
		for (const [_root, ids] of groups) {
			const groupFragments = ids.map((id) => fragmentMap.get(id));
			const allDomains = /* @__PURE__ */ new Set();
			let tensionCount = 0;
			let strengthSum = 0;
			for (const f of groupFragments) {
				for (const d of f.domains) allDomains.add(d);
				if (f.kind === "unresolved_tension" || f.kind === "contradictory_positions") tensionCount++;
				strengthSum += f.strength;
			}
			const avgStrength = strengthSum / groupFragments.length;
			if (groupFragments.length < 2) continue;
			if (allDomains.size < 2 && tensionCount < 1 && groupFragments.length < 3) continue;
			if (avgStrength < .15) continue;
			clusters.push({
				id: randomUUID(),
				fragmentIds: ids,
				domains: [...allDomains],
				structuralPattern: [...new Set(groupFragments.map((f) => f.kind))].join("+"),
				averageStrength: avgStrength,
				createdAt: Date.now()
			});
		}
		clusters.sort((a, b) => b.averageStrength * b.fragmentIds.length - a.averageStrength * a.fragmentIds.length);
		log.info("fragment clusters", {
			agentId,
			userId,
			clusterCount: clusters.length,
			sizes: clusters.map((c) => c.fragmentIds.length),
			domains: clusters.map((c) => [...c.domains])
		});
		return clusters;
	}
	async removeFragment(agentId, userId, fragmentId) {
		const filtered = (await this.load(agentId, userId)).filter((f) => f.id !== fragmentId);
		await this.save(agentId, userId, filtered);
	}
	async touchFragment(agentId, userId, fragmentId) {
		const fragments = await this.load(agentId, userId);
		const target = fragments.find((f) => f.id === fragmentId);
		if (target) {
			target.strength = Math.min(1, target.strength + .1);
			target.initialStrength = Math.min(1, (target.initialStrength ?? target.strength) + .1);
			await this.save(agentId, userId, fragments);
		}
	}
	async listUserIds(agentId) {
		const dir = join(this.configDir, FRAGMENTS_DIR, agentId);
		try {
			return (await readdir(dir)).filter((name) => name.endsWith(".json")).map((name) => name.slice(0, -5)).toSorted();
		} catch {
			return [];
		}
	}
	async listAgentIds() {
		const dir = join(this.configDir, FRAGMENTS_DIR);
		try {
			const entries = await readdir(dir);
			const result = [];
			for (const name of entries) if ((await stat(join(dir, name))).isDirectory()) result.push(name);
			return result.toSorted();
		} catch {
			return [];
		}
	}
};
//#endregion
export { FragmentStore as t };
