/**
 * @internal
 * consistency/rules/r3-dimension-claims.mjs — R3 (C15).
 *
 * For every axis where plan.dimensions_covered[axis].crossed === true, the
 * spec.md must cite at least one keyword from that axis's values (or have
 * a "Dimensions Crossed" / "Deployment Modes" section). Warn-severity.
 *
 * Stages: plan, execute, verify, ship.
 * Skip when: plan has no dimensions_covered entries.
 */

import { registerRule } from '../registry.mjs'

const rule = {
	id: 'r3-dimension-claims',
	severity: 'warn',
	stages: ['plan', 'execute', 'verify', 'ship'],
	description: 'Every crossed dimension is cited in spec.md.',
	async check({ parsed }) {
		const dims = parsed?.plan?.dimensions_covered ?? {}
		const axes = Object.keys(dims)
		if (axes.length === 0) {
			return { status: 'skip', drift: [], reason: 'plan has no dimensions_covered' }
		}
		const claimed = new Set((parsed?.spec?.dimensions_claimed ?? []).map((k) => k.toLowerCase()))
		const specTextLower = (parsed?.spec?.rawLower ?? '').toString()
		const drift = []
		let crossedCount = 0
		for (const axis of axes) {
			const entry = dims[axis]
			if (!entry?.crossed) continue
			crossedCount++
			const keywords = extractKeywords(axis, entry)
			if (keywords.length === 0) continue // nothing to check
			const found = keywords.some(
				(k) => claimed.has(k.toLowerCase()) || specTextLower.includes(k.toLowerCase()),
			)
			if (!found) {
				drift.push({
					axis,
					expected_keywords: keywords,
					reason: 'spec.md has no evidence',
				})
			}
		}
		if (crossedCount === 0) {
			return { status: 'skip', drift: [], reason: 'no dimensions marked crossed' }
		}
		if (drift.length > 0) {
			return {
				status: 'fail',
				drift,
				reason: `${drift.length} crossed dimension(s) not cited in spec.md`,
			}
		}
		return {
			status: 'pass',
			drift: [],
			reason: `all ${crossedCount} crossed dimension(s) cited`,
		}
	},
}

function extractKeywords(axis, entry) {
	const out = new Set()
	out.add(axis)
	const values = entry?.values
	if (Array.isArray(values)) {
		for (const v of values) if (typeof v === 'string') out.add(v)
	} else if (values && typeof values === 'object') {
		for (const k of Object.keys(values)) out.add(k)
	}
	return Array.from(out)
}

registerRule(rule)
export default rule
