"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.providerEnvVarMap = void 0;
exports.getZFactory = getZFactory;
exports.validateZodSchema = validateZodSchema;
exports.isRunningInBun = isRunningInBun;
exports.toGeminiSchema = toGeminiSchema;
exports.getZodType = getZodType;
exports.transformSchema = transformSchema;
exports.injectUrls = injectUrls;
exports.loadApiKeyFromEnv = loadApiKeyFromEnv;
exports.trimTrailingTextNode = trimTrailingTextNode;
exports.jsonSchemaToZod = jsonSchemaToZod;
const sdkErrors_js_1 = require("./v3/types/public/sdkErrors.js");
const genai_1 = require("@google/genai");
const zod_1 = require("zod");
const v3_1 = __importDefault(require("zod/v3"));
const zodCompat_js_1 = require("./v3/zodCompat.js");
const ID_PATTERN = /^\d+-\d+$/;
const zFactories = {
    v4: zod_1.z,
    v3: v3_1.default,
};
function getZFactory(schema) {
    return (0, zodCompat_js_1.isZod4Schema)(schema) ? zFactories.v4 : zFactories.v3;
}
const TYPE_NAME_MAP = {
    ZodString: "string",
    string: "string",
    ZodNumber: "number",
    number: "number",
    ZodBoolean: "boolean",
    boolean: "boolean",
    ZodObject: "object",
    object: "object",
    ZodArray: "array",
    array: "array",
    ZodUnion: "union",
    union: "union",
    ZodIntersection: "intersection",
    intersection: "intersection",
    ZodOptional: "optional",
    optional: "optional",
    ZodNullable: "nullable",
    nullable: "nullable",
    ZodLiteral: "literal",
    literal: "literal",
    ZodEnum: "enum",
    enum: "enum",
    ZodDefault: "default",
    default: "default",
    ZodEffects: "effects",
    effects: "effects",
    pipe: "pipe",
};
function getZ4Def(schema) {
    return schema._zod?.def;
}
function getZ4Bag(schema) {
    return schema._zod?.bag;
}
function getZ3Def(schema) {
    return schema._def;
}
function getObjectShape(schema) {
    const z4Shape = getZ4Def(schema)?.shape;
    if (z4Shape) {
        return z4Shape;
    }
    const z3Shape = getZ3Def(schema)?.shape;
    if (!z3Shape) {
        return undefined;
    }
    if (typeof z3Shape === "function") {
        return z3Shape();
    }
    return z3Shape;
}
function getArrayElement(schema) {
    return (getZ4Def(schema)?.element ?? getZ3Def(schema)?.type);
}
function getInnerType(schema) {
    return (getZ4Def(schema)?.innerType ?? getZ3Def(schema)?.innerType);
}
function getUnionOptions(schema) {
    const z4Options = getZ4Def(schema)?.options;
    if (Array.isArray(z4Options)) {
        return z4Options;
    }
    const z3Options = getZ3Def(schema)?.options;
    return Array.isArray(z3Options)
        ? z3Options
        : undefined;
}
function getIntersectionSides(schema) {
    const z4Def = getZ4Def(schema);
    if (z4Def?.left || z4Def?.right) {
        return {
            left: z4Def?.left,
            right: z4Def?.right,
        };
    }
    const z3Def = getZ3Def(schema);
    return {
        left: z3Def?.left,
        right: z3Def?.right,
    };
}
function getEnumValues(schema) {
    const z4Entries = getZ4Def(schema)?.entries;
    if (z4Entries && typeof z4Entries === "object") {
        return Object.values(z4Entries);
    }
    const z3Values = getZ3Def(schema)?.values;
    return Array.isArray(z3Values) ? z3Values : undefined;
}
function getLiteralValues(schema) {
    const z4Values = getZ4Def(schema)?.values;
    if (Array.isArray(z4Values)) {
        return z4Values;
    }
    const value = getZ3Def(schema)?.value;
    return typeof value !== "undefined" ? [value] : [];
}
function getStringChecks(schema) {
    const z4Checks = getZ4Def(schema)?.checks;
    if (Array.isArray(z4Checks)) {
        return z4Checks;
    }
    const z3Checks = getZ3Def(schema)?.checks;
    return Array.isArray(z3Checks) ? z3Checks : [];
}
function getStringFormat(schema) {
    const bagFormat = getZ4Bag(schema)?.format;
    if (typeof bagFormat === "string") {
        return bagFormat;
    }
    const z4Format = getZ4Def(schema)?.format;
    if (typeof z4Format === "string") {
        return z4Format;
    }
    const z3Format = getZ3Def(schema)?.format;
    return typeof z3Format === "string" ? z3Format : undefined;
}
function getPipeEndpoints(schema) {
    const z4Def = getZ4Def(schema);
    if (z4Def?.in || z4Def?.out) {
        return {
            in: z4Def?.in,
            out: z4Def?.out,
        };
    }
    return {};
}
function getEffectsBaseSchema(schema) {
    return getZ3Def(schema)?.schema;
}
function validateZodSchema(schema, data) {
    const result = schema.safeParse(data);
    if (result.success) {
        return true;
    }
    throw new sdkErrors_js_1.ZodSchemaValidationError(data, result.error.format());
}
/**
 * Detects if the code is running in the Bun runtime environment.
 * @returns {boolean} True if running in Bun, false otherwise.
 */
