/**
 * roadmap-json-projection.js - structured roadmap projection rendering.
 *
 * Purpose: keep DB-origin roadmap state machine-readable for export and
 * recovery while ROADMAP.md remains a human display artifact.
 */
import { mkdirSync } from "node:fs";
import { join } from "node:path";
import { atomicWriteSync } from "./atomic-write.js";
import { getDbPath } from "./sf-db.js";

function normalizeStringArray(value) {
	if (!Array.isArray(value)) return [];
	return value.filter((item) => typeof item === "string");
}
function normalizeObject(value) {
	return value && typeof value === "object" && !Array.isArray(value)
		? value
		: null;
}

function normalizeMilestone(milestoneRow) {
	return {
		id: String(milestoneRow.id),
		title: String(milestoneRow.title ?? ""),
		status: String(milestoneRow.status ?? ""),
		vision: String(milestoneRow.vision ?? ""),
		dependsOn: normalizeStringArray(
			milestoneRow.dependsOn ?? milestoneRow.depends_on,
		),
		successCriteria: normalizeStringArray(
			milestoneRow.successCriteria ?? milestoneRow.success_criteria,
		),
		definitionOfDone: normalizeStringArray(
			milestoneRow.definitionOfDone ?? milestoneRow.definition_of_done,
		),
		requirementCoverage: String(
			milestoneRow.requirementCoverage ??
				milestoneRow.requirement_coverage ??
				"",
		),
		boundaryMapMarkdown: String(
			milestoneRow.boundaryMapMarkdown ??
				milestoneRow.boundary_map_markdown ??
				"",
		),
		productResearch: normalizeObject(
			milestoneRow.productResearch ?? milestoneRow.product_research,
		),
	};
}

function normalizeSlice(sliceRow) {
	return {
		id: String(sliceRow.id),
		title: String(sliceRow.title ?? ""),
		status: String(sliceRow.status ?? ""),
		risk: String(sliceRow.risk ?? ""),
		depends: normalizeStringArray(sliceRow.depends),
		demo: String(sliceRow.demo ?? ""),
		goal: String(sliceRow.goal ?? ""),
		successCriteria: String(
			sliceRow.successCriteria ?? sliceRow.success_criteria ?? "",
		),
		proofLevel: String(sliceRow.proofLevel ?? sliceRow.proof_level ?? ""),
		integrationClosure: String(
			sliceRow.integrationClosure ?? sliceRow.integration_closure ?? "",
		),
		observabilityImpact: String(
			sliceRow.observabilityImpact ?? sliceRow.observability_impact ?? "",
		),
		isSketch: sliceRow.isSketch === true || sliceRow.is_sketch === 1,
		sketchScope: String(sliceRow.sketchScope ?? sliceRow.sketch_scope ?? ""),
		sequence: Number(sliceRow.sequence ?? 0),
	};
}

/**
 * Render structured ROADMAP.json content from database rows.
 *
 * Purpose: provide a deterministic DB projection for export/recovery without
 * making JSON a peer source of truth for normal runtime dispatch.
 *
 * Consumer: roadmap renderers invoked by planning and reassessment tools.
 */
export function renderRoadmapJsonProjectionContent(milestoneRow, sliceRows) {
	return JSON.stringify(
		{
			schemaVersion: 1,
			origin: "db-projection",
			generatedAt: new Date().toISOString(),
			sourceDbPath: getDbPath() ?? null,
			milestone: normalizeMilestone(milestoneRow),
			slices: sliceRows.map(normalizeSlice),
		},
		null,
		2,
	);
}

/**
 * Write Mxxx-ROADMAP.json beside the rendered Markdown roadmap.
 *
 * Purpose: keep human and machine projections refreshed in the same render
 * transaction boundary.
 *
 * Consumer: renderRoadmapFromDb and renderRoadmapProjection.
 */
export function writeRoadmapJsonProjection(
	basePath,
	milestoneId,
	milestoneRow,
	sliceRows,
) {
	const dir = join(basePath, ".sf", "milestones", milestoneId);
	mkdirSync(dir, { recursive: true });
	const content = `${renderRoadmapJsonProjectionContent(milestoneRow, sliceRows)}\n`;
	const path = join(dir, `${milestoneId}-ROADMAP.json`);
	atomicWriteSync(path, content);
	return { roadmapJsonPath: path, roadmapJsonContent: content };
}
