/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { SchemaValidator } from '../utils/schemaValidator.js';
import { BaseTool, ToolConfirmationOutcome, Icon, } from './tools.js';
import { Type } from '@google/genai';
import { ApprovalMode } from '../config/config.js';
import { getResponseText } from '../utils/generateContentResponseUtilities.js';
import { fetchWithTimeout } from '../utils/fetch.js';
import { convert } from 'html-to-text';
import { ProxyAgent, setGlobalDispatcher } from 'undici';
const URL_FETCH_TIMEOUT_MS = 10000;
const MAX_CONTENT_LENGTH = 100000;
/**
 * Implementation of the WebFetch tool logic
 */
export class WebFetchTool extends BaseTool {
    config;
    static Name = 'web_fetch';
    constructor(config) {
        super(WebFetchTool.Name, 'WebFetch', 'Fetches content from a specified URL and processes it using an AI model\n- Takes a URL and a prompt as input\n- Fetches the URL content, converts HTML to markdown\n- Processes the content with the prompt using a small, fast model\n- Returns the model\'s response about the content\n- Use this tool when you need to retrieve and analyze web content\n\nUsage notes:\n  - IMPORTANT: If an MCP-provided web fetch tool is available, prefer using that tool instead of this one, as it may have fewer restrictions. All MCP-provided tools start with "mcp__".\n  - The URL must be a fully-formed valid URL\n  - The prompt should describe what information you want to extract from the page\n  - This tool is read-only and does not modify any files\n  - Results may be summarized if the content is very large', Icon.Globe, {
            properties: {
                url: {
                    description: 'The URL to fetch content from',
                    type: Type.STRING,
                },
                prompt: {
                    description: 'The prompt to run on the fetched content',
                    type: Type.STRING,
                },
            },
            required: ['url', 'prompt'],
            type: Type.OBJECT,
        });
        this.config = config;
        const proxy = config.getProxy();
        if (proxy) {
            setGlobalDispatcher(new ProxyAgent(proxy));
        }
    }
    async executeFetch(params, signal) {
        let url = params.url;
        // Convert GitHub blob URL to raw URL
        if (url.includes('github.com') && url.includes('/blob/')) {
            url = url
                .replace('github.com', 'raw.githubusercontent.com')
                .replace('/blob/', '/');
        }
        try {
            const response = await fetchWithTimeout(url, URL_FETCH_TIMEOUT_MS);
            if (!response.ok) {
                throw new Error(`Request failed with status code ${response.status} ${response.statusText}`);
            }
            const html = await response.text();
            const textContent = convert(html, {
                wordwrap: false,
                selectors: [
                    { selector: 'a', options: { ignoreHref: true } },
                    { selector: 'img', format: 'skip' },
                ],
            }).substring(0, MAX_CONTENT_LENGTH);
            const geminiClient = this.config.getGeminiClient();
            const fallbackPrompt = `The user requested the following: "${params.prompt}".

I have fetched the content from ${params.url}. Please use the following content to answer the user's request.

---
${textContent}
---`;
            const result = await geminiClient.generateContent([{ role: 'user', parts: [{ text: fallbackPrompt }] }], {}, signal);
            const resultText = getResponseText(result) || '';
            return {
                llmContent: resultText,
                returnDisplay: `Content from ${params.url} processed successfully.`,
            };
        }
        catch (e) {
            const error = e;
            const errorMessage = `Error during fetch for ${url}: ${error.message}`;
            return {
                llmContent: `Error: ${errorMessage}`,
                returnDisplay: `Error: ${errorMessage}`,
            };
        }
    }
    validateParams(params) {
        const errors = SchemaValidator.validate(this.schema.parameters, params);
        if (errors) {
            return errors;
        }
        if (!params.url || params.url.trim() === '') {
            return "The 'url' parameter cannot be empty.";
        }
        if (!params.url.startsWith('http://') &&
            !params.url.startsWith('https://')) {
            return "The 'url' must be a valid URL starting with http:// or https://.";
        }
        if (!params.prompt || params.prompt.trim() === '') {
            return "The 'prompt' parameter cannot be empty.";
        }
        return null;
    }
    getDescription(params) {
        const displayPrompt = params.prompt.length > 100
            ? params.prompt.substring(0, 97) + '...'
            : params.prompt;
        return `Fetching content from ${params.url} and processing with prompt: "${displayPrompt}"`;
    }
    async shouldConfirmExecute(params) {
        if (this.config.getApprovalMode() === ApprovalMode.AUTO_EDIT) {
            return false;
        }
        const validationError = this.validateParams(params);
        if (validationError) {
            return false;
        }
        const confirmationDetails = {
            type: 'info',
            title: `Confirm Web Fetch`,
            prompt: `Fetch content from ${params.url} and process with: ${params.prompt}`,
            urls: [params.url],
            onConfirm: async (outcome) => {
                if (outcome === ToolConfirmationOutcome.ProceedAlways) {
                    this.config.setApprovalMode(ApprovalMode.AUTO_EDIT);
                }
            },
        };
        return confirmationDetails;
    }
    async execute(params, signal) {
        const validationError = this.validateParams(params);
        if (validationError) {
            return {
                llmContent: `Error: Invalid parameters provided. Reason: ${validationError}`,
                returnDisplay: validationError,
            };
        }
        return this.executeFetch(params, signal);
    }
}
//# sourceMappingURL=web-fetch.js.map