import { AgencyFunction } from "../agencyFunction.js";
export class FunctionRefReviver {
    registry = null;
    nativeTypeName() {
        return "FunctionRef";
    }
    isInstance(value) {
        return AgencyFunction.isAgencyFunction(value);
    }
    serialize(value) {
        const result = {
            __nativeType: this.nativeTypeName(),
            name: value.name,
            module: value.module,
        };
        // Serialize params with bound values inline
        if (value.params.some(p => p.isBound)) {
            result.params = value.params;
        }
        // Serialize full tool definition (may have reduced schema/description)
        if (value.toolDefinition) {
            result.toolDescription = value.toolDefinition.description;
        }
        if (value.isPreapproved) {
            result.isPreapproved = true;
        }
        return result;
    }
    validate(value) {
        return typeof value.name === "string" && typeof value.module === "string";
    }
    revive(value) {
        if (!this.registry) {
            throw new Error(`FunctionRefReviver: no registry set. Cannot revive function "${value.name}" from module "${value.module}".`);
        }
        const name = value.name;
        const module = value.module;
        const original = this.findInRegistry(name, module);
        let result;
        // If serialized params have bound values, reconstruct the bound function
        if (Array.isArray(value.params) && value.params.some((p) => p.isBound)) {
            const params = value.params;
            // Rebuild bindings from the serialized params
            const bindings = {};
            for (const p of params) {
                if (p.isBound) {
                    bindings[p.name] = p.boundValue;
                }
            }
            result = original.partial(bindings);
            // Restore tool description if it was customized via .describe(). Use
            // `.describe()` instead of `.withToolDefinition()` because the bound
            // result may have `toolDefinition === null` (when the original had
            // no tool def), and `.describe()` synthesizes a stub tool def in
            // that case so the description isn't silently dropped.
            if (typeof value.toolDescription === "string"
                && value.toolDescription !== result.toolDefinition?.description) {
                result = result.describe(value.toolDescription);
            }
        }
        else if (typeof value.toolDescription === "string"
            && value.toolDescription !== original.toolDefinition?.description) {
            // Restore tool description for non-bound functions that used .describe().
            // Covers the case where `original.toolDefinition` is null too (a function
            // that gained its tool def solely through `.describe()`).
            result = original.describe(value.toolDescription);
        }
        else {
            result = original;
        }
        // Restore preapproved state
        if (value.isPreapproved) {
            result = result.preapprove();
        }
        return result;
    }
    findInRegistry(name, module) {
        // Fast path: direct lookup by composite `${module}:${name}` key.
        const direct = this.registry[`${module}:${name}`];
        if (direct && direct.name === name && direct.module === module) {
            return direct;
        }
        // Slow path: linear scan covers two legacy cases — older `.js`
        // output that keyed by bare name, and any future keying scheme
        // collisions. Either way the function identity check is authoritative.
        for (const entry of Object.values(this.registry)) {
            if (entry.name === name && entry.module === module) {
                return entry;
            }
        }
        throw new Error(`FunctionRefReviver: function "${name}" from module "${module}" not found in registry. ` +
            `The function may have been renamed or removed since this state was serialized.`);
    }
}
