import { i as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIT1-v_a.js";
import { S as resolveThinkingDefault, T as normalizeModelRef, _ as resolvePersistedOverrideModelRef, h as resolveModelRefFromString, r as buildConfiguredModelCatalog, t as buildAllowedModelSet, w as modelKey, y as resolveReasoningDefault } from "./model-selection-DCVjDyLz.js";
import { r as normalizeProviderId } from "./provider-id-Cy-cze0N.js";
import { i as resolveAgentConfig } from "./agent-scope-BA9YeMqS.js";
import "./defaults-COZq2n5p.js";
import { n as resolveContextTokensForModel } from "./context-Cy3UPG6J.js";
import { t as applyModelOverrideToSessionEntry } from "./model-overrides-BQZfy4TT.js";
import { t as clearSessionAuthProfileOverride } from "./session-override-DRIjT1WJ.js";
import { t as resolveStoredModelOverride } from "./stored-model-override-DjzJZdau.js";
//#region src/auto-reply/reply/model-selection.ts
function createFastTestModelSelectionState(params) {
	return {
		provider: params.provider,
		model: params.model,
		allowedModelKeys: /* @__PURE__ */ new Set(),
		allowedModelCatalog: [],
		resetModelOverride: false,
		resolveDefaultThinkingLevel: async () => params.agentCfg?.thinkingDefault,
		resolveDefaultReasoningLevel: async () => "off",
		needsModelCatalog: false
	};
}
function shouldLogModelSelectionTiming() {
	return process.env.KAIJIBOT_DEBUG_INGRESS_TIMING === "1";
}
let modelCatalogRuntimePromise;
let sessionStoreRuntimePromise;
function loadModelCatalogRuntime() {
	modelCatalogRuntimePromise ??= import("./agents/model-catalog.runtime.js");
	return modelCatalogRuntimePromise;
}
function loadSessionStoreRuntime() {
	sessionStoreRuntimePromise ??= import("./store.runtime-GfL88qFV.js");
	return sessionStoreRuntimePromise;
}
const FUZZY_VARIANT_TOKENS = [
	"lightning",
	"preview",
	"mini",
	"fast",
	"turbo",
	"lite",
	"beta",
	"small",
	"nano"
];
function boundedLevenshteinDistance(a, b, maxDistance) {
	if (a === b) return 0;
	if (!a || !b) return null;
	const aLen = a.length;
	const bLen = b.length;
	if (Math.abs(aLen - bLen) > maxDistance) return null;
	const prev = Array.from({ length: bLen + 1 }, (_, idx) => idx);
	const curr = Array.from({ length: bLen + 1 }, () => 0);
	for (let i = 1; i <= aLen; i++) {
		curr[0] = i;
		let rowMin = curr[0];
		const aChar = a.charCodeAt(i - 1);
		for (let j = 1; j <= bLen; j++) {
			const cost = aChar === b.charCodeAt(j - 1) ? 0 : 1;
			curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
			if (curr[j] < rowMin) rowMin = curr[j];
		}
		if (rowMin > maxDistance) return null;
		for (let j = 0; j <= bLen; j++) prev[j] = curr[j] ?? 0;
	}
	const dist = prev[bLen] ?? null;
	if (dist == null || dist > maxDistance) return null;
	return dist;
}
function scoreFuzzyMatch(params) {
	const provider = normalizeProviderId(params.provider);
	const model = params.model;
	const fragment = normalizeLowercaseStringOrEmpty(params.fragment);
	const providerLower = normalizeLowercaseStringOrEmpty(provider);
	const modelLower = normalizeLowercaseStringOrEmpty(model);
	const haystack = `${providerLower}/${modelLower}`;
	const key = modelKey(provider, model);
	const scoreFragment = (value, weights) => {
		if (!fragment) return 0;
		let score = 0;
		if (value === fragment) score = Math.max(score, weights.exact);
		if (value.startsWith(fragment)) score = Math.max(score, weights.starts);
		if (value.includes(fragment)) score = Math.max(score, weights.includes);
		return score;
	};
	let score = 0;
	score += scoreFragment(haystack, {
		exact: 220,
		starts: 140,
		includes: 110
	});
	score += scoreFragment(providerLower, {
		exact: 180,
		starts: 120,
		includes: 90
	});
	score += scoreFragment(modelLower, {
		exact: 160,
		starts: 110,
		includes: 80
	});
	const distModel = boundedLevenshteinDistance(fragment, modelLower, 3);
	if (distModel != null) score += (3 - distModel) * 70;
	const aliases = params.aliasIndex.byKey.get(key) ?? [];
	for (const alias of aliases) score += scoreFragment(normalizeLowercaseStringOrEmpty(alias), {
		exact: 140,
		starts: 90,
		includes: 60
	});
	if (modelLower.startsWith(providerLower)) score += 30;
	const fragmentVariants = FUZZY_VARIANT_TOKENS.filter((token) => fragment.includes(token));
	const modelVariants = FUZZY_VARIANT_TOKENS.filter((token) => modelLower.includes(token));
	const variantMatchCount = fragmentVariants.filter((token) => modelLower.includes(token)).length;
	const variantCount = modelVariants.length;
	if (fragmentVariants.length === 0 && variantCount > 0) score -= variantCount * 30;
	else if (fragmentVariants.length > 0) {
		if (variantMatchCount > 0) score += variantMatchCount * 40;
		if (variantMatchCount === 0) score -= 20;
	}
	const isDefault = provider === normalizeProviderId(params.defaultProvider) && model === params.defaultModel;
	if (isDefault) score += 20;
	return {
		score,
		isDefault,
		variantCount,
		variantMatchCount,
		modelLength: modelLower.length,
		key
	};
}
async function createModelSelectionState(params) {
	const timingEnabled = shouldLogModelSelectionTiming();
	const startMs = timingEnabled ? Date.now() : 0;
	const logStage = (stage, extra) => {
		if (!timingEnabled) return;
		const suffix = extra ? ` ${extra}` : "";
		console.log(`[model-selection] session=${params.sessionKey ?? "(no-session)"} stage=${stage} elapsedMs=${Date.now() - startMs}${suffix}`);
	};
	const { cfg, agentCfg, sessionEntry, sessionStore, sessionKey, parentSessionKey, storePath, defaultProvider, defaultModel } = params;
	let provider = params.provider;
	let model = params.model;
	const hasAllowlist = agentCfg?.models && Object.keys(agentCfg.models).length > 0;
	const configuredModelCatalog = buildConfiguredModelCatalog({ cfg });
	const needsModelCatalog = params.hasModelDirective;
	let allowedModelKeys = /* @__PURE__ */ new Set();
	let allowedModelCatalog = configuredModelCatalog;
	let modelCatalog = null;
	let resetModelOverride = false;
	const agentEntry = params.agentId ? resolveAgentConfig(cfg, params.agentId) : void 0;
	const directStoredOverride = resolvePersistedOverrideModelRef({
		defaultProvider,
		overrideProvider: sessionEntry?.providerOverride,
		overrideModel: sessionEntry?.modelOverride
	});
	if (needsModelCatalog) {
		modelCatalog = await (await loadModelCatalogRuntime()).loadModelCatalog({ config: cfg });
		logStage("catalog-loaded", `entries=${modelCatalog.length}`);
		const allowed = buildAllowedModelSet({
			cfg,
			catalog: modelCatalog,
			defaultProvider,
			defaultModel,
			agentId: params.agentId
		});
		allowedModelCatalog = allowed.allowedCatalog;
		allowedModelKeys = allowed.allowedKeys;
		logStage("allowlist-built", `allowed=${allowedModelCatalog.length} keys=${allowedModelKeys.size}`);
	} else if (hasAllowlist) {
		const allowed = buildAllowedModelSet({
			cfg,
			catalog: configuredModelCatalog,
			defaultProvider,
			defaultModel,
			agentId: params.agentId
		});
		allowedModelCatalog = allowed.allowedCatalog;
		allowedModelKeys = allowed.allowedKeys;
		logStage("configured-allowlist-built", `allowed=${allowedModelCatalog.length} keys=${allowedModelKeys.size}`);
	} else if (configuredModelCatalog.length > 0) logStage("configured-catalog-ready", `entries=${configuredModelCatalog.length}`);
	if (sessionEntry && sessionStore && sessionKey && directStoredOverride) {
		const normalizedOverride = normalizeModelRef(directStoredOverride.provider, directStoredOverride.model);
		const key = modelKey(normalizedOverride.provider, normalizedOverride.model);
		if (allowedModelKeys.size > 0 && !allowedModelKeys.has(key)) {
			const { updated } = applyModelOverrideToSessionEntry({
				entry: sessionEntry,
				selection: {
					provider: defaultProvider,
					model: defaultModel,
					isDefault: true
				}
			});
			if (updated) {
				sessionStore[sessionKey] = sessionEntry;
				if (storePath) await (await loadSessionStoreRuntime()).updateSessionStore(storePath, (store) => {
					store[sessionKey] = sessionEntry;
				});
			}
			resetModelOverride = updated;
		}
	}
	const storedOverride = resolveStoredModelOverride({
		sessionEntry,
		sessionStore,
		sessionKey,
		parentSessionKey,
		defaultProvider
	});
	const skipStoredOverride = params.hasResolvedHeartbeatModelOverride === true;
	if (storedOverride?.model && !skipStoredOverride) {
		const normalizedStoredOverride = normalizeModelRef(storedOverride.provider || defaultProvider, storedOverride.model);
		const key = modelKey(normalizedStoredOverride.provider, normalizedStoredOverride.model);
		if (allowedModelKeys.size === 0 || allowedModelKeys.has(key)) {
			provider = normalizedStoredOverride.provider;
			model = normalizedStoredOverride.model;
		}
	}
	if (sessionEntry && sessionStore && sessionKey && sessionEntry.authProfileOverride) {
		const { ensureAuthProfileStore } = await import("./agents/auth-profiles.runtime.js");
		const store = ensureAuthProfileStore(void 0, { allowKeychainPrompt: false });
		logStage("auth-profile-store-loaded", `profiles=${Object.keys(store.profiles).length}`);
		const profile = store.profiles[sessionEntry.authProfileOverride];
		const providerKey = normalizeProviderId(provider);
		if (!profile || normalizeProviderId(profile.provider) !== providerKey) await clearSessionAuthProfileOverride({
			sessionEntry,
			sessionStore,
			sessionKey,
			storePath
		});
	}
	let defaultThinkingLevel;
	const resolveDefaultThinkingLevel = async () => {
		if (defaultThinkingLevel) return defaultThinkingLevel;
		let catalogForThinking = modelCatalog ?? allowedModelCatalog;
		if (!catalogForThinking || catalogForThinking.length === 0) {
			modelCatalog = await (await loadModelCatalogRuntime()).loadModelCatalog({ config: cfg });
			logStage("catalog-loaded-for-thinking", `entries=${modelCatalog.length}`);
			catalogForThinking = modelCatalog;
		}
		const resolved = resolveThinkingDefault({
			cfg,
			provider,
			model,
			catalog: catalogForThinking
		});
		defaultThinkingLevel = agentEntry?.thinkingDefault ?? resolved ?? agentCfg?.thinkingDefault ?? "off";
		return defaultThinkingLevel;
	};
	const resolveDefaultReasoningLevel = async () => {
		let catalogForReasoning = modelCatalog ?? allowedModelCatalog;
		if (!catalogForReasoning || catalogForReasoning.length === 0) {
			modelCatalog = await (await loadModelCatalogRuntime()).loadModelCatalog({ config: cfg });
			logStage("catalog-loaded-for-reasoning", `entries=${modelCatalog.length}`);
			catalogForReasoning = modelCatalog;
		}
		return resolveReasoningDefault({
			provider,
			model,
			catalog: catalogForReasoning
		});
	};
	return {
		provider,
		model,
		allowedModelKeys,
		allowedModelCatalog,
		resetModelOverride,
		resolveDefaultThinkingLevel,
		resolveDefaultReasoningLevel,
		needsModelCatalog
	};
}
function resolveModelDirectiveSelection(params) {
	const { raw, defaultProvider, defaultModel, aliasIndex, allowedModelKeys } = params;
	const rawTrimmed = raw.trim();
	const rawLower = normalizeLowercaseStringOrEmpty(rawTrimmed);
	const pickAliasForKey = (provider, model) => aliasIndex.byKey.get(modelKey(provider, model))?.[0];
	const buildSelection = (provider, model) => {
		const alias = pickAliasForKey(provider, model);
		return {
			provider,
			model,
			isDefault: provider === defaultProvider && model === defaultModel,
			...alias ? { alias } : void 0
		};
	};
	const resolveFuzzy = (params) => {
		const fragment = normalizeLowercaseStringOrEmpty(params.fragment);
		if (!fragment) return {};
		const providerFilter = params.provider ? normalizeProviderId(params.provider) : void 0;
		const candidates = [];
		for (const key of allowedModelKeys) {
			const slash = key.indexOf("/");
			if (slash <= 0) continue;
			const provider = normalizeProviderId(key.slice(0, slash));
			const model = key.slice(slash + 1);
			if (providerFilter && provider !== providerFilter) continue;
			candidates.push({
				provider,
				model
			});
		}
		if (!params.provider) {
			const aliasMatches = [];
			for (const [aliasKey, entry] of aliasIndex.byAlias.entries()) {
				if (!aliasKey.includes(fragment)) continue;
				aliasMatches.push({
					provider: entry.ref.provider,
					model: entry.ref.model
				});
			}
			for (const match of aliasMatches) {
				const key = modelKey(match.provider, match.model);
				if (!allowedModelKeys.has(key)) continue;
				if (!candidates.some((c) => c.provider === match.provider && c.model === match.model)) candidates.push(match);
			}
		}
		if (candidates.length === 0) return {};
		const bestScored = candidates.map((candidate) => {
			const details = scoreFuzzyMatch({
				provider: candidate.provider,
				model: candidate.model,
				fragment,
				aliasIndex,
				defaultProvider,
				defaultModel
			});
			return Object.assign({ candidate }, details);
		}).toSorted((a, b) => {
			if (b.score !== a.score) return b.score - a.score;
			if (a.isDefault !== b.isDefault) return a.isDefault ? -1 : 1;
			if (a.variantMatchCount !== b.variantMatchCount) return b.variantMatchCount - a.variantMatchCount;
			if (a.variantCount !== b.variantCount) return a.variantCount - b.variantCount;
			if (a.modelLength !== b.modelLength) return a.modelLength - b.modelLength;
			return a.key.localeCompare(b.key);
		})[0];
		const best = bestScored?.candidate;
		if (!best || !bestScored) return {};
		const minScore = providerFilter ? 90 : 120;
		if (bestScored.score < minScore) return {};
		return { selection: buildSelection(best.provider, best.model) };
	};
	const resolved = resolveModelRefFromString({
		raw: rawTrimmed,
		defaultProvider,
		aliasIndex
	});
	if (!resolved) {
		const fuzzy = resolveFuzzy({ fragment: rawTrimmed });
		if (fuzzy.selection || fuzzy.error) return fuzzy;
		return { error: `Unrecognized model "${rawTrimmed}". Use /models to list providers, or /models <provider> to list models.` };
	}
	const resolvedKey = modelKey(resolved.ref.provider, resolved.ref.model);
	if (allowedModelKeys.size === 0 || allowedModelKeys.has(resolvedKey)) return { selection: {
		provider: resolved.ref.provider,
		model: resolved.ref.model,
		isDefault: resolved.ref.provider === defaultProvider && resolved.ref.model === defaultModel,
		alias: resolved.alias
	} };
	if (rawLower.includes("/")) {
		const slash = rawTrimmed.indexOf("/");
		const fuzzy = resolveFuzzy({
			provider: normalizeProviderId(rawTrimmed.slice(0, slash).trim()),
			fragment: rawTrimmed.slice(slash + 1).trim()
		});
		if (fuzzy.selection || fuzzy.error) return fuzzy;
	}
	const fuzzy = resolveFuzzy({ fragment: rawTrimmed });
	if (fuzzy.selection || fuzzy.error) return fuzzy;
	return { error: `Model "${resolved.ref.provider}/${resolved.ref.model}" is not allowed. Use /models to list providers, or /models <provider> to list models.` };
}
function resolveContextTokens(params) {
	return params.agentCfg?.contextTokens ?? resolveContextTokensForModel({
		cfg: params.cfg,
		provider: params.provider,
		model: params.model,
		allowAsyncLoad: false
	}) ?? 2e5;
}
//#endregion
export { resolveModelDirectiveSelection as i, createModelSelectionState as n, resolveContextTokens as r, createFastTestModelSelectionState as t };
