import { Type } from "typebox";
import { z } from "zod";
import { createToolError, createToolResult } from "../utils/messages.js";
import { buildHeadTailPreviewLines } from "../utils/tool_preview.js";
import { TRUNCATION_MARKER, truncateForTokens, truncateToBytesFromStart, } from "../utils/truncate.js";
import { formatZodError } from "../utils/zod.js";
import { TOOL_NAME_READ } from "./tool_names.js";
export const READ_TOOL_MAX_TOKENS = 8192;
export const READ_MAX_CAPTURE_BYTES = 1024 * 1024;
const READ_DESCRIPTION = ["Read a file from the project safely."].join(" ");
const READ_PATH_DESCRIPTION = "File path to read (relative to the current working directory).";
const READ_START_LINE_DESCRIPTION = "1-based inclusive start line.";
const READ_END_LINE_DESCRIPTION = "1-based inclusive end line.";
export const READ_TOOL = {
    name: TOOL_NAME_READ,
    description: READ_DESCRIPTION,
    parameters: Type.Object({
        path: Type.String({ description: READ_PATH_DESCRIPTION }),
        startLine: Type.Optional(Type.Integer({ description: READ_START_LINE_DESCRIPTION, minimum: 1 })),
        endLine: Type.Optional(Type.Integer({ description: READ_END_LINE_DESCRIPTION, minimum: 1 })),
    }, { additionalProperties: false }),
};
const readArgsSchema = z.object({
    path: z.string().trim().min(1),
    startLine: z.number().int().positive().optional(),
    endLine: z.number().int().positive().optional(),
});
function parseReadArgs(raw) {
    const parsed = readArgsSchema.safeParse(raw);
    if (!parsed.success) {
        return { ok: false, error: formatZodError(parsed.error) };
    }
    return { ok: true, data: parsed.data };
}
function formatRange(startLine, endLine) {
    if (!endLine) {
        return `${startLine}-EOF`;
    }
    return `${startLine}-${endLine}`;
}
function formatReadToolResultText(args) {
    const parts = [];
    const body = args.content.trimEnd();
    if (body) {
        parts.push(body);
    }
    if (args.truncation.truncated || args.captureTruncated) {
        if (parts.length > 0) {
            parts.push("");
        }
        parts.push(`truncated for model: ${args.truncation.outputLines} of ${args.totalLines} lines. read smaller chunks with startLine/endLine to see more.`);
    }
    return parts.join("\n");
}
function buildReadUiText(args) {
    const { modelTruncation, startLine, endLine, fullText, totalLines, captureTruncated } = args;
    const totalLinesForSummary = modelTruncation.truncated || captureTruncated ? totalLines : modelTruncation.outputLines;
    const previewContentLines = buildHeadTailPreviewLines(modelTruncation.content, {
        headLines: 5,
        tailLines: 5,
    });
    const infoText = `${totalLinesForSummary} lines · ${formatRange(startLine, endLine)}`;
    const summaryLine = modelTruncation.truncated || captureTruncated ? `${TRUNCATION_MARKER} · ${infoText}` : infoText;
    const previewLines = previewContentLines.map((text) => ({ text }));
    const trimmedFullText = fullText.trimEnd();
    const fullLines = trimmedFullText
        ? trimmedFullText.split("\n").map((text) => ({ text }))
        : [];
    return {
        previewLines,
        statusLine: summaryLine,
        fullLines,
    };
}
export function createReadToolDefinition(backend) {
    return {
        schema: READ_TOOL,
        async dispatch(toolCall, _riskLevel) {
            const parsedArgs = parseReadArgs(toolCall.arguments);
            const path = parsedArgs.ok ? parsedArgs.data.path : "";
            const headerTarget = path || "(invalid arguments)";
            const blocked = (reason) => {
                const toolResult = createToolError(toolCall, reason);
                const uiEvent = {
                    type: "read_blocked",
                    toolCallId: toolCall.id,
                    path: path || "(invalid path)",
                    headerTarget,
                    reason,
                };
                return { kind: "single", toolResult, uiEvent };
            };
            if (!parsedArgs.ok) {
                return blocked(`invalid arguments: ${parsedArgs.error}`);
            }
            const { startLine, endLine } = parsedArgs.data;
            if (startLine !== undefined && endLine !== undefined && endLine < startLine) {
                return blocked("endLine must be >= startLine.");
            }
            try {
                const { path: resolvedPath, content: rawContent } = await backend.readFile(path);
                const allLines = rawContent.split("\n");
                const totalLines = allLines.length;
                const start = startLine ?? 1;
                const endRequested = endLine ?? totalLines;
                const endEffective = Math.min(endRequested, totalLines);
                const endDisplay = endLine === undefined ? undefined : endEffective;
                if (start > totalLines) {
                    return blocked(`startLine (${start}) exceeds total lines (${totalLines}).`);
                }
                const startIndex = Math.max(0, start - 1);
                const endIndex = Math.max(startIndex, endEffective);
                const selected = allLines.slice(startIndex, endIndex).join("\n");
                const selectedLines = Math.max(0, endIndex - startIndex);
                const selectedBytes = Buffer.byteLength(selected, "utf-8");
                const captureTruncated = selectedBytes > READ_MAX_CAPTURE_BYTES;
                const captured = captureTruncated
                    ? truncateToBytesFromStart(selected, READ_MAX_CAPTURE_BYTES)
                    : selected;
                const modelTruncation = truncateForTokens(captured, {
                    maxTokens: READ_TOOL_MAX_TOKENS,
                    strategy: "head",
                });
                const toolText = formatReadToolResultText({
                    content: modelTruncation.content,
                    truncation: modelTruncation,
                    captureTruncated,
                    totalLines: selectedLines,
                });
                const uiText = buildReadUiText({
                    modelTruncation,
                    startLine: start,
                    endLine: endDisplay,
                    fullText: toolText,
                    totalLines: selectedLines,
                    captureTruncated,
                });
                const toolResult = createToolResult(toolCall, toolText, false);
                const uiEvent = {
                    type: "read_success",
                    toolCallId: toolCall.id,
                    path: resolvedPath,
                    headerTarget: resolvedPath,
                    startLine: start,
                    endLine: endDisplay,
                    content: modelTruncation.content,
                    modelTruncation: {
                        truncated: modelTruncation.truncated,
                        totalLines: modelTruncation.totalLines,
                        outputLines: modelTruncation.outputLines,
                    },
                    uiText,
                };
                return { kind: "single", toolResult, uiEvent };
            }
            catch (e) {
                const errorMessage = e instanceof Error ? e.message : String(e);
                return blocked(`read failed: ${errorMessage}`);
            }
        },
    };
}
//# sourceMappingURL=read.js.map