"use strict";
/**
 * Compiled-contract registry.
 *
 * The OData submission actions accept a string `compiledArtifactRef`
 * (e.g. "attestation-vault") and resolve it through this registry to:
 *   - the compiled contract module (Compact `compactc` output under
 *     `<base>/<name>/src/managed/<name>/contract/`)
 *   - the contract's `privateStateId`
 *   - the `zkConfigPath` the SDK's NodeZkConfigProvider reads from
 *
 * The registry is in-memory and starts empty. Until a contract is registered,
 * the OData actions return a clear 404-style error rather than failing somewhere
 * deep in the SDK.
 *
 * Registration shape is loaded from `cds.requires.nightgate.contracts` if
 * present:
 *
 *   contracts: {
 *     "attestation-vault": {
 *       artifactPath:   "<repo>/contracts/attestation-vault/src/managed/attestation-vault/contract/index.js",
 *       privateStateId: "attestationVaultPrivateState",
 *       zkConfigPath:   "<repo>/contracts/attestation-vault/src/managed/attestation-vault"
 *     }
 *   }
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContractNotRegisteredError = void 0;
exports.registerContract = registerContract;
exports.unregisterContract = unregisterContract;
exports.clearRegistry = clearRegistry;
exports.listRegisteredContracts = listRegisteredContracts;
exports.loadRegistryFromConfig = loadRegistryFromConfig;
exports.resolveContract = resolveContract;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const url_1 = require("url");
// Package root (…/node_modules/@odatano/nightgate when installed). contract-registry
// lives at <root>/srv/submission/, so ../.. is the package root.
const PACKAGE_ROOT = path_1.default.resolve(__dirname, '..', '..');
/**
 * Resolve a configured contract path. Absolute paths pass through. For a
 * RELATIVE path we prefer the package root when the target exists there — this
 * is how the BUNDLED contracts (counter, attestation-vault, shipped under the
 * package's contracts/ dir) resolve correctly in a consumer app, where
 * process.cwd() is the consumer's project root, not node_modules/@odatano/nightgate.
 * A consumer's OWN relative path (not present under the package) falls back to
 * baseDir (cwd), preserving the prior behaviour for consumer-registered contracts.
 */
function resolveContractPath(p, baseDir) {
    if (path_1.default.isAbsolute(p))
        return p;
    const fromPackage = path_1.default.join(PACKAGE_ROOT, p);
    if (fs_1.default.existsSync(fromPackage))
        return fromPackage;
    return path_1.default.join(baseDir, p);
}
const registry = new Map();
function registerContract(name, reg) {
    if (!name || !reg.artifactPath || !reg.privateStateId || !reg.zkConfigPath) {
        throw new Error('registerContract: all fields are required');
    }
    registry.set(name, reg);
}
function unregisterContract(name) {
    return registry.delete(name);
}
function clearRegistry() {
    registry.clear();
}
function listRegisteredContracts() {
    return Array.from(registry.keys());
}
/**
 * Load all contracts declared under `cds.requires.nightgate.contracts`.
 * Idempotent, safe to call multiple times.
 */
function loadRegistryFromConfig(config, baseDir = process.cwd()) {
    const contracts = config?.contracts;
    if (!contracts || typeof contracts !== 'object')
        return;
    for (const [name, reg] of Object.entries(contracts)) {
        const r = reg;
        if (!r?.artifactPath || !r?.privateStateId || !r?.zkConfigPath)
            continue;
        registerContract(name, {
            artifactPath: resolveContractPath(r.artifactPath, baseDir),
            privateStateId: r.privateStateId,
            zkConfigPath: resolveContractPath(r.zkConfigPath, baseDir)
        });
    }
}
async function resolveContract(name) {
    const reg = registry.get(name);
    if (!reg) {
        const available = listRegisteredContracts();
        throw new ContractNotRegisteredError(name, available);
    }
    // Node's ESM loader on Windows rejects raw `C:\...` paths in dynamic
    // import, must be a file:// URL. pathToFileURL handles both platforms.
    const importSpec = path_1.default.isAbsolute(reg.artifactPath)
        ? (0, url_1.pathToFileURL)(reg.artifactPath).href
        : reg.artifactPath;
    const mod = await import(importSpec);
    const ContractClass = mod.Contract ?? mod.default ?? mod;
    // The midnight-js-contracts SDK expects a `CompiledContract` wrapper around
    // the raw Compact-emitted `Contract` class, not the class itself. The wrapper
    // attaches witnesses + ZK asset paths (keys/, zkir/) and adds the Symbol-keyed
    // CompactContext that `deployContract` reads. Pattern from example-counter:
    //   CompiledContract.make(name, Contract)
    //     .pipe(withVacantWitnesses, withCompiledFileAssets(zkConfigPath))
    const compactJs = await import('@midnight-ntwrk/compact-js');
    // CompiledContract namespace lives under `effect/CompiledContract` in the
    // compact-js package — re-exported at the top level.
    const CompiledContract = compactJs.CompiledContract ?? compactJs.effect?.CompiledContract;
    if (!CompiledContract?.make) {
        throw new Error(`CompiledContract.make not found in @midnight-ntwrk/compact-js exports; got keys: ${Object.keys(compactJs).join(',')}`);
    }
    const compiledContract = CompiledContract.make(name, ContractClass).pipe(CompiledContract.withVacantWitnesses, CompiledContract.withCompiledFileAssets(reg.zkConfigPath));
    return {
        compiledContract,
        privateStateId: reg.privateStateId,
        zkConfigPath: reg.zkConfigPath,
        artifactPath: reg.artifactPath
    };
}
class ContractNotRegisteredError extends Error {
    name;
    available;
    constructor(name, available) {
        super(available.length === 0
            ? `Contract '${name}' is not registered. No contracts are registered yet (register via cds.requires.nightgate.contracts or call registerContract()).`
            : `Contract '${name}' is not registered. Available: ${available.join(', ')}`);
        this.name = name;
        this.available = available;
        this.name = 'ContractNotRegisteredError';
    }
}
exports.ContractNotRegisteredError = ContractNotRegisteredError;
