/**
 * execution-policy.js — internal execution permission profiles for SF tool calls.
 *
 * Purpose: give autonomous run control and the machine surface a Codex/Crush-style
 * vocabulary for filesystem, network, git, and mutation posture before those
 * policies are enforced at every tool boundary.
 *
 * Consumer: write-gate, autonomous run-control hooks, machine-surface event DTOs,
 * and future DB evidence rows that need stable permission-profile names instead
 * of ad hoc booleans.
 */

import { shouldBlockQueueExecutionInSnapshot } from "./bootstrap/write-gate.js";
import { resolvePermissionProfile } from "./operating-model.js";
import { classifyCommand } from "./safety/destructive-guard.js";

export const EXECUTION_POLICY_PROFILES = {
	restricted: {
		id: "restricted",
		permissionProfile: "restricted",
		filesystem: "read-mostly",
		network: "read-only",
		git: "read-only",
		mutation: "planning-artifacts-only",
	},
	normal: {
		id: "normal",
		permissionProfile: "normal",
		filesystem: "workspace-write",
		network: "allowed",
		git: "normal",
		mutation: "workspace",
	},
	trusted: {
		id: "trusted",
		permissionProfile: "trusted",
		filesystem: "workspace-write",
		network: "allowed",
		git: "normal",
		mutation: "workspace",
	},
	unrestricted: {
		id: "unrestricted",
		permissionProfile: "unrestricted",
		filesystem: "danger-full-access",
		network: "allowed",
		git: "dangerous",
		mutation: "host",
	},
};

/**
 * Resolve a named execution policy profile.
 *
 * Purpose: normalize unknown or missing policy names to the safest default
 * profile so callers can fail closed.
 *
 * Consumer: tool-call classifiers and future typed headless events.
 */
export function resolveExecutionPolicyProfile(profileId) {
	const id = resolvePermissionProfile(profileId);
	return EXECUTION_POLICY_PROFILES[id] ?? EXECUTION_POLICY_PROFILES.restricted;
}

function classifyBashRisk(command) {
	const destructive = classifyCommand(command);
	if (destructive.destructive) {
		return { risk: "destructive", labels: destructive.labels };
	}
	if (
		/\b(git\s+push|npm\s+publish|docker\s+push|kubectl\s+apply|terraform\s+apply)\b/.test(
			command,
		)
	) {
		return { risk: "external-mutation", labels: [] };
	}
	if (/\b(curl|wget|fetch|gh\s+api)\b/.test(command)) {
		return { risk: "network", labels: [] };
	}
	return { risk: "normal", labels: [] };
}

/**
 * Classify a tool call under a named execution profile.
 *
 * Purpose: produce deterministic allow/deny/risk metadata before tool calls are
 * executed, without coupling policy consumers to queue-mode implementation
 * details.
 *
 * Consumer: auto/headless safety gates and execution evidence writers.
 */
export function classifyExecutionPolicyCall(profileId, toolName, input = "") {
	const profile = resolveExecutionPolicyProfile(profileId);
	if (profile.id === "restricted") {
		const queueDecision = shouldBlockQueueExecutionInSnapshot(
			{
				verifiedDepthMilestones: [],
				activeQueuePhase: true,
				pendingGateId: null,
			},
			toolName,
			input,
			true,
		);
		if (queueDecision.block) {
			return {
				profile: profile.id,
				permissionProfile: profile.permissionProfile,
				allowed: false,
				reason: queueDecision.reason,
				risk: "mutation",
				destructiveLabels: [],
			};
		}
	}
	const bashRisk =
		toolName === "Bash"
			? classifyBashRisk(input)
			: { risk: "normal", labels: [] };
	const allowed =
		profile.id === "unrestricted" ||
		bashRisk.risk !== "destructive" ||
		profile.mutation !== "planning-artifacts-only";
	return {
		profile: profile.id,
		permissionProfile: profile.permissionProfile,
		allowed,
		reason: allowed ? "allowed" : "destructive command requires approval",
		risk: bashRisk.risk,
		destructiveLabels: bashRisk.labels,
	};
}

/**
 * Extract the policy-relevant input string from a tool-call event.
 *
 * Purpose: avoid leaking full structured tool payloads into policy logs while
 * preserving the command or path needed for deterministic classification.
 *
 * Consumer: execution-policy journal events.
 */
export function extractExecutionPolicyInput(toolName, input) {
	if (!input || typeof input !== "object") return "";
	if (toolName === "Bash") return String(input.command ?? "");
	if (toolName === "Write" || toolName === "Edit") {
		return String(input.path ?? "");
	}
	return "";
}

/**
 * Build a journal entry for a tool-call execution-policy decision.
 *
 * Purpose: make policy decisions inspectable before broad enforcement, matching
 * the comparison-survey direction from Codex/Crush without changing runtime
 * permissions in this slice.
 *
 * Consumer: SF autonomous mode tool_call hook.
 */
export function buildExecutionPolicyJournalEntry(args) {
	const input = extractExecutionPolicyInput(
		args.event.toolName,
		args.event.input,
	);
	const decision = classifyExecutionPolicyCall(
		args.profileId,
		args.event.toolName,
		input,
	);
	return {
		ts: args.now?.() ?? new Date().toISOString(),
		flowId:
			args.flowId ??
			`execution-policy:${args.event.toolCallId ?? args.event.toolName}`,
		seq: 0,
		eventType: "execution-policy-decision",
		unitType: args.unit?.type,
		unitId: args.unit?.id,
		data: {
			toolCallId: args.event.toolCallId,
			toolName: args.event.toolName,
			input,
			decision,
		},
	};
}
