import { z } from 'zod';
import { truncateOutput } from './types.js';
/** Crude but dependency-free HTML-to-text: good enough for docs and articles. */
export function htmlToText(html) {
    return html
        .replace(/<script[\s\S]*?<\/script>/gi, '')
        .replace(/<style[\s\S]*?<\/style>/gi, '')
        .replace(/<noscript[\s\S]*?<\/noscript>/gi, '')
        .replace(/<!--[\s\S]*?-->/g, '')
        .replace(/<(br|\/p|\/div|\/li|\/h[1-6]|\/tr)\b[^>]*>/gi, '\n')
        .replace(/<li\b[^>]*>/gi, '- ')
        .replace(/<[^>]+>/g, '')
        .replace(/&nbsp;/g, ' ')
        .replace(/&amp;/g, '&')
        .replace(/&lt;/g, '<')
        .replace(/&gt;/g, '>')
        .replace(/&quot;/g, '"')
        .replace(/&#0?39;|&apos;/g, "'")
        .replace(/&#(\d+);/g, (_, n) => String.fromCodePoint(Number(n)))
        .replace(/[ \t]+/g, ' ')
        .replace(/\n\s*\n\s*\n+/g, '\n\n')
        .trim();
}
const FETCH_HEADERS = {
    'User-Agent': 'Mozilla/5.0 (compatible; CheapCode/0.4; +https://cheapcode.ai)',
    Accept: 'text/html,application/xhtml+xml,application/json;q=0.9,text/plain;q=0.8,*/*;q=0.5',
};
const fetchSchema = z.object({
    url: z.string().url(),
});
export const webFetchTool = {
    name: 'web_fetch',
    description: 'Fetch a URL and return its content as readable text (HTML is stripped to text; ' +
        'JSON and plain text are returned as-is). Use for documentation, articles, APIs, README files.',
    schema: fetchSchema,
    jsonSchema: {
        type: 'object',
        properties: {
            url: { type: 'string', description: 'The full URL to fetch (http or https)' },
        },
        required: ['url'],
    },
    readOnly: true,
    summarize: (i) => i.url,
    run: async (input) => {
        let res;
        try {
            res = await fetch(input.url, {
                headers: FETCH_HEADERS,
                redirect: 'follow',
                signal: AbortSignal.timeout(30_000),
            });
        }
        catch (err) {
            return `error: fetch failed: ${err.message}`;
        }
        if (!res.ok)
            return `error: HTTP ${res.status} from ${input.url}`;
        const contentType = res.headers.get('content-type') ?? '';
        const raw = await res.text();
        if (contentType.includes('html'))
            return truncateOutput(htmlToText(raw));
        return truncateOutput(raw);
    },
};
const searchSchema = z.object({
    query: z.string().min(1),
});
/**
 * Dependency-free web search via DuckDuckGo's HTML endpoint. Best-effort:
 * a scrape, not an API, so treat failures as soft and fall back to web_fetch
 * on a known site when it misbehaves.
 */
export const webSearchTool = {
    name: 'web_search',
    description: 'Search the web and return the top results as "title - url - snippet" lines. ' +
        'Follow up with web_fetch on promising results.',
    schema: searchSchema,
    jsonSchema: {
        type: 'object',
        properties: {
            query: { type: 'string', description: 'The search query' },
        },
        required: ['query'],
    },
    readOnly: true,
    summarize: (i) => i.query,
    run: async (input) => {
        let res;
        try {
            res = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(input.query)}`, {
                headers: FETCH_HEADERS,
                signal: AbortSignal.timeout(20_000),
            });
        }
        catch (err) {
            return `error: search failed: ${err.message}`;
        }
        if (!res.ok)
            return `error: search returned HTTP ${res.status}`;
        const html = await res.text();
        const results = [];
        const linkRe = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/g;
        const snippetRe = /<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g;
        const snippets = [...html.matchAll(snippetRe)].map((m) => htmlToText(m[1]));
        let m;
        let i = 0;
        while ((m = linkRe.exec(html)) !== null && results.length < 8) {
            let url = m[1];
            // DDG wraps result URLs in a redirect (//duckduckgo.com/l/?uddg=<encoded>).
            const uddg = url.match(/[?&]uddg=([^&]+)/);
            if (uddg)
                url = decodeURIComponent(uddg[1]);
            const title = htmlToText(m[2]);
            const snippet = snippets[i] ?? '';
            results.push(`${title}\n  ${url}${snippet ? `\n  ${snippet}` : ''}`);
            i++;
        }
        if (results.length === 0)
            return 'No results found (the search endpoint may be blocking; try web_fetch on a likely URL instead).';
        return results.join('\n\n');
    },
};
