/**
 * Shared function-resolution helpers.
 *
 * Provides one lookup path for built-in and user-defined function
 * metadata so validators can share behavior.
 */

import { getFunctionSignature } from "../builtins/functions.js";
import { getOutputDirective } from "../builtins/outputPositioningDirectives.js";

/**
 * Resolves a function name against built-in and user-defined catalogs.
 * @param {string} functionName - Name as written at call site
 * @param {Record<string, {declaredName:string, params:Array<{name:string,type:string}>, returnType:string|null}>} userDefinedFunctionCatalog - User function catalog
 * @returns {{kind:"builtin"|"user-defined", upperName:string, declaredName:string, signature:{params:Array<any>,returns:string|null,varArgs?:boolean}, returnType:string|null}|null}
 */
function resolveKnownFunction(functionName, userDefinedFunctionCatalog = {}) {
    const upperName = functionName.toUpperCase();
    const builtInSignature = getFunctionSignature(functionName);

    if (builtInSignature) {
        return {
            kind: "builtin",
            upperName,
            declaredName: functionName,
            signature: builtInSignature,
            returnType: builtInSignature.returns || null
        };
    }

    const outputDirective = getOutputDirective(functionName);
    if (outputDirective) {
        return {
            kind: "output-directive",
            upperName,
            declaredName: upperName,
            signature: outputDirective,
            returnType: null
        };
    }

    const userFunction = userDefinedFunctionCatalog[upperName];
    if (!userFunction) {
        return null;
    }

    return {
        kind: "user-defined",
        upperName,
        declaredName: userFunction.declaredName,
        signature: {
            params: userFunction.params || [],
            returns: userFunction.returnType || null,
            varArgs: false
        },
        returnType: userFunction.returnType || null
    };
}

/**
 * Gets the resolved return type from a built-in or user-defined function.
 * @param {string} functionName - Function name at call site
 * @param {Record<string, {declaredName:string, params:Array<{name:string,type:string}>, returnType:string|null}>} userDefinedFunctionCatalog - User function catalog
 * @returns {string|null}
 */
function getResolvedFunctionReturnType(functionName, userDefinedFunctionCatalog = {}) {
    const resolved = resolveKnownFunction(functionName, userDefinedFunctionCatalog);
    return resolved ? resolved.returnType : null;
}

/**
 * Validates argument count for known functions.
 * @param {string} functionName - Function name at call site
 * @param {number} argCount - Number of arguments supplied
 * @param {Record<string, {declaredName:string, params:Array<{name:string,type:string}>, returnType:string|null}>} userDefinedFunctionCatalog - User function catalog
 * @returns {string|null} - Error detail string when invalid, else null
 */
function validateKnownFunctionArgumentCount(functionName, argCount, userDefinedFunctionCatalog = {}) {
    const resolved = resolveKnownFunction(functionName, userDefinedFunctionCatalog);
    if (!resolved) {
        return null;
    }

    if (resolved.signature.varArgs) {
        return null;
    }

    const expectedCount = Array.isArray(resolved.signature.params)
        ? resolved.signature.params.length
        : 0;

    if (argCount === expectedCount) {
        return null;
    }

    const displayName = resolved.kind === "user-defined"
        ? resolved.declaredName
        : functionName;

    return `${displayName} expects ${expectedCount} argument(s), but ${argCount} provided`;
}

export {
    resolveKnownFunction,
    getResolvedFunctionReturnType,
    validateKnownFunctionArgumentCount
};
