import { useEffect, useRef } from 'react';
import { getModelContextLimit } from '../models/index.js';
import { getTuneToolMode } from '../types/config.js';
import { calculateTokenBreakdown, calculateToolDefinitionsTokensFromDefs, } from '../usage/calculator.js';
import { resolveContextUsage } from '../usage/context-source.js';
import { getLastBuiltPrompt } from '../utils/prompt-builder.js';
export function useContextPercentage({ currentModel, currentProvider, currentProviderConfig, messages, tokenizer, getMessageTokens, toolManager, streamingTokenCount, contextLimit, lastApiUsage, setContextPercentUsed, setContextLimit, setContextSource, developmentMode = 'normal', tune, }) {
    const lastResolvedKeyRef = useRef('');
    // Effect 1: Resolve context limit when model or provider changes. The
    // resolved limit is published to `contextLimit` (state), which Effect 2
    // depends on — so the percentage recomputes against the new model's window
    // as soon as it resolves (not just on the next message).
    useEffect(() => {
        if (!currentModel) {
            lastResolvedKeyRef.current = '';
            setContextLimit(null);
            setContextPercentUsed(null);
            setContextSource(null);
            return;
        }
        const resolutionKey = `${currentProvider}:${currentModel}`;
        if (resolutionKey === lastResolvedKeyRef.current)
            return;
        lastResolvedKeyRef.current = resolutionKey;
        let cancelled = false;
        void getModelContextLimit(currentModel, {
            providerConfig: currentProviderConfig ?? undefined,
        }).then(limit => {
            if (cancelled)
                return;
            setContextLimit(limit);
            if (!limit) {
                setContextPercentUsed(null);
                setContextSource(null);
            }
        });
        return () => {
            cancelled = true;
        };
    }, [
        currentModel,
        currentProvider,
        currentProviderConfig,
        setContextLimit,
        setContextPercentUsed,
        setContextSource,
    ]);
    // Effect 2: Recalculate percentage. Mirrors the /usage command exactly:
    // the tool-definition overhead counts only the tools actually exposed to
    // the model (profile + mode filtered), and the prompt/tools/limit all
    // re-resolve when the model, mode, tune profile, or window changes.
    useEffect(() => {
        if (!contextLimit) {
            setContextPercentUsed(null);
            setContextSource(null);
            return;
        }
        // Use the cached prompt which includes XML tool definitions when applicable
        const systemPrompt = getLastBuiltPrompt();
        const systemMessage = {
            role: 'system',
            content: systemPrompt,
        };
        const breakdown = calculateTokenBreakdown([systemMessage, ...messages], tokenizer, (message) => {
            // System message won't be in the cache, use tokenizer directly
            if (message.role === 'system') {
                return tokenizer.countTokens(message);
            }
            return getMessageTokens(message);
        });
        // Tool definition overhead — only when native tool calling is active, and
        // only for the tools actually exposed (profile + mode filtered). Under
        // XML/JSON fallback the definitions already live inside the system prompt.
        const nativeToolsDisabled = currentProviderConfig?.disableTools === true ||
            (currentProviderConfig?.disableToolModels?.includes(currentModel) ??
                false) ||
            getTuneToolMode(tune) !== 'native';
        const toolDefTokens = toolManager && !nativeToolsDisabled
            ? calculateToolDefinitionsTokensFromDefs(toolManager.getFilteredTools(toolManager.getAvailableToolNames(tune, developmentMode, undefined, currentModel)), tokenizer)
            : 0;
        const total = breakdown.total + toolDefTokens + streamingTokenCount;
        // Estimate of only the messages appended since the API snapshot was taken,
        // plus the in-flight streaming reply. The API total already accounts for
        // the system prompt, tool definitions and history up to `atMessageCount`,
        // so the anchor must add nothing but this fresh tail. Guard the slice
        // against a snapshot whose count overtook the conversation (stale across a
        // clear/compaction) — `resolveContextUsage` ignores that snapshot anyway.
        const apiAtCount = lastApiUsage?.atMessageCount ?? null;
        let tailTokens = streamingTokenCount;
        if (apiAtCount !== null && apiAtCount <= messages.length) {
            for (let i = apiAtCount; i < messages.length; i++) {
                tailTokens += getMessageTokens(messages[i]);
            }
        }
        // Anchor on API-reported usage and estimate only the fresh tail; fall back
        // to the full client-side estimate when there's no usable snapshot.
        const { percent, source } = resolveContextUsage({
            estimatedTotalTokens: total,
            estimatedTailTokens: tailTokens,
            apiSnapshot: lastApiUsage,
            currentMessageCount: messages.length,
            contextLimit,
        });
        setContextPercentUsed(percent);
        setContextSource(source);
        // contextLimit is included to re-trigger calculation after async limit resolution
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [
        messages,
        tokenizer,
        getMessageTokens,
        toolManager,
        streamingTokenCount,
        lastApiUsage,
        setContextPercentUsed,
        setContextSource,
        tune,
        developmentMode,
        currentModel,
        currentProviderConfig,
        contextLimit,
    ]);
}
//# sourceMappingURL=useContextPercentage.js.map