import { randomUUID } from "node:crypto";
import { debugLog } from "../debug-logger.js";
import {
	defaultPermissionProfileForRunControl,
	resolvePermissionProfile,
	resolveRunControlMode,
	runControlModeForSession,
} from "../operating-model.js";
import {
	isDbAvailable,
	recordUokRunExit,
	recordUokRunStart,
} from "../sf-db.js";
import { buildAuditEnvelope, emitUokAuditEvent } from "./audit.js";
import { setAuditEnvelopeEnabled } from "./audit-toggle.js";
import { writeUokDiagnostics } from "./diagnostic-synthesis.js";
import { resolveUokFlags } from "./flags.js";
import { createTurnObserver } from "./loop-adapter.js";
import {
	checkAndDrainMissingExit,
	resetParityCommitBlock,
	signalKernelEnter,
} from "./parity-diff-capture.js";
import {
	hasCurrentParityWarning,
	writeParityHeartbeat,
	writeParityReport,
} from "./parity-report.js";

function refreshParityReport(basePath) {
	try {
		return writeParityReport(basePath);
	} catch (err) {
		debugLog("uok-parity-report-write-failed", {
			error: err instanceof Error ? err.message : String(err),
		});
		return null;
	}
}
function resolveKernelPathLabel() {
	return "uok-kernel";
}
/**
 * Records an abnormal UOK kernel termination in both durable stores.
 *
 * Purpose: keep the DB-backed UOK run ledger and JSONL parity heartbeat
 * symmetrical when autonomous mode exits via signal and bypasses the async kernel
 * finally block.
 *
 * Consumer: auto signal cleanup and UOK parity tests.
 */
export function recordUokKernelTermination({
	basePath,
	runId,
	sessionId,
	flags,
	runControl,
	permissionProfile,
	status = "signal",
	error,
}) {
	const endedAt = new Date().toISOString();
	const lifecycleFlags = {
		...(flags ?? {}),
		...(runControl ? { runControl } : {}),
		...(permissionProfile ? { permissionProfile } : {}),
	};
	if (runId && isDbAvailable()) {
		recordUokRunExit({
			runId,
			sessionId,
			path: resolveKernelPathLabel(),
			flags: lifecycleFlags,
			status,
			endedAt,
			...(error ? { error } : {}),
		});
	}
	writeParityHeartbeat(basePath, {
		ts: endedAt,
		...(runId ? { runId } : {}),
		sessionId,
		path: resolveKernelPathLabel(),
		flags: lifecycleFlags,
		...(runControl ? { runControl } : {}),
		...(permissionProfile ? { permissionProfile } : {}),
		phase: "exit",
		status,
		...(error ? { error } : {}),
	});
	const report = refreshParityReport(basePath);
	try {
		writeUokDiagnostics(basePath);
	} catch (err) {
		debugLog("uok-diagnostics-write-failed", {
			error: err instanceof Error ? err.message : String(err),
		});
	}
	return report;
}
export async function runAutoLoopWithUok(args) {
	const { ctx, pi, s, deps, runKernelLoop } = args;
	const prefs = deps.loadEffectiveSFPreferences()?.preferences;
	const flags = { ...resolveUokFlags(prefs), enabled: true };
	const runControl = resolveRunControlMode(
		args.runControl ?? runControlModeForSession(s),
	);
	const permissionProfile = resolvePermissionProfile(
		args.permissionProfile ??
			prefs?.uok?.permission_profile ??
			defaultPermissionProfileForRunControl(runControl),
	);
	// Include workMode and modelMode from session in lifecycle flags
	const workMode = s.workMode ?? "chat";
	const modelMode = s.modelMode ?? "smart";
	const lifecycleFlags = {
		...flags,
		runControl,
		permissionProfile,
		workMode,
		modelMode,
	};

	const healthVerdict = writeUokDiagnostics(s.basePath);
	debugLog("uok-system-health-verdict", healthVerdict);

	const previousReport = refreshParityReport(s.basePath);
	const runId = `uok-${randomUUID()}`;
	s.currentUokRunId = runId;
	resetParityCommitBlock();
	if (
		previousReport &&
		previousReport.missingExitEvents > 0 &&
		hasCurrentParityWarning(previousReport)
	) {
		checkAndDrainMissingExit(
			previousReport.enterEvents,
			previousReport.exitEvents,
		);
	}
	setAuditEnvelopeEnabled(flags.auditEnvelope);
	signalKernelEnter();
	const startedAt = new Date().toISOString();
	if (isDbAvailable()) {
		recordUokRunStart({
			runId,
			sessionId: ctx.sessionManager?.getSessionId?.(),
			path: resolveKernelPathLabel(),
			flags: lifecycleFlags,
			startedAt,
		});
	}
	writeParityHeartbeat(s.basePath, {
		ts: startedAt,
		runId,
		sessionId: ctx.sessionManager?.getSessionId?.(),
		path: resolveKernelPathLabel(),
		flags: lifecycleFlags,
		runControl,
		permissionProfile,
		phase: "enter",
	});
	if (flags.auditEnvelope) {
		emitUokAuditEvent(
			s.basePath,
			buildAuditEnvelope({
				traceId: `session:${String(s.autoStartTime || Date.now())}`,
				category: "orchestration",
				type: "uok-kernel-enter",
				payload: {
					flags: lifecycleFlags,
					runControl,
					permissionProfile,
					workMode,
					modelMode,
					sessionId: ctx.sessionManager?.getSessionId?.(),
				},
			}),
		);
	}
	const decoratedDeps = {
		...deps,
		uokObserver: createTurnObserver({
			basePath: s.basePath,
			gitAction: flags.gitopsTurnAction,
			gitPush: flags.gitopsTurnPush,
			enableAudit: flags.auditEnvelope,
			enableGitops: flags.gitops,
			enableChaosMonkey: flags.chaosMonkey,
			runControl,
			permissionProfile,
		}),
		uokRunControl: runControl,
		uokPermissionProfile: permissionProfile,
	};
	let status = "ok";
	let error;
	try {
		await runKernelLoop(ctx, pi, s, decoratedDeps);
	} catch (err) {
		status = "error";
		error = err instanceof Error ? err.message : String(err);
		throw err;
	} finally {
		recordUokKernelTermination({
			basePath: s.basePath,
			runId,
			sessionId: ctx.sessionManager?.getSessionId?.(),
			flags: lifecycleFlags,
			runControl,
			permissionProfile,
			status,
			...(error ? { error } : {}),
		});
		if (s.currentUokRunId === runId) s.currentUokRunId = undefined;
	}
}
