/**
 * @internal
 * gate/registry.mjs — first-class gate subsystem (C2).
 *
 * Gates are composable, deterministic predicates that a skill MUST satisfy
 * before it may do any other work. Each registered gate exports:
 *
 *   {
 *     id:          string,                // e.g. "plan-exists"
 *     description: string,                // human-readable one-liner
 *     severity:    'blocking' | 'warn',   // blocking gates halt the skill
 *     skills:      string[],              // skills this gate fires for
 *     check:       async (ctx) => GateVerdict,
 *     applies_to_profile?: (ctx) => boolean // optional; when present and
 *                                         // returns false, the gate is
 *                                         // skipped before cache lookup or
 *                                         // check() invocation. Omit to
 *                                         // apply universally (default).
 *   }
 *
 * A GateVerdict is:
 *
 *   {
 *     status:   'pass' | 'fail' | 'warn' | 'skip',
 *     reason:   string,
 *     evidence?: object,                  // gate-specific structured data
 *     inputs?:  string[]                  // absolute paths the gate read
 *                                         // (used as cache keys in cache.mjs)
 *   }
 *
 * The registry is a module-scope singleton. Tests can reset it via
 * `__resetRegistryForTests()`. Built-in gates self-register through the
 * `gates/index.mjs` barrel, which is imported eagerly by the CLI dispatcher.
 */

const registry = new Map()

/**
 * Register a gate. Re-registering the same id replaces the previous entry
 * (supports hot-reload in tests and monkey-patching).
 *
 * @internal
 */
export function registerGate(gate) {
	if (!gate || typeof gate !== 'object') {
		throw new Error('registerGate: gate must be an object')
	}
	if (typeof gate.id !== 'string' || !gate.id) {
		throw new Error('registerGate: gate.id must be a non-empty string')
	}
	if (typeof gate.description !== 'string') {
		throw new Error(`registerGate(${gate.id}): description must be a string`)
	}
	if (gate.severity !== 'blocking' && gate.severity !== 'warn') {
		throw new Error(
			`registerGate(${gate.id}): severity must be "blocking" or "warn" (got ${gate.severity})`,
		)
	}
	if (!Array.isArray(gate.skills) || gate.skills.length === 0) {
		throw new Error(`registerGate(${gate.id}): skills must be a non-empty array`)
	}
	if (typeof gate.check !== 'function') {
		throw new Error(`registerGate(${gate.id}): check must be a function`)
	}
	if (gate.applies_to_profile !== undefined && typeof gate.applies_to_profile !== 'function') {
		throw new Error(
			`registerGate(${gate.id}): applies_to_profile, when present, must be a function`,
		)
	}
	registry.set(gate.id, gate)
	return gate
}

/**
 * List all registered gates, optionally filtered to those that fire for a
 * specific skill. Returns a shallow copy so callers can safely mutate.
 *
 * @internal
 */
export function listGates({ skill } = {}) {
	const all = Array.from(registry.values())
	if (!skill) return all
	return all.filter((g) => g.skills.includes(skill))
}

/**
 * Lookup gates that fire for a specific skill. Convenience wrapper over
 * listGates({ skill }).
 *
 * @internal
 */
export function getGatesForSkill(skill) {
	return listGates({ skill })
}

/**
 * Lookup a single gate by id. Returns undefined when unknown.
 *
 * @internal
 */
export function getGate(id) {
	return registry.get(id)
}

/**
 * Test-only helper — drop every registered gate so a fresh import sequence
 * starts from an empty registry. Not part of the public surface.
 *
 * @internal
 */
export function __resetRegistryForTests() {
	registry.clear()
}

/**
 * Test-only helper — uniformly-named alias for the in-process cmd harness
 * (run-cmd.mjs). Built-in gates self-register via the `gates/index.mjs`
 * barrel at module-import time, which ESM evaluates exactly once per
 * process — so the harness deliberately does NOT call this by default
 * (clearing here would leave subsequent cmd invocations looking at an empty
 * registry). Tests that need an empty registry import this name explicitly.
 *
 * @internal
 */
export function __resetCaches() {
	registry.clear()
}
