/**
 * URL 컨텍스트 빌더 — 채팅 프롬프트에 URL 이 있으면 본문을 추출해 실데이터로 주입.
 *
 * 문제: /wikify 는 URL 에 접근하지만(브리지 /api/web-extract), 일반 채팅에 URL 을
 * 주면 추출 경로가 없어 모델이 "접근할 수 없습니다"라고 답하거나 내용을 추측했다.
 *
 * v2: Bridge 100% 의존이 두 번째 공백이었다 — Bridge 가 꺼져 있으면(확장은 자동
 * 시작하지 않음) 접근 실패 블록이 떠서 결국 "방문 불가"가 됐다. 이제 ① Bridge
 * 추출(품질 우선) → ② 직접 fetch 폴백(webFetch.fetchUrlDirect) → ③ 정직 실패
 * 블록의 3단계. 성공 결과는 5분 TTL 캐시 — chat/alignment/기업모드가 같은 URL 을
 * 연달아 요청해도 네트워크 1회.
 */
import { logInfo } from '../../utils';
import { bridgeFetch, BRIDGE_API } from '../../features/datacollect/bridgeClient';
import { fetchUrlDirect } from '../../features/web/webFetch';

const URL_RE = /https?:\/\/[^\s<>"'`)\]]+/i;
const MAX_BODY_CHARS = 8000;
// Bridge 추출 대기 — 직접 fetch 폴백이 있으므로 45s → 15s 로 단축 (총 대기 최대 ~30s).
const EXTRACT_TIMEOUT_MS = 15_000;

// 성공 블록만 캐시 (실패는 캐시하지 않음 — Bridge 재기동/일시 오류 후 재시도 가능해야).
const CACHE_TTL_MS = 5 * 60 * 1000;
const CACHE_MAX = 10;
const _cache = new Map<string, { block: string; expiresAt: number }>();

/** 테스트/세션 전환용 캐시 초기화. */
export function clearUrlContextCache(): void {
    _cache.clear();
}

/**
 * 프롬프트에서 추출 대상 URL 을 찾는다. 슬래시 명령(/wikify 등)은 자체 처리하므로
 * 제외. 없으면 null.
 */
export function extractUrlFromPrompt(prompt: string): string | null {
    const p = (prompt || '').trim();
    // 슬래시 *명령* 만 제외 — 절대경로 시작 프롬프트는 추출 대상 (경로 오인 버그 수정).
    if (!p || /^\/[a-zA-Z][\w-]*(\s|$)/.test(p)) return null;
    const m = URL_RE.exec(p);
    if (!m) return null;
    // 문장 끝 구두점이 URL 에 붙는 흔한 오염 제거.
    return m[0].replace(/[.,;:!?…」』)]+$/, '');
}

/** URL 본문을 추출해 컨텍스트 블록 생성. 실패해도 throw 하지 않는다 (정직 블록 반환). */
export async function buildUrlContext(url: string): Promise<string> {
    const now = Date.now();
    const cached = _cache.get(url);
    if (cached && cached.expiresAt > now) return cached.block;

    // ① Bridge 추출 (readability 급 품질 — 우선).
    let bridgeError = '';
    try {
        const data = await bridgeFetch<{ success: boolean; title?: string; description?: string; text?: string; textLength?: number; truncated?: boolean }>(
            BRIDGE_API.web.extract,
            { method: 'POST', body: JSON.stringify({ url }) },
            { timeoutMs: EXTRACT_TIMEOUT_MS },
        );
        const body = String(data?.text || '').slice(0, MAX_BODY_CHARS);
        if (body.trim() && body.trim().length >= 50) {
            logInfo('URL 컨텍스트 주입 (bridge).', { url, chars: body.length, truncated: !!data?.truncated });
            const block = _successBlock(url, data?.title || '', data?.description || '', body, body.length >= MAX_BODY_CHARS || !!data?.truncated);
            _cachePut(url, block);
            return block;
        }
        bridgeError = '본문 추출 실패 (콘텐츠 없음 또는 JS 전용 렌더링)';
    } catch (e: any) {
        bridgeError = String(e?.message ?? e).slice(0, 120);
    }

    // ② 직접 fetch 폴백 — Bridge 가 꺼져 있거나 추출 실패해도 웹 접근은 살아 있어야.
    const direct = await fetchUrlDirect(url, { maxChars: MAX_BODY_CHARS });
    if (direct.ok) {
        logInfo('URL 컨텍스트 주입 (direct fetch 폴백).', { url, chars: direct.text.length, bridgeError });
        const block = _successBlock(url, direct.title, '', direct.text, direct.text.length >= MAX_BODY_CHARS);
        _cachePut(url, block);
        return block;
    }

    // ③ 둘 다 실패 — 모델이 내용을 지어내지 않게 정직 블록.
    return [
        `[URL CONTENT — ${url}]`,
        `상태: 접근 실패 — bridge: ${bridgeError || '(미시도)'} / direct: ${direct.error || '(불명)'}`,
        '→ 사용자에게 이 URL 의 본문을 가져오지 못했다고 정직하게 알리고, 내용을 추측해 답하지 마라.',
    ].join('\n');
}

function _successBlock(url: string, title: string, description: string, body: string, truncated: boolean): string {
    return [
        `[URL CONTENT — 실데이터 · ${url}]`,
        `제목: ${title || '(없음)'}`,
        description ? `설명: ${description}` : '',
        '아래 본문만 근거로 답하라. 본문에 없는 내용은 "본문에서 확인되지 않음"이라고 답하고 지어내지 마라.' +
        (truncated ? ' (본문 일부 잘림 — 전체가 필요하면 /wikify 사용을 안내하라.)' : ''),
        '',
        body,
    ].filter(Boolean).join('\n');
}

function _cachePut(url: string, block: string): void {
    if (_cache.size >= CACHE_MAX) {
        // 가장 오래된 entry 제거 (Map 삽입 순서).
        const oldest = _cache.keys().next().value;
        if (oldest !== undefined) _cache.delete(oldest);
    }
    _cache.set(url, { block, expiresAt: Date.now() + CACHE_TTL_MS });
}