function isRunningInBun() {
    return (typeof process !== "undefined" &&
        typeof process.versions !== "undefined" &&
        "bun" in process.versions);
}
/*
 * Helper functions for converting between Gemini and Zod schemas
 */
function decorateGeminiSchema(geminiSchema, zodSchema) {
    if (geminiSchema.nullable === undefined) {
        geminiSchema.nullable = zodSchema.isOptional();
    }
    if (zodSchema.description) {
        geminiSchema.description = zodSchema.description;
    }
    return geminiSchema;
}
function toGeminiSchema(zodSchema) {
    const normalizedSchema = zodSchema;
    const zodType = getZodType(zodSchema);
    switch (zodType) {
        case "array": {
            const element = getArrayElement(zodSchema) ?? zod_1.z.any();
            return decorateGeminiSchema({
                type: genai_1.Type.ARRAY,
                items: toGeminiSchema(element),
            }, normalizedSchema);
        }
        case "object": {
            const properties = {};
            const required = [];
            const shape = getObjectShape(zodSchema);
            if (shape) {
                Object.entries(shape).forEach(([key, value]) => {
                    properties[key] = toGeminiSchema(value);
                    if (getZodType(value) !== "optional") {
                        required.push(key);
                    }
                });
            }
            return decorateGeminiSchema({
                type: genai_1.Type.OBJECT,
                properties,
                required: required.length > 0 ? required : undefined,
            }, normalizedSchema);
        }
        case "string":
            return decorateGeminiSchema({
                type: genai_1.Type.STRING,
            }, normalizedSchema);
        case "number":
            return decorateGeminiSchema({
                type: genai_1.Type.NUMBER,
            }, normalizedSchema);
        case "boolean":
            return decorateGeminiSchema({
                type: genai_1.Type.BOOLEAN,
            }, normalizedSchema);
        case "enum": {
            const values = getEnumValues(zodSchema);
            return decorateGeminiSchema({
                type: genai_1.Type.STRING,
                enum: values,
            }, normalizedSchema);
        }
        case "default":
        case "nullable":
        case "optional": {
            const innerType = getInnerType(zodSchema) ?? zod_1.z.any();
            const innerSchema = toGeminiSchema(innerType);
            return decorateGeminiSchema({
                ...innerSchema,
                nullable: true,
            }, normalizedSchema);
        }
        case "literal": {
            const values = getLiteralValues(zodSchema);
            return decorateGeminiSchema({
                type: genai_1.Type.STRING,
                enum: values,
            }, normalizedSchema);
        }
        case "pipe": {
            const endpoints = getPipeEndpoints(zodSchema);
            if (endpoints.in) {
                return toGeminiSchema(endpoints.in);
            }
            return decorateGeminiSchema({
                type: genai_1.Type.STRING,
            }, normalizedSchema);
        }
        // Standalone transforms and any unknown types fall through to default
        default:
            return decorateGeminiSchema({
                type: genai_1.Type.STRING,
            }, normalizedSchema);
    }
}
// Helper function to check the type of Zod schema
function getZodType(schema) {
    const schemaWithDef = schema;
    const rawType = schemaWithDef._zod?.def?.type ??
        schemaWithDef._def?.typeName ??
        schemaWithDef._def?.type;
    if (!rawType) {
        return "unknown";
    }
    return TYPE_NAME_MAP[rawType] ?? rawType;
}
/**
 * Recursively traverses a given Zod schema, scanning for any fields of type `z.string().url()`.
 * For each such field, it replaces the `z.string().url()` with `z.number()`.
 *
 * This function is used internally by higher-level utilities (e.g., transforming entire object schemas)
 * and handles nested objects, arrays, unions, intersections, optionals.
 *
 * @param schema - The Zod schema to transform.
 * @param currentPath - An array of string/number keys representing the current schema path (used internally for recursion).
 * @returns A two-element tuple:
 *   1. The updated Zod schema, with any `.url()` fields replaced by `z.number()`.
 *   2. An array of {@link ZodPathSegments} objects representing each replaced field, including the path segments.
 */
