/**
 * Summary Helpers — extract and manage slice/task summary excerpts.
 *
 * Purpose: Consolidate logic for loading, parsing, and formatting summary files.
 * Separates summary extraction from prompt building, enabling testable reuse.
 *
 * Consumer: auto-prompts.js buildComplete* and buildReplan* functions.
 */

import { loadFile, parseSummary } from "./files.js";
import { relSlicePath, resolveTaskFiles, resolveTasksDir } from "./paths.js";
import { extractMarkdownSection } from "./workflow-helpers.js";

/**
 * Extract and format a slice summary as a compact excerpt.
 * Returns frontmatter + brief sections for milestone closers to reduce token load.
 * Falls back to full file if parsing fails (defensive).
 *
 * @param absPath Absolute path to SUMMARY.md
 * @param relPath Relative path for display
 * @param sid Slice ID
 * @returns Formatted markdown excerpt
 */
export async function buildSliceSummaryExcerpt(absPath, relPath, sid) {
	const header = `### ${sid} Summary (excerpt)\nSource: \`${relPath}\``;
	const content = absPath ? await loadFile(absPath) : null;
	if (!content) {
		return `${header}\n\n_(not found — file does not exist yet)_`;
	}
	try {
		const s = parseSummary(content);
		if (!s.frontmatter.id) {
			// Unrecognizable — fall back to full file so no context is lost.
			return `### ${sid} Summary\nSource: \`${relPath}\`\n\n${content.trim()}`;
		}
		const lines = [header, ""];
		if (s.title) lines.push(`**Title:** ${s.title}`);
		if (s.oneLiner) lines.push(`**One-liner:** ${s.oneLiner}`);
		if (s.frontmatter.verification_result) {
			lines.push(`**Verification:** \`${s.frontmatter.verification_result}\``);
		}
		lines.push(
			`**Blockers:** ${s.frontmatter.blocker_discovered ? "⚠️ blocker recorded — Read full summary" : "none"}`,
		);
		if (s.frontmatter.duration)
			lines.push(`**Duration:** ${s.frontmatter.duration}`);
		if (s.frontmatter.provides.length > 0)
			lines.push(`**Provides:** ${s.frontmatter.provides.join("; ")}`);
		if (s.frontmatter.affects.length > 0)
			lines.push(`**Affects:** ${s.frontmatter.affects.join("; ")}`);
		if (s.frontmatter.key_decisions.length > 0)
			lines.push(
				`**Key decisions:** ${s.frontmatter.key_decisions.join("; ")}`,
			);
		if (s.frontmatter.patterns_established.length > 0)
			lines.push(
				`**Patterns established:** ${s.frontmatter.patterns_established.join("; ")}`,
			);
		if (s.frontmatter.key_files.length > 0) {
			const files = s.frontmatter.key_files.slice(0, 8);
			const more =
				s.frontmatter.key_files.length > files.length
					? ` (+${s.frontmatter.key_files.length - files.length} more)`
					: "";
			lines.push(`**Key files:** ${files.join(", ")}${more}`);
		}
		// Cap section bodies: if any narrative sections balloon, excerpt mode
		// still inflates and undermines the token-reduction goal. 800 chars
		// (~200 tokens) is enough to carry intent; the closer agent Reads the
		// full file when it needs richer context for LEARNINGS synthesis.
		const SECTION_CAP_CHARS = 800;
		const capSection = (body) => {
			const trimmed = body.trim();
			if (trimmed.length <= SECTION_CAP_CHARS) return trimmed;
			return `${trimmed.slice(0, SECTION_CAP_CHARS)}\n… (truncated — see full \`${relPath}\`)`;
		};
		if (s.deviations && s.deviations.trim()) {
			lines.push("", "#### Deviations", capSection(s.deviations));
		}
		if (s.knownLimitations && s.knownLimitations.trim()) {
			lines.push("", "#### Known limitations", capSection(s.knownLimitations));
		}
		if (s.followUps && s.followUps.trim()) {
			lines.push("", "#### Follow-ups", capSection(s.followUps));
		}
		lines.push(
			"",
			`> **On-demand:** read \`${relPath}\` for the full "What Happened" narrative, integration notes, and detailed file-change list when drafting LEARNINGS, the Decision Re-evaluation table, or cross-slice synthesis.`,
		);
		return lines.join("\n");
	} catch {
		// Defensive — any parse failure falls back to full inline.
		return `### ${sid} Summary\nSource: \`${relPath}\`\n\n${content.trim()}`;
	}
}

/**
 * Get summary file paths for all prior tasks in sequence order.
 * Used by execute-task to provide context for the current task.
 *
 * @param mid Milestone ID
 * @param sid Slice ID
 * @param currentTid Current task ID (e.g., "T03")
 * @param base Project root
 * @returns Array of relative paths to SUMMARY.md files for prior tasks
 */
