import z from "zod";
import { createToolFactory, ToolCategory } from "../tools2/types.js";
// Default truncation limit for messages (5KB)
const DEFAULT_MESSAGE_TRUNCATE_LIMIT = 5 * 1024;
/**
 * Truncates a string to a maximum number of characters, adding a truncation indicator if needed.
 */
function truncateMessage(str, maxChars) {
    if (str.length <= maxChars) {
        return { text: str, truncated: false, originalLength: str.length };
    }
    return {
        text: str.slice(0, maxChars) +
            `... [truncated, ${str.length - maxChars} more chars]`,
        truncated: true,
        originalLength: str.length,
    };
}
/**
 * Console log entries include:
 * - id: unique identifier for the log entry
 * - timestamp: when the log was created (milliseconds since epoch)
 * - timeAgo: human-readable relative time (e.g., "2 minutes ago", "just now")
 * - level: log level (log/info/debug/warn/error)
 * - message: the logged message (objects are JSON stringified)
 * - stack: stack trace
 * - generationTimestamp: ISO timestamp of when log was captured
 *
 * API run records (controlled by includeApiRuns flag):
 * - Light mode (default): Only includes small predictable fields:
 *   - apiId, id, callId, timestamp, timeAgo, statusCode, timeTaken, isTestRun, loading
 * - Full mode: Includes additional fields based on flags:
 *   - inputs: Input parameters used (includeApiInputs)
 *   - response: API response data (includeApiResponse)
 *   - error: Error message if execution failed (includeApiError)
 *   - stepLogs: Per-step execution details (includeStepOutputs, includeStepLogs):
 *     - stepName: Name of the step (e.g., "Step1", "GetUsers")
 *     - stdout: Array of console.log output (includeStepLogs)
 *     - stderr: Array of console.error output (includeStepLogs)
 *     - error: Step error message (includeStepLogs)
 *     - output: The step's return value (includeStepOutputs)
 *
 * Returns:
 * - consoleLogs: Array of console log entries (most recent first)
 * - apiRuns: Array of API run records (most recent first, empty if feature flag disabled)
 * - consoleLogCount: Number of console logs returned
 * - apiRunCount: Number of API runs returned
 * - totalConsoleLogCount: Total number of console logs in buffer
 * - totalApiRunCount: Total number of API runs in buffer
 */