function transformSchema(schema, currentPath) {
    if (isKind(schema, "string")) {
        const checks = getStringChecks(schema);
        const format = getStringFormat(schema);
        const hasUrlCheck = checks.some((check) => {
            const candidate = check;
            return (candidate.kind === "url" ||
                candidate.format === "url" ||
                candidate._zod?.def?.check === "url" ||
                candidate._zod?.def?.format === "url");
        }) || format === "url";
        if (hasUrlCheck) {
            return [makeIdStringSchema(schema), [{ segments: [] }]];
        }
        return [schema, []];
    }
    if (isKind(schema, "object")) {
        const shape = getObjectShape(schema);
        if (!shape) {
            return [schema, []];
        }
        const newShape = {};
        const urlPaths = [];
        let changed = false;
        for (const key of Object.keys(shape)) {
            const child = shape[key];
            const [transformedChild, childPaths] = transformSchema(child, [
                ...currentPath,
                key,
            ]);
            if (transformedChild !== child) {
                changed = true;
            }
            newShape[key] = transformedChild;
            childPaths.forEach((cp) => {
                urlPaths.push({ segments: [key, ...cp.segments] });
            });
        }
        if (changed) {
            const factory = getZFactory(schema);
            return [
                factory.object(newShape),
                urlPaths,
            ];
        }
        return [schema, urlPaths];
    }
    if (isKind(schema, "array")) {
        const itemType = getArrayElement(schema);
        if (!itemType) {
            return [schema, []];
        }
        const [transformedItem, childPaths] = transformSchema(itemType, [
            ...currentPath,
            "*",
        ]);
        const arrayPaths = childPaths.map((cp) => ({
            segments: ["*", ...cp.segments],
        }));
        if (transformedItem !== itemType) {
            const factory = getZFactory(schema);
            return [
                factory.array(transformedItem),
                arrayPaths,
            ];
        }
        return [schema, arrayPaths];
    }
    if (isKind(schema, "union")) {
        const unionOptions = getUnionOptions(schema);
        if (!unionOptions || unionOptions.length === 0) {
            return [schema, []];
        }
        const newOptions = [];
        let changed = false;
        let allPaths = [];
        unionOptions.forEach((option, idx) => {
            const [newOption, childPaths] = transformSchema(option, [
                ...currentPath,
                `union_${idx}`,
            ]);
            if (newOption !== option) {
                changed = true;
            }
            newOptions.push(newOption);
            allPaths = [...allPaths, ...childPaths];
        });
        if (changed) {
            const factory = getZFactory(schema);
            return [
                factory.union(newOptions),
                allPaths,
            ];
        }
        return [schema, allPaths];
    }
    if (isKind(schema, "intersection")) {
        const { left, right } = getIntersectionSides(schema);
        if (!left || !right) {
            return [schema, []];
        }
        const [newLeft, leftPaths] = transformSchema(left, [
            ...currentPath,
            "intersection_left",
        ]);
        const [newRight, rightPaths] = transformSchema(right, [
            ...currentPath,
            "intersection_right",
        ]);
        const changed = newLeft !== left || newRight !== right;
        const allPaths = [...leftPaths, ...rightPaths];
        if (changed) {
            const factory = getZFactory(schema);
            return [
                factory.intersection(newLeft, newRight),
                allPaths,
            ];
        }
        return [schema, allPaths];
    }
    if (isKind(schema, "optional")) {
        const innerType = getInnerType(schema);
        if (!innerType) {
            return [schema, []];
        }
        const [inner, innerPaths] = transformSchema(innerType, currentPath);
        if (inner !== innerType) {
            return [
                inner.optional(),
                innerPaths,
            ];
        }
        return [schema, innerPaths];
    }
    if (isKind(schema, "nullable")) {
        const innerType = getInnerType(schema);
        if (!innerType) {
            return [schema, []];
        }
        const [inner, innerPaths] = transformSchema(innerType, currentPath);
        if (inner !== innerType) {
            return [
                inner.nullable(),
                innerPaths,
            ];
        }
        return [schema, innerPaths];
    }
    if (isKind(schema, "pipe") && (0, zodCompat_js_1.isZod4Schema)(schema)) {
        const { in: inSchema, out: outSchema } = getPipeEndpoints(schema);
        if (!inSchema || !outSchema) {
            return [schema, []];
        }
        const [newIn, inPaths] = transformSchema(inSchema, currentPath);
        const [newOut, outPaths] = transformSchema(outSchema, currentPath);
        const allPaths = [...inPaths, ...outPaths];
        if (newIn !== inSchema || newOut !== outSchema) {
            const result = zod_1.z.pipe(newIn, newOut);
            return [result, allPaths];
        }
        return [schema, allPaths];
    }
    if (isKind(schema, "effects")) {
        const baseSchema = getEffectsBaseSchema(schema);
        if (!baseSchema) {
            return [schema, []];
        }
        return transformSchema(baseSchema, currentPath);
    }
    return [schema, []];
}
/**
 * Once we get the final extracted object that has numeric IDs in place of URLs,
 * use `injectUrls` to walk the object and replace numeric IDs
 * with the real URL strings from idToUrlMapping. The `path` may include `*`
 * for array indices (indicating "all items in the array").
 */
