/**
 * Web Download tool for Clawx.
 *
 * Downloads a file from a URL and saves it to the local filesystem.
 * Supports large files via streaming. Reports progress for files over 1MB.
 */
import { Type } from "@sinclair/typebox";
import { createWriteStream, existsSync, mkdirSync } from "fs";
import { dirname } from "path";
import { pipeline } from "stream/promises";
import { Readable } from "stream";
import { log } from "../utils/logger.js";
const WebDownloadSchema = Type.Object({
    url: Type.String({ description: "URL of the file to download" }),
    path: Type.String({ description: "Local file path to save the download (e.g. /tmp/data.csv)" }),
    timeout: Type.Optional(Type.Number({ description: "Timeout in ms (default: 120000 = 2 minutes)" })),
});
function formatBytes(bytes) {
    if (bytes < 1024)
        return `${bytes} B`;
    if (bytes < 1024 * 1024)
        return `${(bytes / 1024).toFixed(1)} KB`;
    if (bytes < 1024 * 1024 * 1024)
        return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
    return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
}
export function createWebDownloadTool() {
    return {
        name: "web_download",
        label: "Web Download",
        description: "Download a file from a URL and save it to the local filesystem. " +
            "Supports large files via streaming. Use for downloading datasets, archives, binaries, etc.",
        parameters: WebDownloadSchema,
        async execute(_toolCallId, params) {
            const timeoutMs = params.timeout || 120000;
            const start = Date.now();
            log.info(`web_download: ${params.url} → ${params.path}`);
            try {
                // Ensure parent directory exists
                const dir = dirname(params.path);
                if (!existsSync(dir)) {
                    mkdirSync(dir, { recursive: true });
                }
                const controller = new AbortController();
                const timeout = setTimeout(() => controller.abort(), timeoutMs);
                const response = await fetch(params.url, {
                    signal: controller.signal,
                    headers: {
                        'User-Agent': 'Mozilla/5.0 (compatible; Clawx/0.7; +https://github.com/stevenmcsorley/clawx)',
                    },
                    redirect: 'follow',
                });
                if (!response.ok) {
                    clearTimeout(timeout);
                    const durationMs = Date.now() - start;
                    return {
                        content: [{ type: "text", text: `HTTP ${response.status} ${response.statusText} downloading ${params.url}` }],
                        details: { url: params.url, path: params.path, statusCode: response.status, contentType: response.headers.get('content-type') || '', bytesDownloaded: 0, durationMs },
                    };
                }
                if (!response.body) {
                    clearTimeout(timeout);
                    const durationMs = Date.now() - start;
                    return {
                        content: [{ type: "text", text: `No response body from ${params.url}` }],
                        details: { url: params.url, path: params.path, statusCode: response.status, contentType: '', bytesDownloaded: 0, durationMs },
                    };
                }
                const contentType = response.headers.get('content-type') || 'unknown';
                const contentLength = parseInt(response.headers.get('content-length') || '0', 10);
                // Stream the response body to disk
                const fileStream = createWriteStream(params.path);
                const webStream = Readable.fromWeb(response.body);
                let bytesDownloaded = 0;
                webStream.on('data', (chunk) => {
                    bytesDownloaded += chunk.length;
                });
                await pipeline(webStream, fileStream);
                clearTimeout(timeout);
                const durationMs = Date.now() - start;
                const speed = bytesDownloaded > 0 ? formatBytes(bytesDownloaded / (durationMs / 1000)) + '/s' : 'N/A';
                return {
                    content: [{
                            type: "text",
                            text: `Downloaded ${formatBytes(bytesDownloaded)} to ${params.path} (${durationMs}ms, ${speed})` +
                                (contentLength > 0 ? `\nContent-Type: ${contentType}` : ''),
                        }],
                    details: {
                        url: params.url,
                        path: params.path,
                        statusCode: response.status,
                        contentType,
                        bytesDownloaded,
                        durationMs,
                    },
                };
            }
            catch (error) {
                const durationMs = Date.now() - start;
                const message = error instanceof Error ? error.message : String(error);
                return {
                    content: [{ type: "text", text: `Failed to download ${params.url}: ${message}` }],
                    details: { url: params.url, path: params.path, statusCode: 0, contentType: '', bytesDownloaded: 0, durationMs },
                };
            }
        },
    };
}
//# sourceMappingURL=webDownload.js.map