"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtractHandler = void 0;
exports.transformUrlStringsToNumericIds = transformUrlStringsToNumericIds;
// lib/v3/handlers/extractHandler.ts
const inference_js_1 = require("../../inference.js");
const utils_js_1 = require("../../utils.js");
const logger_js_1 = require("../logger.js");
const methods_js_1 = require("../types/public/methods.js");
const index_js_1 = require("../understudy/a11y/snapshot/index.js");
const methods_js_2 = require("../types/public/methods.js");
const sdkErrors_js_1 = require("../types/public/sdkErrors.js");
const timeoutGuard_js_1 = require("./handlerUtils/timeoutGuard.js");
/**
 * Scans the provided Zod schema for any `z.string().url()` fields and
 * replaces them with `z.number()`.
 *
 * @param schema - The Zod object schema to transform.
 * @returns A tuple containing:
 *   1. The transformed schema (or the original schema if no changes were needed).
 *   2. An array of {@link ZodPathSegments} objects representing all the replaced URL fields,
 *      with each path segment showing where in the schema the replacement occurred.
 */
function transformUrlStringsToNumericIds(schema) {
    const [finalSchema, urlPaths] = (0, utils_js_1.transformSchema)(schema, []);
    return [finalSchema, urlPaths];
}
class ExtractHandler {
    llmClient;
    defaultModelName;
    defaultClientOptions;
    resolveLlmClient;
    systemPrompt;
    logInferenceToFile;
    experimental;
    onMetrics;
    constructor(llmClient, defaultModelName, defaultClientOptions, resolveLlmClient, systemPrompt, logInferenceToFile, experimental, onMetrics) {
        this.llmClient = llmClient;
        this.defaultModelName = defaultModelName;
        this.defaultClientOptions = defaultClientOptions;
        this.resolveLlmClient = resolveLlmClient;
        this.systemPrompt = systemPrompt ?? "";
        this.logInferenceToFile = logInferenceToFile ?? false;
        this.experimental = experimental ?? false;
        this.onMetrics = onMetrics;
    }
    async extract(params) {
        const { instruction, schema, page, selector, timeout, model } = params;
        const llmClient = this.resolveLlmClient(model);
        const effectiveTimeoutMs = typeof timeout === "number" && timeout > 0 ? timeout : undefined;
        const ensureTimeRemaining = (0, timeoutGuard_js_1.createTimeoutGuard)(effectiveTimeoutMs, (ms) => new sdkErrors_js_1.ExtractTimeoutError(ms));
        // No-args → page text (parity with v2)
        const noArgs = !instruction && !schema;
        if (noArgs) {
            const focusSelector = selector?.replace(/^xpath=/i, "") ?? "";
            ensureTimeRemaining();
            const snap = await (0, index_js_1.captureHybridSnapshot)(page, {
                experimental: this.experimental,
                focusSelector: focusSelector || undefined,
            });
            const result = { pageText: snap.combinedTree };
            // Validate via the same schema used in v2
            return methods_js_2.pageTextSchema.parse(result);
        }
        if (!instruction && schema) {
            throw new sdkErrors_js_1.StagehandInvalidArgumentError("extract() requires an instruction when a schema is provided.");
        }
        const focusSelector = selector?.replace(/^xpath=/, "") ?? "";
        // Build the hybrid snapshot (includes combinedTree; combinedUrlMap optional)
        ensureTimeRemaining();
        const { combinedTree, combinedUrlMap } = await (0, index_js_1.captureHybridSnapshot)(page, {
            experimental: this.experimental,
            focusSelector: focusSelector,
        });
        (0, logger_js_1.v3Logger)({
            category: "extraction",
            message: "Starting extraction using a11y snapshot",
            level: 1,
            auxiliary: instruction
                ? { instruction: { value: instruction, type: "string" } }
                : undefined,
        });
        // Normalize schema: if instruction provided without schema, use defaultExtractSchema
        const baseSchema = (schema ??
            methods_js_2.defaultExtractSchema);
        // Ensure we pass an object schema into inference; wrap non-object schemas
        const isObjectSchema = (0, utils_js_1.getZodType)(baseSchema) === "object";
        const WRAP_KEY = "value";
        const factory = (0, utils_js_1.getZFactory)(baseSchema);
        const objectSchema = isObjectSchema
            ? baseSchema
            : factory.object({
                [WRAP_KEY]: baseSchema,
            });
        const [transformedSchema, urlFieldPaths] = transformUrlStringsToNumericIds(objectSchema);
        ensureTimeRemaining();
        const extractionResponse = await (0, inference_js_1.extract)({
            instruction,
            domElements: combinedTree,
            schema: transformedSchema,
            llmClient,
            userProvidedInstructions: this.systemPrompt,
            logger: logger_js_1.v3Logger,
            logInferenceToFile: this.logInferenceToFile,
        });
        const { metadata: { completed }, prompt_tokens, completion_tokens, reasoning_tokens = 0, cached_input_tokens = 0, inference_time_ms, ...rest } = extractionResponse;
        let output = rest;
        // Update EXTRACT metrics from the LLM calls
        this.onMetrics?.(methods_js_1.V3FunctionName.EXTRACT, prompt_tokens, completion_tokens, reasoning_tokens, cached_input_tokens, inference_time_ms);
        // Re-inject URLs for any url() fields we temporarily converted to number()
        const idToUrl = (combinedUrlMap ?? {});
        for (const { segments } of urlFieldPaths) {
            (0, utils_js_1.injectUrls)(output, segments, idToUrl);
        }
        // If we wrapped a non-object schema, unwrap the value
        if (!isObjectSchema && output && typeof output === "object") {
            output = output[WRAP_KEY];
        }
        const resultPreviewLength = 200;
        const resultString = JSON.stringify(output) ?? "undefined";
        const resultPreview = resultString.length > resultPreviewLength
            ? resultString.slice(0, resultPreviewLength) + "..."
            : resultString;
        (0, logger_js_1.v3Logger)({
            category: "extraction",
            message: completed
                ? "Extraction completed successfully"
                : "Extraction incomplete after processing all data",
            level: 1,
            auxiliary: {
                prompt_tokens: { value: String(prompt_tokens), type: "string" },
                completion_tokens: { value: String(completion_tokens), type: "string" },
                inference_time_ms: {
                    value: String(inference_time_ms),
                    type: "string",
                },
                result: { value: resultPreview, type: "string" },
            },
        });
        return output;
    }
}
exports.ExtractHandler = ExtractHandler;
//# sourceMappingURL=extractHandler.js.map