function injectUrls(obj, path, idToUrlMapping) {
    if (path.length === 0)
        return;
    const toId = (value) => {
        if (typeof value === "number") {
            return String(value);
        }
        if (typeof value === "string" && ID_PATTERN.test(value)) {
            return value;
        }
        return undefined;
    };
    const [key, ...rest] = path;
    if (key === "*") {
        if (Array.isArray(obj)) {
            if (rest.length === 0) {
                for (let i = 0; i < obj.length; i += 1) {
                    const id = toId(obj[i]);
                    if (id !== undefined) {
                        obj[i] = idToUrlMapping[id] ?? "";
                    }
                }
            }
            else {
                for (const item of obj)
                    injectUrls(item, rest, idToUrlMapping);
            }
        }
        return;
    }
    if (obj && typeof obj === "object") {
        const record = obj;
        if (path.length === 1) {
            const fieldValue = record[key];
            const id = toId(fieldValue);
            if (id !== undefined) {
                record[key] = idToUrlMapping[id] ?? "";
            }
        }
        else {
            injectUrls(record[key], rest, idToUrlMapping);
        }
    }
}
// Helper to check if a schema is of a specific type
function isKind(s, kind) {
    try {
        return getZodType(s) === kind;
    }
    catch {
        return false;
    }
}
function makeIdStringSchema(orig) {
    const userDesc = orig.description ?? "";
    const base = "This field must be the element-ID in the form 'frameId-backendId' " +
        '(e.g. "0-432").';
    const composed = userDesc.trim().length > 0
        ? `${base} that follows this user-defined description: ${userDesc}`
        : base;
    const factory = getZFactory(orig);
    return factory.string().regex(ID_PATTERN).describe(composed);
}
/**
 * Mapping from LLM provider names to their corresponding environment variable names for API keys.
 */
