/**
 * SF Command — /extract-learnings
 *
 * Analyses completed milestone artefacts and dispatches an LLM turn that
 * extracts structured knowledge into 4 categories:
 *   Decisions · Lessons · Patterns · Surprises
 */
import { existsSync, readFileSync } from "node:fs";
import { basename, join } from "node:path";
import { projectRoot } from "./commands/context.js";
import { resolveMilestonePath, sfRoot } from "./paths.js";
// ─── Pure functions ───────────────────────────────────────────────────────────
export function parseExtractLearningsArgs(args) {
	const trimmed = args.trim();
	return { milestoneId: trimmed || null };
}
export function buildLearningsOutputPath(milestoneDir, milestoneId) {
	return join(milestoneDir, `${milestoneId}-LEARNINGS.md`);
}
export function resolvePhaseArtifacts(milestoneDir, milestoneId) {
	const missingRequired = [];
	const planFile = `${milestoneId}-PLAN.md`;
	const summaryFile = `${milestoneId}-SUMMARY.md`;
	const verificationFile = `${milestoneId}-VERIFICATION.md`;
	const uatFile = `${milestoneId}-UAT.md`;
	const planPath = join(milestoneDir, planFile);
	const summaryPath = join(milestoneDir, summaryFile);
	const verificationPath = join(milestoneDir, verificationFile);
	const uatPath = join(milestoneDir, uatFile);
	const plan = existsSync(planPath) ? planPath : null;
	const summary = existsSync(summaryPath) ? summaryPath : null;
	const verification = existsSync(verificationPath) ? verificationPath : null;
	const uat = existsSync(uatPath) ? uatPath : null;
	if (!plan) missingRequired.push(planFile);
	if (!summary) missingRequired.push(summaryFile);
	return { plan, summary, verification, uat, missingRequired };
}
export function buildExtractLearningsPrompt(ctx) {
	const optionalSections = [];
	if (ctx.verificationContent) {
		optionalSections.push(
			`## Verification Report\n\n${ctx.verificationContent}`,
		);
	}
	if (ctx.uatContent) {
		optionalSections.push(`## UAT Report\n\n${ctx.uatContent}`);
	}
	const missingNote =
		ctx.missingArtifacts.length > 0
			? `\nNote: The following optional artefacts were not available: ${ctx.missingArtifacts.join(", ")}\n`
			: "";
	return `# Extract Learnings — ${ctx.milestoneId}: ${ctx.milestoneName}

**Project:** ${ctx.projectName}
**Output file:** ${ctx.outputPath}

## Your Task

Analyse the artefacts below and extract structured knowledge from milestone **${ctx.milestoneId}**.

Write a LEARNINGS document to \`${ctx.outputPath}\` with the following 4 sections:

### Decisions
Key architectural and design decisions made during this milestone, including the rationale and alternatives considered.

### Lessons
What the team learned — technical discoveries, process insights, and knowledge gaps that were filled.

### Patterns
Reusable patterns, approaches, or solutions that emerged and should be applied in future work.

### Surprises
Unexpected challenges, discoveries, or outcomes — things that deviated from assumptions.

### Source Attribution (REQUIRED)

Every extracted item MUST include a \`Source:\` line immediately after the item text.
Format: \`Source: {artifact-filename}/{section}\`
Example: \`Source: M001-PLAN.md/Architecture Decisions\`

Items without a Source attribution are invalid and must not be included in the output.

---

## Artefacts

### Plan

${ctx.planContent}

---

### Summary

${ctx.summaryContent}

${optionalSections.join("\n\n---\n\n")}
${missingNote}
---

## Output Format

Write the LEARNINGS file to \`${ctx.relativeOutputPath}\` with YAML frontmatter followed by the 4 sections above.
Each section should contain concise, actionable bullet points.
Every bullet point MUST be followed by a source line, for example:

\`\`\`
### Decisions
- Chose PostgreSQL over SQLite for concurrent write support.
  Source: M001-PLAN.md/Architecture Decisions
\`\`\`

Items without a \`Source:\` line are invalid.

---

## Optional: Capture Individual Learnings

If the \`log_reasoning\` tool is available, call it once for each extracted item with:
- category: "decision" | "lesson" | "pattern" | "surprise"
- phase: "${ctx.milestoneId}"
- content: {the learning text}
- source: {artifact filename}

If \`log_reasoning\` is not available, skip this step silently — do not report an error.

---

## Rebuild Knowledge Graph

After writing LEARNINGS.md, call the \`memory_graph\` tool with \`{ "mode": "build" }\` to rebuild the knowledge graph so the new learnings are immediately queryable by future milestone prompts.

If the \`memory_graph\` tool is not available, skip this step silently.
`;
}
export function buildFrontmatter(ctx) {
	const missingList =
		ctx.missingArtifacts.length > 0
			? ctx.missingArtifacts.map((a) => `  - ${a}`).join("\n")
			: "  []";
	const missingValue =
		ctx.missingArtifacts.length > 0 ? `\n${missingList}` : " []";
	return `---
phase: ${ctx.milestoneId}
phase_name: ${ctx.milestoneName}
project: ${ctx.projectName}
generated: ${ctx.generatedAt}
counts:
  decisions: ${ctx.counts.decisions}
  lessons: ${ctx.counts.lessons}
  patterns: ${ctx.counts.patterns}
  surprises: ${ctx.counts.surprises}
missing_artifacts:${missingValue}
---`;
}
export function extractProjectName(basePath) {
	const projectMdPath = join(sfRoot(basePath), "PROJECT.md");
	if (existsSync(projectMdPath)) {
		try {
			const content = readFileSync(projectMdPath, "utf-8");
			const match = content.match(/^name:\s*(.+)$/m);
			if (match) return match[1].trim();
		} catch {
			// non-fatal
		}
	}
	return basename(basePath);
}
// ─── Handler ──────────────────────────────────────────────────────────────────
export async function handleExtractLearnings(args, ctx, pi) {
	const { milestoneId } = parseExtractLearningsArgs(args);
	if (!milestoneId) {
		ctx.ui.notify(
			"Usage: /extract-learnings <milestoneId>  (e.g. M001)",
			"warning",
		);
		return;
	}
	// projectRoot() throws SFNoProjectError if no project found — intentional, handled by dispatcher
	const basePath = projectRoot();
	const milestoneDir = resolveMilestonePath(basePath, milestoneId);
	if (!milestoneDir) {
		ctx.ui.notify(`Milestone not found: ${milestoneId}`, "error");
		return;
	}
	const artifacts = resolvePhaseArtifacts(milestoneDir, milestoneId);
	if (artifacts.missingRequired.length > 0) {
		ctx.ui.notify(
			`Cannot extract learnings — required artefacts missing: ${artifacts.missingRequired.join(", ")}`,
			"error",
		);
		return;
	}
	// Read required artefacts
	const planContent = readFileSync(artifacts.plan, "utf-8");
	const summaryContent = readFileSync(artifacts.summary, "utf-8");
	// Read optional artefacts
	const verificationContent = artifacts.verification
		? readFileSync(artifacts.verification, "utf-8")
		: null;
	const uatContent = artifacts.uat
		? readFileSync(artifacts.uat, "utf-8")
		: null;
	// Determine missing optional artefacts for context
	const missingArtifacts = [];
	if (!artifacts.verification)
		missingArtifacts.push(`${milestoneId}-VERIFICATION.md`);
	if (!artifacts.uat) missingArtifacts.push(`${milestoneId}-UAT.md`);
	// Extract milestone name from Plan H1 or fall back to milestoneId
	const h1Match = planContent.match(/^#\s+(.+)$/m);
	const milestoneName = h1Match?.[1]?.trim() ?? milestoneId;
	const projectName = extractProjectName(basePath);
	const outputPath = buildLearningsOutputPath(milestoneDir, milestoneId);
	const relativeOutputPath = outputPath.replace(basePath + "/", "");
	const prompt = buildExtractLearningsPrompt({
		milestoneId,
		milestoneName,
		outputPath,
		relativeOutputPath,
		planContent,
		summaryContent,
		verificationContent,
		uatContent,
		missingArtifacts,
		projectName,
	});
	ctx.ui.notify(
		`Extracting learnings for ${milestoneId}: "${milestoneName}"...`,
		"info",
	);
	pi.sendMessage(
		{ customType: "sf-extract-learnings", content: prompt, display: false },
		{ triggerTurn: true },
	);
}
/**
 * Canonical structured-extraction instructions, shared by the manual
 * `/extract-learnings` path and the autonomous mode complete-milestone turn.
 */
export function buildExtractionStepsBlock(ctx) {
	return `## Structured Learnings Extraction

Perform the following steps IN ORDER. Each step is mandatory unless explicitly
marked optional. These instructions are the single source of truth shared by
\`/extract-learnings\` and the autonomous mode milestone-completion turn.

### Step 1 — Classify findings into four categories

Review the milestone artefacts (roadmap, slice summaries, verification report,
UAT report) and structure your findings into exactly four categories:

- **Decisions** — architectural or design choices made during this milestone, including rationale and alternatives considered.
- **Lessons** — technical discoveries, process insights, knowledge gaps that were filled.
- **Patterns** — reusable approaches or solutions that emerged and should be applied in future work.
- **Surprises** — unexpected challenges, discoveries, or outcomes that deviated from assumptions.

Every item MUST carry a \`Source:\` line using the format
\`Source: {artifact-filename}/{section}\` (e.g.
\`Source: ${ctx.milestoneId}-ROADMAP.md/Architecture Decisions\`).
Items without a source attribution are invalid — drop them.

### Step 2 — Write the LEARNINGS.md audit trail

Using the \`write\` tool, persist the full structured report to
\`${ctx.relativeOutputPath}\` with this shape:

- YAML frontmatter with keys: \`phase\`, \`phase_name\`, \`project\`, \`generated\` (ISO-8601 UTC), \`counts\` (decisions / lessons / patterns / surprises), \`missing_artifacts\`.
- Four H3 sections (\`### Decisions\`, \`### Lessons\`, \`### Patterns\`, \`### Surprises\`) containing bullet points. Each bullet is followed by its \`Source:\` line.

LEARNINGS.md is the full, cited audit trail. Write it first — subsequent steps
feed from its content.

### Step 3 — Optionally pre-query the memory store for semantic duplicates

Before persisting any extracted item in Steps 4–6, you may call
\`memory_search\` with 2–3 keywords from the item to check whether the
memory store already holds a semantically equivalent entry at high
confidence. Skip those items in their respective steps.

### Step 4 — Persist Patterns via \`log_reasoning\`

For each extracted Pattern, call \`log_reasoning\` exactly once with:
- \`category: "pattern"\`
- \`content\`: a 1–2 sentence restatement combining the Pattern, Where, and any non-obvious notes
- \`scope: "${ctx.milestoneId}"\`

### Step 5 — Persist Lessons via \`log_reasoning\`

For each extracted Lesson, call \`log_reasoning\` exactly once with:
- \`category: "gotcha"\` when the Lesson describes a pitfall, surprise root cause, or recurring failure mode; \`category: "convention"\` when it describes a project-wide rule or normative practice
- \`content\`: a 1–3 sentence restatement of What Happened + Root Cause + Fix
- \`scope: "${ctx.milestoneId}"\`

### Step 6 — Persist Decisions via \`log_reasoning\`

For each extracted Decision, call \`log_reasoning\` exactly once with:
- \`category: "architecture"\`
- \`content\`: a 1–3 sentence restatement combining decision + choice + rationale
- \`scope: "${ctx.milestoneId}"\`
- \`structuredFields\`: an object preserving the original decision schema

### Step 7 — Deduplication rule (applies to Steps 4, 5, 6)

Before each \`log_reasoning\` call, optionally call \`memory_search\` with 2–3
keywords from the entry. If a semantically equivalent memory is returned at
high confidence, skip the capture entirely.

### Step 8 — Surprises stay only in LEARNINGS.md

Surprises are milestone-local context and are NOT cross-session-reusable. Do
not persist them via \`log_reasoning\` or any other native memory tool.`;
}
