import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
import { join } from "node:path";
//#region extensions/crypto/src/services/delegation-store.ts
/**
* Delegation Store — File-based persistence for full SignedDelegation structs.
*
* The DelegationInfo on Policy stores lightweight metadata (hash, addresses,
* status). This store persists the complete signed delegation including caveats
* and signature bytes — required for on-chain redemption and revocation.
*
* Layout:
*   ~/.openclawnch/delegations/<policyId>.json
*
* Each file contains a StoredDelegation: the full SignedDelegation struct
* plus chainId and metadata. Bigints are serialized as hex strings.
*
* Follows the same patterns as policy-store.ts: atomic writes, 0o600
* permissions, singleton instance, in-memory cache.
*/
const DELEGATIONS_DIR = join(process.env.HOME ?? "/home/openclawnch", ".openclawnch", "delegations");
/** File permissions (owner read/write only). */
const FILE_MODE = 384;
function ensureDir() {
	if (!existsSync(DELEGATIONS_DIR)) mkdirSync(DELEGATIONS_DIR, { recursive: true });
}
function delegationPath(policyId) {
	return join(DELEGATIONS_DIR, `${policyId.replace(/[^a-zA-Z0-9_-]/g, "_")}.json`);
}
function atomicWrite(targetPath, data) {
	const tmpPath = targetPath + ".tmp." + Date.now();
	writeFileSync(tmpPath, data, { mode: FILE_MODE });
	renameSync(tmpPath, targetPath);
}
function getEncryptionKey() {
	const envKey = process.env.DELEGATION_STORE_KEY;
	if (envKey && envKey.length > 0) return createHash("sha256").update(envKey).digest();
	return null;
}
function encrypt(plaintext) {
	const key = getEncryptionKey();
	if (!key) return plaintext;
	const iv = randomBytes(12);
	const cipher = createCipheriv("aes-256-gcm", key, iv);
	const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
	const tag = cipher.getAuthTag();
	return "ENC:" + Buffer.concat([
		iv,
		tag,
		encrypted
	]).toString("base64");
}
function decrypt(data) {
	if (!data.startsWith("ENC:")) return data;
	const key = getEncryptionKey();
	if (!key) return data;
	const combined = Buffer.from(data.slice(4), "base64");
	const iv = combined.subarray(0, 12);
	const tag = combined.subarray(12, 28);
	const ciphertext = combined.subarray(28);
	const decipher = createDecipheriv("aes-256-gcm", key, iv);
	decipher.setAuthTag(tag);
	return decipher.update(ciphertext) + decipher.final("utf8");
}
function toStored(delegation, chainId, policyId) {
	return {
		delegate: delegation.delegate,
		delegator: delegation.delegator,
		authority: delegation.authority,
		caveats: delegation.caveats.map((c) => ({
			enforcer: c.enforcer,
			terms: c.terms,
			args: c.args
		})),
		salt: "0x" + delegation.salt.toString(16),
		signature: delegation.signature,
		chainId,
		policyId,
		storedAt: (/* @__PURE__ */ new Date()).toISOString()
	};
}
function fromStored(stored) {
	return {
		delegate: stored.delegate,
		delegator: stored.delegator,
		authority: stored.authority,
		caveats: stored.caveats.map((c) => ({
			enforcer: c.enforcer,
			terms: c.terms,
			args: c.args
		})),
		salt: BigInt(stored.salt),
		signature: stored.signature
	};
}
var DelegationStore = class {
	cache = /* @__PURE__ */ new Map();
	/** Save a signed delegation for a policy. Overwrites any existing. */
	save(delegation, chainId, policyId) {
		ensureDir();
		const stored = toStored(delegation, chainId, policyId);
		this.cache.set(policyId, stored);
		atomicWrite(delegationPath(policyId), encrypt(JSON.stringify(stored, null, 2)));
	}
	/** Load a signed delegation by policy ID. Returns null if not found. */
	load(policyId) {
		const cached = this.cache.get(policyId);
		if (cached) return {
			delegation: fromStored(cached),
			chainId: cached.chainId
		};
		const path = delegationPath(policyId);
		if (!existsSync(path)) return null;
		try {
			const raw = readFileSync(path, "utf8");
			const data = JSON.parse(decrypt(raw));
			this.cache.set(policyId, data);
			return {
				delegation: fromStored(data),
				chainId: data.chainId
			};
		} catch (err) {
			const msg = err instanceof Error ? err.message : String(err);
			console.error(`[delegation-store] Failed to load ${policyId}: ${msg}. File preserved as .corrupt`);
			try {
				renameSync(path, path + ".corrupt." + Date.now());
			} catch {}
			return null;
		}
	}
	/** Check if a delegation exists for a policy. */
	has(policyId) {
		if (this.cache.has(policyId)) return true;
		return existsSync(delegationPath(policyId));
	}
	/** Delete a stored delegation. */
	delete(policyId) {
		this.cache.delete(policyId);
		const path = delegationPath(policyId);
		if (existsSync(path)) try {
			unlinkSync(path);
			return true;
		} catch {
			return false;
		}
		return false;
	}
	/** Clear all caches (for testing). */
	reset() {
		this.cache.clear();
	}
};
let _instance = null;
function getDelegationStore() {
	if (!_instance) _instance = new DelegationStore();
	return _instance;
}
function resetDelegationStore() {
	_instance?.reset();
	_instance = null;
}
//#endregion
export { DelegationStore, getDelegationStore, resetDelegationStore };

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