/**
 * M006/S02 regression test — manifest-driven composition drift guard.
 *
 * Must-Have #3 from M006/S02 plan:
 *   "Regression test that (a) confirms manifest declares computed for all
 *    knowledge-relevant unit types and (b) checks auto-prompts.js has no
 *    direct inlineKnowledgeBudgeted/inlineGraphSubgraph calls outside
 *    composeUnitContext."
 *
 * This test asserts the M005/M006 migration invariants stay green:
 *   1. No builder function pushes knowledge/graph blocks to its `parts` array
 *      via a top-level `await inlineKnowledgeBudgeted` / `inlineKnowledgeScoped`
 *      / `inlineGraphSubgraph` call. Those calls must only happen INSIDE the
 *      composeUnitContext `computed: { ... }` registry, where the composer
 *      controls ordering and prevents duplication.
 *   2. Unit type manifests that previously declared knowledge/graph as
 *      computed continue to declare them — guards against accidental removal.
 */
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";

const AUTO_PROMPTS = "src/resources/extensions/sf/auto-prompts.js";
const MANIFEST = "src/resources/extensions/sf/unit-context-manifest.js";

describe("M006/S02 — manifest-driven composition drift", () => {
	it("no top-level manual knowledge/graph inline calls outside composer", () => {
		const src = readFileSync(AUTO_PROMPTS, "utf8");
		// Match any line where the result of inlineKnowledge* / inlineGraphSubgraph
		// is assigned to a top-level builder variable (not inside a `build:` arrow
		// passed to composeUnitContext.computed). The pattern matches:
		//   `\tconst <name> = await inline(Knowledge|Graph)...`
		// at builder-function indentation. Calls inside the composer's `build:` arrow
		// are at deeper indentation (multiple tabs) AND inside `computed: { ... }`.
		const violatingPattern =
			/^\tconst\s+\w+\s*=\s*await\s+inline(Knowledge(Budgeted|Scoped)|GraphSubgraph)\(/gm;
		const matches = [...src.matchAll(violatingPattern)];
		if (matches.length > 0) {
			const lines = matches.map((m) => {
				const upTo = src.slice(0, m.index);
				const lineNum = upTo.split("\n").length;
				return `${AUTO_PROMPTS}:${lineNum}: ${m[0]}`;
			});
			throw new Error(
				`Found ${matches.length} manual knowledge/graph inline call(s) outside the composer. ` +
					`These violate M005/M006 invariant — knowledge/graph must be declared as computed ` +
					`artifacts in the manifest and resolved inside composeUnitContext.computed. ` +
					`Sites:\n  ${lines.join("\n  ")}`,
			);
		}
		expect(matches.length).toBe(0);
	});

	it("manifest declares computed knowledge/graph for builders that need them", () => {
		const src = readFileSync(MANIFEST, "utf8");
		// Locate unit-type entries that should carry knowledge/graph computed
		// (i.e. they previously had manual injection in their builder). This
		// list is the union of M004/M005/M006 migration targets — adding a
		// new builder with knowledge/graph requires updating this list AND
		// declaring computed in the manifest.
		const KNOWLEDGE_AWARE_UNIT_TYPES = [
			"discuss-project",
			"discuss-requirements",
			"research-project",
			"research-milestone",
			"discuss-milestone",
			"plan-milestone",
			"plan-slice",
			"execute-task",
			"complete-slice",
			"complete-milestone",
			"validate-milestone",
			"reassess-roadmap",
		];
		// replan-slice intentionally omitted: it re-plans from slice-context +
		// slice-plan + blocker-summaries and has no knowledge/graph dependency
		// (manifest declares computed: []; builder makes no manual inline calls).
		const missing = [];
		for (const unitType of KNOWLEDGE_AWARE_UNIT_TYPES) {
			// Find the manifest section for this unit type.
			const sectionRe = new RegExp(
				`"${unitType}":\\s*\\{[\\s\\S]*?maxSystemPromptChars`,
			);
			const sectionMatch = src.match(sectionRe);
			if (!sectionMatch) {
				missing.push(`${unitType}: manifest entry not found`);
				continue;
			}
			const section = sectionMatch[0];
			// Either declared as inline knowledge (M006 research-milestone style)
			// OR computed (most other builders).
			const hasInlineKnowledge = /inline:\s*\[[^\]]*"knowledge"/.test(section);
			const hasComputedKnowledge =
				/computed:\s*\[[^\]]*"knowledge"/.test(section) ||
				/computed:\s*\[[^\]]*"graph"/.test(section);
			if (!hasInlineKnowledge && !hasComputedKnowledge) {
				missing.push(
					`${unitType}: manifest does not declare "knowledge" or "graph" (inline or computed)`,
				);
			}
		}
		if (missing.length > 0) {
			throw new Error(
				`Manifest drift detected — knowledge-aware unit types missing computed declarations:\n  ${missing.join("\n  ")}`,
			);
		}
		expect(missing.length).toBe(0);
	});
});
