/**
 * @internal
 * consistency/parse-spec.mjs — extract ACs + dimension claims from spec.md (C15).
 *
 * Pure: takes a markdown string, returns a normalized shape. Never touches
 * the filesystem. Tolerant of missing / empty input.
 *
 * Output shape:
 *   {
 *     acs: [{id: 'AC<n>', text: string, line: number}],
 *     dimensions_claimed: string[]   // lowercase keywords observed in spec
 *   }
 *
 * Strategy:
 *   - Locate the `## Acceptance Criteria` heading (case-insensitive, any level
 *     >= 2). Collect numbered list items (`1. ...`) until the next heading of
 *     equal or shallower depth.
 *   - Assign sequential ids AC1..ACn matching the source order.
 *   - Accept an explicit `**AC7**` prefix inside a bullet for forward-compat.
 *   - Collect dimension claim keywords from the "Dimensions Crossed" /
 *     "Deployment Modes" section and from the three-mode obligation keywords
 *     (desktop|electron|libsql|convex|self-hosted|cloud).
 */

const DIMENSION_KEYWORDS = ['desktop', 'electron', 'libsql', 'convex', 'self-hosted', 'cloud']

const DIMENSION_SECTION_RE = /^(#+)\s*(dimensions\s+crossed|deployment\s+modes)\b/i

/**
 * Parse a spec.md string.
 *
 * @param {string | null | undefined} md
 * @returns {{acs: Array<{id: string, text: string, line: number}>, dimensions_claimed: string[]}}
 */
export function parseSpec(md) {
	if (typeof md !== 'string' || md.length === 0) {
		return { acs: [], dimensions_claimed: [] }
	}

	const lines = md.split(/\r?\n/)
	const acs = collectAcs(lines)
	const dimensions_claimed = collectDimensions(md, lines)
	return { acs, dimensions_claimed }
}

function collectAcs(lines) {
	// Find the first "## Acceptance Criteria" heading (level >= 2).
	let headingIdx = -1
	let headingLevel = 0
	for (let i = 0; i < lines.length; i++) {
		const m = lines[i].match(/^(#{2,})\s*acceptance\s+criteria\s*$/i)
		if (m) {
			headingIdx = i
			headingLevel = m[1].length
			break
		}
	}
	if (headingIdx === -1) return []

	const acs = []
	for (let i = headingIdx + 1; i < lines.length; i++) {
		const line = lines[i]
		const h = line.match(/^(#{1,6})\s+/)
		if (h && h[1].length <= headingLevel) break

		// Match numbered list items `1. ...` at start of line (allow leading spaces).
		const numMatch = line.match(/^\s*(\d+)\.\s+(.*)$/)
		if (!numMatch) continue
		let text = numMatch[2].trim()
		// Strip explicit **AC7** prefix if present.
		const explicit = text.match(/^\*\*AC(\d+)\*\*\s*[:-]?\s*(.*)$/i)
		let id
		if (explicit) {
			id = `AC${explicit[1]}`
			text = explicit[2].trim()
		} else {
			id = `AC${acs.length + 1}`
		}
		acs.push({ id, text, line: i + 1 })
	}
	return acs
}

function collectDimensions(md, lines) {
	const claimed = new Set()
	const lower = md.toLowerCase()
	for (const kw of DIMENSION_KEYWORDS) {
		if (lower.includes(kw)) claimed.add(kw)
	}
	// Detect explicit "Dimensions Crossed" / "Deployment Modes" section.
	for (let i = 0; i < lines.length; i++) {
		if (DIMENSION_SECTION_RE.test(lines[i])) {
			claimed.add('__has_dimensions_section__')
			break
		}
	}
	return Array.from(claimed)
}