exports.providerEnvVarMap = {
    openai: "OPENAI_API_KEY",
    anthropic: "ANTHROPIC_API_KEY",
    google: ["GEMINI_API_KEY", "GOOGLE_GENERATIVE_AI_API_KEY", "GOOGLE_API_KEY"],
    vertex: "GOOGLE_VERTEX_AI_API_KEY",
    groq: "GROQ_API_KEY",
    cerebras: "CEREBRAS_API_KEY",
    togetherai: "TOGETHER_AI_API_KEY",
    mistral: "MISTRAL_API_KEY",
    deepseek: "DEEPSEEK_API_KEY",
    perplexity: "PERPLEXITY_API_KEY",
    azure: "AZURE_API_KEY",
    xai: "XAI_API_KEY",
    google_legacy: "GOOGLE_API_KEY",
};
const providersWithoutApiKey = new Set(["bedrock", "ollama"]);
/**
 * Loads an API key for a provider, checking environment variables.
 * @param provider The name of the provider (e.g., 'openai', 'anthropic')
 * @param logger Optional logger for info/error messages
 * @returns The API key if found, undefined otherwise
 */
function loadApiKeyFromEnv(provider, logger) {
    if (!provider) {
        return undefined;
    }
    const envVarName = exports.providerEnvVarMap[provider];
    if (!envVarName) {
        if (!providersWithoutApiKey.has(provider)) {
            logger({
                category: "init",
                message: `No known environment variable for provider '${provider}'`,
                level: 0,
            });
        }
        return undefined;
    }
    const apiKeyFromEnv = Array.isArray(envVarName)
        ? envVarName
            .map((name) => process.env[name])
            .find((key) => key && key.length > 0)
        : process.env[envVarName];
    if (typeof apiKeyFromEnv === "string" && apiKeyFromEnv.length > 0) {
        return apiKeyFromEnv;
    }
    // Don't log - this is expected when llmClient is provided or API key will be set later
    return undefined;
}
function trimTrailingTextNode(path) {
    return path?.replace(/\/text\(\)(\[\d+\])?$/iu, "");
}
/**
 * Converts a JSON Schema object to a Zod schema
 * @param schema The JSON Schema object to convert
 * @returns A Zod schema equivalent to the input JSON Schema
 */
function jsonSchemaToZod(schema) {
    switch (schema.type) {
        case "object":
            if (schema.properties) {
                const shape = {};
                for (const key in schema.properties) {
                    shape[key] = jsonSchemaToZod(schema.properties[key]);
                }
                let zodObject = zod_1.z.object(shape);
                if (schema.required && Array.isArray(schema.required)) {
                    const requiredFields = schema.required.reduce((acc, field) => ({ ...acc, [field]: true }), {});
                    zodObject = zodObject.partial().required(requiredFields);
                }
                if (schema.description) {
                    zodObject = zodObject.describe(schema.description);
                }
                return zodObject;
            }
            else {
                return zod_1.z.object({});
            }
        case "array":
            if (schema.items) {
                let zodArray = zod_1.z.array(jsonSchemaToZod(schema.items));
                if (schema.description) {
                    zodArray = zodArray.describe(schema.description);
                }
                return zodArray;
            }
            else {
                return zod_1.z.array(zod_1.z.any());
            }
        case "string": {
            if (schema.enum) {
                return zod_1.z.string().refine((val) => schema.enum.includes(val));
            }
            let zodString = zod_1.z.string();
            // Handle JSON Schema format field
            if (schema.format === "uri" || schema.format === "url") {
                zodString = zodString.url();
            }
            else if (schema.format === "email") {
                zodString = zodString.email();
            }
            else if (schema.format === "uuid") {
                zodString = zodString.uuid();
            }
            // Add more format handlers as needed
            if (schema.description) {
                zodString = zodString.describe(schema.description);
            }
            return zodString;
        }
        case "number": {
            let zodNumber = zod_1.z.number();
            if (schema.minimum !== undefined) {
                zodNumber = zodNumber.min(schema.minimum);
            }
            if (schema.maximum !== undefined) {
                zodNumber = zodNumber.max(schema.maximum);
            }
            if (schema.description) {
                zodNumber = zodNumber.describe(schema.description);
            }
            return zodNumber;
        }
        case "boolean": {
            let zodBoolean = zod_1.z.boolean();
            if (schema.description) {
                zodBoolean = zodBoolean.describe(schema.description);
            }
            return zodBoolean;
        }
        default:
            return zod_1.z.any();
    }
}
//# sourceMappingURL=utils.js.map