export const buildGetLogsToolFactory = createToolFactory("get_logs", ({ clark, services, }) => {
    // Feature flag controls whether API run logs are enabled
    const apiRunLogsEnabled = services.features.apiRunLogsEnabled ?? true;
    return {
        category: ToolCategory.DEBUG,
        description: `
Retrieve console logs, API execution history, and server-side step logs from the user's application.

Use to investigate errors, verify API execution, and debug runtime issues.
When apiId is provided, logs are automatically correlated with that API's execution window.
Records sorted by recency, retained for 10 minutes.

By default, API runs return only lightweight metadata. Set apiRunLightMode=false and use the include* flags for detailed data.

If the apiRuns field is undefined, API run tracking is disabled. If apiRuns is an empty array, it means the API did NOT execute.`,
        inputSchema: z.object({
            apiId: z
                .string()
                .optional()
                .describe("Filter by specific API name. Returns API runs for this API and console logs correlated to those runs."),
            level: z
                .enum(["log", "info", "debug", "warn", "error"])
                .optional()
                .describe("Filter console logs by level"),
            prefix: z
                .string()
                .min(3, "Prefix must be at least 3 characters")
                .optional()
                .describe("Filter console logs where message starts with this prefix (e.g., '[DEBUG]', 'Error:')"),
            limit: z
                .number()
                .positive()
                .max(200)
                .optional()
                .describe("Maximum number of entries to return (most recent first). Default: 50, max: 200."),
            fullContent: z
                .boolean()
                .optional()
                .describe("If true, return full (non-truncated) console log messages. Use when you need complete log content."),
            maxMessageLength: z
                .number()
                .positive()
                .optional()
                .describe("Maximum characters per console log message before truncation. Default: 5000. Ignored if fullContent is true."),
            apiRunLightMode: z
                .boolean()
                .optional()
                .describe("If true, API runs only include lightweight metadata (apiId, statusCode, timeTaken, etc.). " +
                "Set to false and use include* flags for detailed data. Default: true."),
            includeApiInputs: z
                .boolean()
                .optional()
                .describe("Include API input parameters in results. Requires apiRunLightMode=false. Default: false."),
            includeApiResponse: z
                .boolean()
                .optional()
                .describe("Include API response data in results. Requires apiRunLightMode=false. Default: false."),
            includeApiError: z
                .boolean()
                .optional()
                .describe("Include API-level error details in results. Requires apiRunLightMode=false. Default: false."),
            includeStepOutputs: z
                .boolean()
                .optional()
                .describe("Include each step's return value in stepLogs. Requires apiRunLightMode=false. Default: false."),
            includeStepLogs: z
                .boolean()
                .optional()
                .describe("Include step console output (stdout/stderr) and step errors in stepLogs. Requires apiRunLightMode=false. Default: false."),
            includeApiDiagnostics: z
                .boolean()
                .optional()
                .describe("Include per-integration-call diagnostics in API run results. Requires apiRunLightMode=false. Default: false."),
            onlyApiRuns: z
                .boolean()
                .optional()
                .describe("If true, return only API runs (no console logs). Useful when you only need execution history."),
            apiRunLimit: z
                .number()
                .positive()
                .max(20)
                .optional()
                .describe("Maximum number of API runs to return. Default: 5."),
        }),
        execute: async ({ apiId, level, prefix, limit, fullContent, maxMessageLength, apiRunLightMode = true, includeApiInputs = false, includeApiResponse = false, includeApiError = false, includeStepOutputs = false, includeStepLogs = false, includeApiDiagnostics = false, onlyApiRuns = false, apiRunLimit = 5, }) => {
            const allConsoleLogs = clark.context.consoleLogs ?? [];
            const allApiRuns = clark.context.apiRunRecords ?? [];
            const now = Date.now();
            const effectiveLimit = limit ?? 50;
            const truncateLimit = maxMessageLength ?? DEFAULT_MESSAGE_TRUNCATE_LIMIT;
            // Helper function to format relative time
            const formatTimeAgo = (timestamp) => {
                const seconds = Math.floor((now - timestamp) / 1000);
                if (seconds < 10)
                    return "just now";
                if (seconds < 60)
                    return `${seconds} seconds ago`;
                const minutes = Math.floor(seconds / 60);
                return `${minutes} ${minutes === 1 ? "minute" : "minutes"} ago`;
            };
            // Filter API runs
            let apiRuns = allApiRuns;
            if (apiId) {
                apiRuns = apiRuns.filter((run) => run.apiId === apiId);
            }
            // Sort API runs by timestamp descending (most recent first) and limit
            apiRuns = apiRuns
                .sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0))
                .slice(0, apiRunLimit);
            // When onlyApiRuns is true, skip console log processing entirely
            let consoleLogs = onlyApiRuns ? [] : allConsoleLogs;
            if (!onlyApiRuns) {
                if (level) {
                    consoleLogs = consoleLogs.filter((log) => log.level === level);
                }
                // Filter by prefix if specified
                if (prefix) {
                    consoleLogs = consoleLogs.filter((log) => typeof log.message === "string" &&
                        log.message.startsWith(prefix));
                }
                // If apiId is provided, correlate console logs with API execution windows
                if (apiId) {
                    // First loop: calculate overall bounds across all API runs
                    let overallStart = Infinity;
                    let overallEnd = -Infinity;
                    for (const run of apiRuns) {
                        const runStart = run.timestamp ?? 0;
                        const runEnd = runStart + (run.timeTaken ?? 0);
                        // Include logs within the API execution window plus a small buffer (500ms before/after)
                        const windowStart = runStart - 500;
                        const windowEnd = runEnd + 500;
                        overallStart = Math.min(overallStart, windowStart);
                        overallEnd = Math.max(overallEnd, windowEnd);
                    }
                    // Second loop: filter logs within overall bounds
                    // This captures logs between runs and related processes outside individual run windows
                    consoleLogs = consoleLogs
                        .filter((log) => {
                        const logTime = log.timestamp ?? 0;
                        return logTime >= overallStart && logTime <= overallEnd;
                    })
                        .sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
                }
                else {
                    // Sort console logs by timestamp descending (most recent first)
                    consoleLogs = consoleLogs.sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
                }
            }
            // Apply limit (default 50, applies to combined results)
            // Combine and sort all entries by timestamp
            const allEntries = [
                ...apiRuns.map((run) => ({
                    type: "apiRun",
                    timestamp: run.timestamp ?? 0,
                    data: run,
                })),
                ...consoleLogs.map((log) => ({
                    type: "consoleLog",
                    timestamp: log.timestamp ?? 0,
                    data: log,
                })),
            ].sort((a, b) => b.timestamp - a.timestamp);
            const limitedEntries = allEntries.slice(0, effectiveLimit);
            apiRuns = limitedEntries
                .filter((e) => e.type === "apiRun")
                .map((e) => e.data);
            consoleLogs = limitedEntries
                .filter((e) => e.type === "consoleLog")
                .map((e) => e.data);
            // Process API runs based on feature flag
            const processedApiRuns = apiRunLogsEnabled
                ? apiRuns.map((run) => {
                    const timeAgo = run.timestamp
                        ? formatTimeAgo(run.timestamp)
                        : "unknown";
                    // Light mode: only include small predictable fields
                    if (apiRunLightMode) {
                        return {
                            apiId: run.apiId,
                            id: run.id,
                            callId: run.callId,
                            timestamp: run.timestamp,
                            timeAgo,
                            statusCode: run.statusCode,
                            timeTaken: run.timeTaken,
                            isTestRun: run.isTestRun,
                            loading: run.loading,
                        };
                    }
                    // Full mode: include fields based on individual flags
                    const result = {
                        apiId: run.apiId,
                        id: run.id,
                        callId: run.callId,
                        timestamp: run.timestamp,
                        timeAgo,
                        statusCode: run.statusCode,
                        timeTaken: run.timeTaken,
                        isTestRun: run.isTestRun,
                        loading: run.loading,
                    };
                    if (includeApiInputs) {
                        result.inputs = run.inputs;
                    }
                    if (includeApiResponse) {
                        result.response = run.response;
                    }
                    if (includeApiError) {
                        result.error = run.error;
                    }
                    if (includeApiDiagnostics && run.diagnostics) {
                        result.diagnostics = run.diagnostics;
                    }
                    // Process stepLogs based on flags
                    if (run.stepLogs &&
                        run.stepLogs.length > 0 &&
                        (includeStepOutputs || includeStepLogs)) {
                        result.stepLogs = run.stepLogs.map((step) => {
                            const stepResult = {
                                stepName: step.stepName,
                            };
                            if (includeStepLogs) {
                                stepResult.stdout = step.stdout;
                                stepResult.stderr = step.stderr;
                                stepResult.error = step.error;
                            }
                            if (includeStepOutputs) {
                                stepResult.output = step.output;
                            }
                            return stepResult;
                        });
                    }
                    return result;
                })
                : [];
            let truncatedCount = 0;
            // Process console logs: add timeAgo and handle truncation
            const processedConsoleLogs = consoleLogs.map((log) => {
                const shouldTruncate = !fullContent;
                const message = typeof log.message === "string"
                    ? log.message
                    : String(log.message ?? "");
                // Truncate message if needed
                const messageResult = shouldTruncate
                    ? truncateMessage(message, truncateLimit)
                    : {
                        text: message,
                        truncated: false,
                        originalLength: message.length,
                    };
                // Truncate stack if present and truncation is enabled
                const stack = typeof log.stack === "string" ? log.stack : undefined;
                const stackResult = stack
                    ? shouldTruncate
                        ? truncateMessage(stack, truncateLimit)
                        : {
                            text: stack,
                            truncated: false,
                            originalLength: stack.length,
                        }
                    : undefined;
                if (messageResult.truncated || stackResult?.truncated) {
                    truncatedCount++;
                }
                return {
                    id: log.id,
                    timestamp: log.timestamp,
                    timeAgo: log.timestamp ? formatTimeAgo(log.timestamp) : "unknown",
                    level: log.level,
                    message: messageResult.text,
                    messageTruncated: messageResult.truncated,
                    messageOriginalLength: messageResult.truncated
                        ? messageResult.originalLength
                        : undefined,
                    stack: stackResult?.text,
                    stackTruncated: stackResult?.truncated,
                    generationTimestamp: log.generationTimestamp,
                };
            });
            return {
                consoleLogs: processedConsoleLogs,
                consoleLogCount: processedConsoleLogs.length,
                totalConsoleLogCount: allConsoleLogs.length,
                truncatedCount,
                // Only include API run fields when feature flag is enabled
                ...(apiRunLogsEnabled && {
                    apiRuns: processedApiRuns,
                    apiRunCount: processedApiRuns.length,
                    totalApiRunCount: allApiRuns.length,
                }),
            };
        },
        readOnly: true,
        getSummary: (_input, output) => {
            const logCount = output.consoleLogCount ?? 0;
            const logText = `${logCount} log${logCount === 1 ? "" : "s"}`;
            if (!apiRunLogsEnabled) {
                return { output: logText };
            }
            const runCount = output.apiRunCount ?? 0;
            return {
                output: `${logText}, ${runCount} API run${runCount === 1 ? "" : "s"}`,
            };
        },
    };
});
//# sourceMappingURL=get-logs.js.map