/**
 * design/validate.mjs — C2 DESIGN.md validator (pure fn).
 *
 * Grep-extracts the leading YAML frontmatter block of DESIGN.md, parses it
 * with `yaml`, and validates the result against `DesignFrontmatterSchema`.
 * Mirrors `skill-author/validate.mjs` so call sites share a surface.
 *
 * We do NOT inspect the prose body. The 9-section Stitch-style narrative is
 * informational for humans and for generation prompts — it carries no
 * machine-consumed facts that aren't already in the frontmatter.
 */

import { parse as parseYaml } from 'yaml'
import { validateDesignFrontmatter } from './frontmatter-schema.mjs'

const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n/

/**
 * @typedef {{ ok: boolean, errors: string[], warnings: string[] }} ValidationResult
 */

/**
 * Validate a DESIGN.md text against the canonical frontmatter schema.
 *
 * @param {string} text — full DESIGN.md contents, including frontmatter
 * @returns {ValidationResult}
 */
export function validateDesignMarkdown(text) {
	const errors = []
	const warnings = []

	if (typeof text !== 'string' || text.length === 0) {
		return { ok: false, errors: ['DESIGN.md is empty or not a string'], warnings }
	}

	const fmMatch = text.match(FRONTMATTER_RE)
	if (!fmMatch) {
		errors.push('missing YAML frontmatter block (must start with `---\\n...\\n---\\n`)')
		return { ok: false, errors, warnings }
	}

	let parsed
	try {
		parsed = parseYaml(fmMatch[1])
	} catch (err) {
		errors.push(`frontmatter YAML parse error: ${(err && err.message) || String(err)}`)
		return { ok: false, errors, warnings }
	}

	if (
		parsed === null ||
		parsed === undefined ||
		typeof parsed !== 'object' ||
		Array.isArray(parsed)
	) {
		errors.push('frontmatter must be a YAML object')
		return { ok: false, errors, warnings }
	}

	const res = validateDesignFrontmatter(parsed)
	if (!res.success) {
		for (const issue of res.error.issues) {
			const pathStr = issue.path && issue.path.length > 0 ? issue.path.join('.') : '(root)'
			errors.push(`frontmatter.${pathStr}: ${issue.message}`)
		}
		return { ok: false, errors, warnings }
	}

	// Soft checks (warnings only). Match numbered Stitch sections too ("## 2. Color Palette")
	// so scaffolds with prose ordering still pass.
	if (!/##\s+(?:\d+\.\s+)?Color(?:\s+Palette|s)\b/.test(text)) {
		warnings.push(
			'body: no "## Color Palette" section found — Stitch 9-section convention recommends one',
		)
	}
	if (!/##\s+(?:\d+\.\s+)?Typography\b/.test(text)) {
		warnings.push(
			'body: no "## Typography" section found — Stitch 9-section convention recommends one',
		)
	}

	return { ok: true, errors, warnings }
}

/**
 * Extract the frontmatter object from a DESIGN.md string.
 *
 * Callers that want the parsed tokens (not just ok/errors) can invoke this.
 * Returns `null` if the file has no parseable frontmatter.
 *
 * @param {string} text
 * @returns {Record<string, unknown> | null}
 */
export function extractDesignFrontmatter(text) {
	if (typeof text !== 'string' || text.length === 0) return null
	const m = text.match(FRONTMATTER_RE)
	if (!m) return null
	try {
		const parsed = parseYaml(m[1])
		if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
			return /** @type {Record<string, unknown>} */ (parsed)
		}
	} catch {
		return null
	}
	return null
}
