/**
 * External sources for brand voice analysis — SSRF-safe URL fetch and pasted uploads.
 */
import { safeFetch, SsrfBlockedError } from '../security/safe-fetch.js';
import { stripHtmlTags } from '../seo/analysis.js';
export const MAX_ANALYZE_URLS = 5;
export const MAX_ANALYZE_UPLOADS = 5;
export const MAX_UPLOAD_TEXT_CHARS = 15_000;
export const MAX_URL_FETCH_CHARS = 8_000;
export const MAX_URL_FETCH_BYTES = 512 * 1024;
function clampText(text, max) {
    return text.replace(/\s+/g, ' ').trim().slice(0, max);
}
function extractHtmlTitle(html) {
    const match = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(html);
    if (!match?.[1])
        return 'Web page';
    return stripHtmlTags(match[1]).slice(0, 200) || 'Web page';
}
function extractReadableText(html) {
    const withoutScripts = html
        .replace(/<script[\s\S]*?<\/script>/gi, ' ')
        .replace(/<style[\s\S]*?<\/style>/gi, ' ')
        .replace(/<nav[\s\S]*?<\/nav>/gi, ' ')
        .replace(/<footer[\s\S]*?<\/footer>/gi, ' ');
    return clampText(stripHtmlTags(withoutScripts), MAX_URL_FETCH_CHARS);
}
export class BrandVoiceSourceError extends Error {
    constructor(message) {
        super(message);
        this.name = 'BrandVoiceSourceError';
    }
}
/** Fetch and extract readable text from a public URL (SSRF-safe, no redirects). */
export async function fetchBrandVoiceUrlSample(url) {
    let parsed;
    try {
        parsed = new URL(url);
    }
    catch {
        throw new BrandVoiceSourceError(`Invalid URL: ${url}`);
    }
    if (!['http:', 'https:'].includes(parsed.protocol)) {
        throw new BrandVoiceSourceError('Only http and https URLs are supported.');
    }
    let response;
    try {
        response = await safeFetch(url, { method: 'GET', timeoutMs: 8000, followRedirects: false });
    }
    catch (err) {
        if (err instanceof SsrfBlockedError) {
            throw new BrandVoiceSourceError(`URL blocked for security: ${url}`);
        }
        throw new BrandVoiceSourceError(`Could not fetch ${url}`);
    }
    if (!response.ok) {
        throw new BrandVoiceSourceError(`URL returned HTTP ${response.status}: ${url}`);
    }
    const contentType = (response.headers.get('content-type') ?? '').split(';')[0]?.trim() ?? '';
    const buffer = Buffer.from(await response.arrayBuffer());
    if (buffer.byteLength === 0) {
        throw new BrandVoiceSourceError(`Empty response from ${url}`);
    }
    if (buffer.byteLength > MAX_URL_FETCH_BYTES) {
        throw new BrandVoiceSourceError(`Response too large from ${url}`);
    }
    const raw = buffer.toString('utf-8');
    let text;
    let title;
    if (contentType.includes('text/html') || raw.includes('<html')) {
        title = extractHtmlTitle(raw);
        text = extractReadableText(raw);
    }
    else if (contentType.startsWith('text/') || contentType.includes('markdown')) {
        title = parsed.hostname;
        text = clampText(raw, MAX_URL_FETCH_CHARS);
    }
    else {
        throw new BrandVoiceSourceError(`Unsupported content type "${contentType || 'unknown'}" from ${url}`);
    }
    if (!text) {
        throw new BrandVoiceSourceError(`No readable text found at ${url}`);
    }
    return {
        collection: 'url',
        documentId: url,
        title,
        text,
    };
}
export function uploadToBrandVoiceSample(name, text) {
    const trimmed = text.trim();
    if (!trimmed) {
        throw new BrandVoiceSourceError(`Upload "${name}" is empty.`);
    }
    if (trimmed.length > MAX_UPLOAD_TEXT_CHARS) {
        throw new BrandVoiceSourceError(`Upload "${name}" exceeds ${MAX_UPLOAD_TEXT_CHARS.toLocaleString()} characters.`);
    }
    return {
        collection: 'upload',
        documentId: name.slice(0, 120),
        title: name.slice(0, 200) || 'Uploaded document',
        text: trimmed,
    };
}
//# sourceMappingURL=brand-voice-sources.js.map