/**
 * uok/auto-uok-ctx.js — autonomous-loop helpers for building the
 * schema-v2 UOK run-context.
 *
 * Lives in uok/ so it sits next to run-context.js (its only collaborator)
 * and so the phase modules (auto/phases-*) can import it without setting
 * up a circular dependency between each other.
 *
 * Slice 3b of "Make UOK the SF Control Plane".
 */

import { UokGateRunner } from "./gate-runner.js";
import { buildUokRunContext } from "./run-context.js";

/**
 * Derive the schema-v2 UOK permissionProfile from session/prefs.
 *
 *   - "low" when YOLO mode is active or prefs.permissionLevel is "low"
 *     or "minimal" (sandboxed / read-only sessions).
 *   - "medium" when prefs.permissionLevel is "medium".
 *   - "high" otherwise (production / write-allowed — the default).
 *
 * Best-effort: if the session probe throws (test mocks, partial state),
 * we fall back to the prefs path. The function never throws — gate
 * emission must not break dispatch on a permission lookup.
 */
export function derivePermissionProfile(s, prefs) {
	try {
		if (typeof s?.isYolo === "function" && s.isYolo()) return "low";
	} catch {
		// session probe must never break dispatch
	}
	const level =
		typeof prefs?.permissionLevel === "string"
			? prefs.permissionLevel.toLowerCase()
			: undefined;
	if (level === "minimal" || level === "low") return "low";
	if (level === "medium") return "medium";
	return "high";
}

/**
 * Build the schema-v2 UOK run-context for an autonomous-loop phase.
 *
 * Wraps buildUokRunContext with the autonomous-surface defaults so each
 * phase module doesn't need to hardcode the same surface/runControl
 * strings. Callers pass the phase-specific traceId (e.g.
 * `pre-dispatch:<flowId>`, `guard:<flowId>`, `finalize:<flowId>`) and
 * any unit-level keys they already have.
 *
 * Returns null on invalid input (same contract as buildUokRunContext).
 * Callers should treat null as "no v2 ctx, fall through to legacy".
 *
 * @param {object} opts
 * @param {object} opts.s         Autonomous session state.
 * @param {object} [opts.prefs]   Operator preferences.
 * @param {string} opts.traceId   Phase-specific trace id.
 * @param {string} [opts.parentTrace]
 * @param {string} [opts.unitType]
 * @param {string} [opts.unitId]
 * @param {string} [opts.milestoneId]
 * @param {string} [opts.sliceId]
 * @param {string} [opts.taskId]
 */
export function buildAutonomousUokContext(opts) {
	if (!opts || typeof opts !== "object") return null;
	return buildUokRunContext({
		surface: "autonomous",
		runControl: "autonomous",
		permissionProfile: derivePermissionProfile(opts.s, opts.prefs),
		traceId: opts.traceId,
		parentTrace: opts.parentTrace,
		unitType: opts.unitType,
		unitId: opts.unitId,
		milestoneId: opts.milestoneId,
		sliceId: opts.sliceId,
		taskId: opts.taskId,
	});
}

/**
 * Emit a one-shot UOK gate via UokGateRunner, with the schema-v2
 * run-context already attached.
 *
 * Why this exists: the autonomous-loop phases register a gate that
 * just returns the verdict the phase already computed (it's a recording
 * gate, not a decision gate), then immediately invokes runner.run.
 * Centralizing that pattern means every call site forwards the same
 * surface/runControl/permissionProfile/parentTrace fields without
 * having to duplicate the 6-line setup, which is what was making the
 * phases drift to "legacy" before this slice.
 *
 * The optional UokGateRunner argument lets tests inject a mock; the
 * default constructs a fresh runner per emission (matches the
 * existing pre-dispatch pattern).
 *
 * @param {object} opts
 * @param {string} opts.basePath          .sf base path for trace writes.
 * @param {string} opts.gateId            Gate id (e.g. "pre-dispatch-health-gate").
 * @param {string} opts.gateType          UOK gate type ("policy", "execution", ...).
 * @param {string} opts.outcome           Decision: "pass" / "fail" / "manual-attention".
 * @param {string} [opts.failureClass]    Failure class when outcome != "pass".
 * @param {string} [opts.rationale]
 * @param {string} [opts.findings]
 * @param {string} opts.traceId           Phase trace id.
 * @param {string} opts.turnId            Per-iteration turn id.
 * @param {object} [opts.uokContext]      Schema-v2 ctx (from buildAutonomousUokContext).
 * @param {string} [opts.unitType]
 * @param {string} [opts.unitId]
 * @param {string} [opts.milestoneId]
 * @param {string} [opts.sliceId]
 * @param {string} [opts.taskId]
 * @param {UokGateRunner} [opts.runnerOverride] Test seam.
 *
 * @returns The runner's result object.
 */
export async function emitAutonomousGate(opts) {
	const runner = opts.runnerOverride ?? new UokGateRunner();
	runner.register({
		id: opts.gateId,
		type: opts.gateType,
		execute: async () => ({
			outcome: opts.outcome,
			failureClass: opts.failureClass,
			rationale: opts.rationale,
			findings: opts.findings ?? "",
		}),
	});
	return runner.run(opts.gateId, {
		basePath: opts.basePath,
		traceId: opts.traceId,
		turnId: opts.turnId,
		milestoneId: opts.milestoneId,
		unitType: opts.unitType,
		unitId: opts.unitId,
		sliceId: opts.sliceId,
		taskId: opts.taskId,
		surface: opts.uokContext?.surface,
		runControl: opts.uokContext?.runControl,
		permissionProfile: opts.uokContext?.permissionProfile,
		parentTrace: opts.uokContext?.parentTrace,
	});
}