export async function getPriorTaskSummaryPaths(mid, sid, currentTid, base) {
	const tDir = resolveTasksDir(base, mid, sid);
	if (!tDir) return [];
	const summaryFiles = resolveTaskFiles(tDir, "SUMMARY");
	const currentNum = parseInt(currentTid.replace(/^T/, ""), 10);
	const sRel = relSlicePath(base, mid, sid);
	return summaryFiles
		.filter((f) => {
			const num = parseInt(f.replace(/^T/, ""), 10);
			return num < currentNum;
		})
		.map((f) => `${sRel}/tasks/${f}`);
}

/**
 * Get summary file paths scoped to a task's derived dependencies.
 * Returns only summaries for task IDs in dependsOn (not order-based siblings).
 * Used by reactive-execute to give each subagent only the context it needs.
 *
 * Falls back to order-based when dependsOn is empty (root tasks still get
 * any available prior summaries for continuity).
 *
 * @param mid Milestone ID
 * @param sid Slice ID
 * @param currentTid Current task ID
 * @param dependsOn Array of dependency task IDs
 * @param base Project root
 * @returns Array of relative paths to SUMMARY.md files for dependent tasks
 */
export async function getDependencyTaskSummaryPaths(
	mid,
	sid,
	currentTid,
	dependsOn,
	base,
) {
	// If no dependencies, fall back to order-based for root tasks
	if (dependsOn.length === 0) {
		return getPriorTaskSummaryPaths(mid, sid, currentTid, base);
	}
	const tDir = resolveTasksDir(base, mid, sid);
	if (!tDir) return [];
	const summaryFiles = resolveTaskFiles(tDir, "SUMMARY");
	const sRel = relSlicePath(base, mid, sid);
	const depSet = new Set(dependsOn.map((d) => d.toUpperCase()));
	return summaryFiles
		.filter((f) => {
			// Extract task ID from filename: "T02-SUMMARY.md" → "T02"
			const tid = f.replace(/-SUMMARY\.md$/i, "").toUpperCase();
			return depSet.has(tid);
		})
		.map((f) => `${sRel}/tasks/${f}`);
}

/**
 * Check if a summary is clean for skip (no blockers, no critical issues).
 * Used by replan logic to decide if a slice can be skipped in replan mode.
 *
 * @param content Summary file content
 * @returns true if summary is clean (safe to skip), false otherwise
 */
export function isSummaryCleanForSkip(content) {
	if (!content) return false;
	try {
		const s = parseSummary(content);
		// Unclean if any blockers were discovered
		if (s.frontmatter.blocker_discovered) return false;
		// Unclean if verification failed
		if (
			s.frontmatter.verification_result &&
			s.frontmatter.verification_result !== "passed"
		) {
			return false;
		}
		// Check for critical keywords in narrative
		const narrative =
			`${s.deviations} ${s.knownLimitations} ${s.followUps}`.toLowerCase();
		const criticalKeywords = [
			"critical",
			"blocker",
			"blocked",
			"fail",
			"error",
		];
		if (criticalKeywords.some((kw) => narrative.includes(kw))) {
			return false;
		}
		return true;
	} catch {
		return false;
	}
}

/**
 * Extract key sections from a slice PLAN.md for use in task execution prompts.
 * Returns Goal, Demo, Verification, and Observability sections as a compact excerpt.
 *
 * Purpose: give task executors the slice-level contract without inlining the full plan.
 * Consumer: auto-prompts.js buildExecuteTask*.
 */
export function extractSliceExecutionExcerpt(content, relPath) {
	if (!content) {
		return [
			"## Slice Plan Excerpt",
			`Slice plan not found at dispatch time. Read \`${relPath}\` before running slice-level verification.`,
		].join("\n");
	}
	const lines = content.split("\n");
	const goalLine = lines.find((line) => line.startsWith("**Goal:**"))?.trim();
	const demoLine = lines.find((line) => line.startsWith("**Demo:**"))?.trim();
	const verification = extractMarkdownSection(content, "Verification");
	const observability = extractMarkdownSection(
		content,
		"Observability / Diagnostics",
	);
	const parts = ["## Slice Plan Excerpt", `Source: \`${relPath}\``];
	if (goalLine) parts.push(goalLine);
	if (demoLine) parts.push(demoLine);
	if (verification)
		parts.push("", "### Slice Verification", verification.trim());
	if (observability)
		parts.push(
			"",
			"### Slice Observability / Diagnostics",
			observability.trim(),
		);
	return parts.join("\n");
}
