import { i as createPublicClient, n as http } from "../../_esm-BrzUp3yp.mjs";
import { a as mainnet, c as base, i as optimism, l as arbitrum, n as sepolia, o as linea, r as polygon, s as baseSepolia } from "../../chains-DtOuO1lR.mjs";
import { isDelegationMode } from "./policy-types.mjs";
import { getPolicyStore } from "./policy-store.mjs";
import { DELEGATION_CONTRACTS, ERC20_PERIOD_ENFORCER_ABI, LIMITED_CALLS_ENFORCER_ABI, NATIVE_PERIOD_ENFORCER_ABI } from "./delegation-types.mjs";
import { getDelegationStore } from "./delegation-store.mjs";
import { getDelegatedPolicies } from "./delegation-service.mjs";
//#region extensions/crypto/src/services/delegation-monitor.ts
/**
* Delegation Monitor
*
* Tracks remaining budget, expiry, and health of active delegations.
* Emits warnings when:
*   - A delegation is within 20% of its spending limit
*   - A delegation expires within 24 hours
*   - A delegation has been revoked on-chain
*
* The monitor runs periodically via the event bus / heartbeat service,
* or can be invoked on-demand via /delegate status.
*/
/** Warn when usage exceeds this percentage of limit. */
const SPENDING_WARN_THRESHOLD = .8;
/** Critical when usage exceeds this percentage. */
const SPENDING_CRITICAL_THRESHOLD = .95;
/** Warn when delegation expires within this many seconds. */
const EXPIRY_WARN_SEC = 86400;
/** Critical when delegation expires within this many seconds. */
const EXPIRY_CRITICAL_SEC = 3600;
/**
* Check all active delegations for the given user and return health + alerts.
*/
function checkDelegations(userId) {
	if (!isDelegationMode()) return {
		health: [],
		alerts: []
	};
	const policies = getDelegatedPolicies(userId);
	const store = getPolicyStore();
	const health = [];
	const alerts = [];
	for (const policy of policies) {
		const info = policy.delegation;
		if (!info) continue;
		const h = buildHealth(policy, info, store, userId);
		health.push(h);
		const policyAlerts = generateAlerts(h, policy.name);
		alerts.push(...policyAlerts);
	}
	return {
		health,
		alerts
	};
}
/**
* Get a formatted summary of delegation health for display.
*/
function formatDelegationHealth(userId) {
	const { health, alerts } = checkDelegations(userId);
	if (health.length === 0) return "No active delegations to monitor.";
	const lines = [];
	lines.push("**Delegation Health**");
	lines.push("");
	for (const h of health) {
		const statusIcon = getStatusIcon(h.status);
		lines.push(`${statusIcon} **${h.policyName}** (chain ${h.chainId})`);
		if (h.usagePercent !== null && h.limitUsd !== null) {
			const bar = renderProgressBar(h.usagePercent);
			lines.push(`  Spending: $${(h.spentUsd ?? 0).toFixed(0)} / $${h.limitUsd.toFixed(0)} ${bar}`);
		}
		if (h.actionsUsed !== null && h.actionsLimit !== null) lines.push(`  Actions: ${h.actionsUsed} / ${h.actionsLimit}`);
		if (h.expiresInSec !== null) lines.push(`  Expires: ${formatDuration(h.expiresInSec)}`);
		lines.push("");
	}
	if (alerts.length > 0) {
		lines.push("---");
		lines.push("");
		for (const a of alerts) {
			const icon = a.severity === "critical" ? "!!" : a.severity === "warning" ? "!" : "-";
			lines.push(`[${icon}] ${a.message}`);
			lines.push(`    ${a.action}`);
		}
	}
	return lines.join("\n");
}
function buildHealth(policy, info, store, userId) {
	let spentUsd = null;
	let limitUsd = null;
	let usagePercent = null;
	let expiresInSec = null;
	let actionsUsed = null;
	let actionsLimit = null;
	for (const rule of policy.rules) {
		if (rule.type === "spending_limit") {
			limitUsd = rule.maxAmountUsd;
			const periodMs = getPeriodMs(rule.period);
			spentUsd = store.getSpendInWindow(userId, policy.id, periodMs);
			usagePercent = limitUsd > 0 ? spentUsd / limitUsd * 100 : null;
		}
		if (rule.type === "rate_limit") {
			actionsLimit = rule.maxCalls;
			actionsUsed = store.getCallsInWindow(userId, policy.id, rule.periodMs);
		}
	}
	if (info.expiresAt) {
		const expiryMs = new Date(info.expiresAt).getTime();
		if (!isNaN(expiryMs)) {
			const remainingMs = expiryMs - Date.now();
			expiresInSec = Math.max(0, Math.floor(remainingMs / 1e3));
		}
	}
	return {
		policyId: policy.id,
		policyName: policy.name,
		chainId: info.chainId,
		status: info.status,
		spentUsd,
		limitUsd,
		usagePercent,
		expiresInSec,
		actionsUsed,
		actionsLimit
	};
}
function generateAlerts(h, policyName) {
	const alerts = [];
	if (h.status === "revoked") alerts.push({
		severity: "critical",
		policyId: h.policyId,
		policyName,
		chainId: h.chainId,
		message: `Delegation "${policyName}" has been revoked on-chain.`,
		action: "Create a new delegation with `/delegate create`."
	});
	if (h.status === "expired") alerts.push({
		severity: "critical",
		policyId: h.policyId,
		policyName,
		chainId: h.chainId,
		message: `Delegation "${policyName}" has expired.`,
		action: "Create a new delegation with `/delegate create`."
	});
	if (h.usagePercent !== null) {
		if (h.usagePercent >= SPENDING_CRITICAL_THRESHOLD * 100) alerts.push({
			severity: "critical",
			policyId: h.policyId,
			policyName,
			chainId: h.chainId,
			message: `Spending at ${h.usagePercent.toFixed(0)}% of limit for "${policyName}".`,
			action: "Consider increasing the limit or creating a new delegation."
		});
		else if (h.usagePercent >= SPENDING_WARN_THRESHOLD * 100) alerts.push({
			severity: "warning",
			policyId: h.policyId,
			policyName,
			chainId: h.chainId,
			message: `Spending at ${h.usagePercent.toFixed(0)}% of limit for "${policyName}".`,
			action: "Monitor spending or adjust limits."
		});
	}
	if (h.actionsUsed !== null && h.actionsLimit !== null && h.actionsLimit > 0) {
		const pct = h.actionsUsed / h.actionsLimit * 100;
		if (pct >= SPENDING_CRITICAL_THRESHOLD * 100) alerts.push({
			severity: "critical",
			policyId: h.policyId,
			policyName,
			chainId: h.chainId,
			message: `${h.actionsUsed}/${h.actionsLimit} actions used for "${policyName}".`,
			action: "Create a new delegation to continue operating."
		});
		else if (pct >= SPENDING_WARN_THRESHOLD * 100) alerts.push({
			severity: "warning",
			policyId: h.policyId,
			policyName,
			chainId: h.chainId,
			message: `${h.actionsUsed}/${h.actionsLimit} actions used for "${policyName}".`,
			action: "Approaching action limit."
		});
	}
	if (h.expiresInSec !== null) {
		if (h.expiresInSec <= EXPIRY_CRITICAL_SEC) alerts.push({
			severity: "critical",
			policyId: h.policyId,
			policyName,
			chainId: h.chainId,
			message: `Delegation "${policyName}" expires in ${formatDuration(h.expiresInSec)}.`,
			action: "Renew immediately with `/delegate create`."
		});
		else if (h.expiresInSec <= EXPIRY_WARN_SEC) alerts.push({
			severity: "warning",
			policyId: h.policyId,
			policyName,
			chainId: h.chainId,
			message: `Delegation "${policyName}" expires in ${formatDuration(h.expiresInSec)}.`,
			action: "Plan renewal with `/delegate create`."
		});
	}
	return alerts;
}
function getStatusIcon(status) {
	switch (status) {
		case "active": return "[OK]";
		case "signed": return "[--]";
		case "revoked": return "[!!]";
		case "expired": return "[!!]";
		case "unsigned": return "[..]";
		default: return "[??]";
	}
}
function renderProgressBar(percent) {
	const width = 10;
	const filled = Math.min(width, Math.round(percent / 100 * width));
	const empty = width - filled;
	return `[${"#".repeat(filled)}${"-".repeat(empty)}]`;
}
function formatDuration(seconds) {
	if (seconds <= 0) return "expired";
	if (seconds < 60) return `${seconds}s`;
	if (seconds < 3600) return `${Math.round(seconds / 60)}m`;
	if (seconds < 86400) return `${Math.round(seconds / 3600)}h`;
	return `${Math.round(seconds / 86400)}d`;
}
function getPeriodMs(period) {
	switch (period) {
		case "hourly": return 3600 * 1e3;
		case "daily": return 1440 * 60 * 1e3;
		case "weekly": return 10080 * 60 * 1e3;
		case "monthly": return 720 * 60 * 60 * 1e3;
		default: return 1440 * 60 * 1e3;
	}
}
const CHAIN_CONFIGS = {
	1: mainnet,
	8453: base,
	42161: arbitrum,
	10: optimism,
	137: polygon,
	59144: linea,
	11155111: sepolia,
	84532: baseSepolia
};
const _clientCache = /* @__PURE__ */ new Map();
function getMonitorClient(chainId) {
	let client = _clientCache.get(chainId);
	if (client) return client;
	const chain = CHAIN_CONFIGS[chainId];
	if (!chain) return null;
	client = createPublicClient({
		chain,
		transport: http()
	});
	_clientCache.set(chainId, client);
	return client;
}
/**
* Read on-chain enforcer state for a delegation.
*
* Queries the enforcer contracts that track cumulative usage:
* - NativeTokenPeriodTransferEnforcer.spentMap → ETH spent
* - ERC20PeriodTransferEnforcer.spentMap → ERC-20 spent
* - LimitedCallsEnforcer.callCounts → call count
*
* Compares on-chain state with local tracking and flags drift.
* All reads are best-effort: RPC errors are caught silently.
*/
async function readOnChainUsage(policyId, delegationHash, chainId, localHealth) {
	const client = getMonitorClient(chainId);
	const result = {
		nativeSpentWei: null,
		erc20Spent: null,
		callCount: null,
		driftDetected: false,
		queriedAt: Date.now()
	};
	if (!client || !delegationHash || delegationHash === "0x") return result;
	const dmAddr = DELEGATION_CONTRACTS.DelegationManager;
	try {
		const [spent] = await client.readContract({
			address: DELEGATION_CONTRACTS.NativeTokenPeriodTransferEnforcer,
			abi: NATIVE_PERIOD_ENFORCER_ABI,
			functionName: "spentMap",
			args: [dmAddr, delegationHash]
		});
		result.nativeSpentWei = spent;
	} catch {}
	try {
		const [spent] = await client.readContract({
			address: DELEGATION_CONTRACTS.ERC20PeriodTransferEnforcer,
			abi: ERC20_PERIOD_ENFORCER_ABI,
			functionName: "spentMap",
			args: [dmAddr, delegationHash]
		});
		result.erc20Spent = spent;
	} catch {}
	try {
		result.callCount = await client.readContract({
			address: DELEGATION_CONTRACTS.LimitedCallsEnforcer,
			abi: LIMITED_CALLS_ENFORCER_ABI,
			functionName: "callCounts",
			args: [dmAddr, delegationHash]
		});
	} catch {}
	result.driftDetected = false;
	const driftNotes = [];
	if (result.callCount !== null && localHealth.actionsUsed !== null) {
		const onChainCalls = Number(result.callCount);
		if (onChainCalls !== localHealth.actionsUsed) driftNotes.push(`calls: local=${localHealth.actionsUsed}, on-chain=${onChainCalls}`);
	}
	if (result.nativeSpentWei !== null && result.nativeSpentWei > 0n) {
		if (localHealth.spentUsd === null || localHealth.spentUsd === 0) driftNotes.push("on-chain shows ETH spending but local tracking is empty");
	}
	if (driftNotes.length > 0) {
		result.driftDetected = true;
		result.driftDetails = driftNotes.join("; ");
	}
	return result;
}
/**
* Enhanced health check that includes on-chain state reads.
* Slower than checkDelegations (makes RPC calls) — use for /delegate status detail.
*/
async function checkDelegationsWithOnChain(userId) {
	const base = checkDelegations(userId);
	if (base.health.length === 0) return base;
	const delegationStore = getDelegationStore();
	return {
		health: await Promise.all(base.health.map(async (h) => {
			if (!delegationStore.load(h.policyId)) return h;
			const hash = getDelegatedPolicies(userId).find((p) => p.id === h.policyId)?.delegation?.hash;
			if (!hash || hash === "0x") return h;
			try {
				const onChain = await readOnChainUsage(h.policyId, hash, h.chainId, h);
				h.onChain = onChain;
				if (onChain.driftDetected && onChain.driftDetails) base.alerts.push({
					severity: "warning",
					policyId: h.policyId,
					policyName: h.policyName,
					chainId: h.chainId,
					message: `Usage drift detected for "${h.policyName}": ${onChain.driftDetails}`,
					action: "Local tracking may be stale. On-chain enforcers have the authoritative state."
				});
			} catch {}
			return h;
		})),
		alerts: base.alerts
	};
}
/**
* Format on-chain usage for display (appended to health output).
*/
function formatOnChainUsage(onChain) {
	const lines = [];
	if (onChain.nativeSpentWei !== null) {
		const ethSpent = Number(onChain.nativeSpentWei) / 0xde0b6b3a7640000;
		lines.push(`  On-chain ETH spent: ${ethSpent.toFixed(6)} ETH`);
	}
	if (onChain.erc20Spent !== null && onChain.erc20Spent > 0n) lines.push(`  On-chain ERC-20 spent: ${onChain.erc20Spent.toString()} (raw units)`);
	if (onChain.callCount !== null) lines.push(`  On-chain call count: ${onChain.callCount.toString()}`);
	if (onChain.driftDetected) lines.push(`  [!] Drift: ${onChain.driftDetails}`);
	return lines.join("\n");
}
//#endregion
export { checkDelegations, checkDelegationsWithOnChain, formatDelegationHealth, formatOnChainUsage, readOnChainUsage };

//# sourceMappingURL=delegation-monitor.mjs.map