import { createLogger } from "../utils/logger/index.js";
import { init_logger } from "../utils/logger.js";
import { isCronSessionKey } from "../routing/session-key-utils.js";
import { init_session_key, parseSessionKey } from "../routing/session-key.js";
import { init_providers, resolveModel } from "../providers/index.js";
import { readAgentMessageContent } from "../agent/memory/agent-message-access.js";
import { stripSessionStartupContextFromUserText } from "../agent/reply/startup-context.js";
import { stripInboundFileMetadataFromText } from "../channels/attachments/inbound-persist.js";
import { stripEnvelopeTimestampPrefix } from "../channels/envelope-timestamp.js";
import { complete } from "@earendil-works/pi-ai";
//#region src/session/session-title.ts
init_session_key();
init_providers();
init_logger();
const log = createLogger("SessionAutoTitle");
const MAX_TITLE_LEN = 80;
/** Collect visible text from any content block that exposes `text` (pi-ai / OpenAI / Anthropic shapes). */
function extractTextFromMessage(m) {
	const raw = readAgentMessageContent(m);
	if (typeof raw === "string") return raw.trim();
	if (Array.isArray(raw)) {
		const parts = [];
		for (const c of raw) if (c && typeof c === "object") {
			const o = c;
			const type = typeof o.type === "string" ? o.type : "";
			if (type === "toolCall" || type === "tool_use" || type === "tool_result") continue;
			if (typeof o.text === "string" && o.text.trim()) parts.push(o.text.trim());
		}
		return parts.join(" ").trim();
	}
	return "";
}
function firstUserText(messages) {
	const u = messages.find((m) => m.role === "user");
	if (!u) return "";
	return stripInboundFileMetadataFromText(stripEnvelopeTimestampPrefix(stripSessionStartupContextFromUserText(extractTextFromMessage(u))));
}
/** First assistant message that has visible text (skips tool-only assistant rows). */
function firstAssistantText(messages) {
	for (const m of messages) if (m.role === "assistant") {
		const t = extractTextFromMessage(m);
		if (t.length > 0) return t;
	}
	return "";
}
function isWebchatSessionKey(sessionKey) {
	if (parseSessionKey(sessionKey)?.source === "webchat") return true;
	return sessionKey.includes(":webchat:");
}
/** Whether to run LLM/fallback session naming for this key (excludes cron, heartbeat). */
function shouldAutoTitleSessionKey(sessionKey) {
	const raw = (sessionKey ?? "").trim();
	if (!raw) return false;
	if (isCronSessionKey(raw)) return false;
	if (raw.toLowerCase().startsWith("heartbeat:")) return false;
	return true;
}
function sanitizeSessionTitle(raw) {
	let s = raw.trim();
	if (s.startsWith("\"") && s.endsWith("\"") || s.startsWith("'") && s.endsWith("'")) s = s.slice(1, -1).trim();
	const lineBreak = s.indexOf("\n");
	if (lineBreak !== -1) s = s.slice(0, lineBreak).trim();
	if (s.length > MAX_TITLE_LEN) s = s.slice(0, MAX_TITLE_LEN - 1).trimEnd() + "…";
	return s;
}
/** Non-LLM title: first line of first user text, else first assistant line. */
function fallbackTitleFromMessages(messages) {
	const u = firstUserText(messages);
	if (u) {
		const line = u.split(/\n/)[0]?.trim();
		if (line) return sanitizeSessionTitle(line);
	}
	const a = firstAssistantText(messages);
	if (a) {
		const line = a.split(/\n/)[0]?.trim();
		if (line) return sanitizeSessionTitle(line);
	}
	return null;
}
/**
* Returns a title string, or null if generation should be skipped or failed.
*/
async function generateSessionTitleFromMessages(modelRef, messages, signal) {
	const userText = firstUserText(messages);
	const assistantText = firstAssistantText(messages);
	if (!userText && !assistantText) return null;
	let model;
	try {
		model = resolveModel(modelRef);
	} catch (err) {
		log.warn({
			err,
			modelRef
		}, "Cannot resolve model for session title");
		return null;
	}
	const userMsg = {
		role: "user",
		content: userText && assistantText ? `You label chat sessions. Given the first user message and the start of the assistant reply, output ONE short title (max 8 words). No quotes. No punctuation at the end. Use the same language as the user when possible.

User: ${userText.slice(0, 2e3)}

Assistant: ${assistantText.slice(0, 2e3)}

Title:` : userText ? `The assistant reply only used tools (no visible text yet). Output ONE short title (max 8 words) based only on the user's first message. No quotes. No punctuation at the end. Use the same language as the user.

User: ${userText.slice(0, 2e3)}

Title:` : `Output ONE short title (max 8 words) for this assistant reply. No quotes. No punctuation at the end.

Assistant: ${assistantText.slice(0, 2e3)}

Title:`,
		timestamp: Date.now()
	};
	try {
		const result = await complete(model, { messages: [userMsg] }, {
			maxTokens: 64,
			temperature: .35,
			signal
		});
		let text = "";
		if (Array.isArray(result.content)) {
			for (const c of result.content) if (c && typeof c === "object" && c.type === "text") text += String(c.text || "");
		}
		const cleaned = sanitizeSessionTitle(text);
		return cleaned.length > 0 ? cleaned : null;
	} catch (err) {
		log.warn({ err }, "Session title LLM call failed");
		return null;
	}
}
/**
* If the session is still unnamed, set `name` (LLM when possible, else first-line fallback).
* Skips cron/heartbeat keys. Ensures index row exists by re-saving when metadata is missing (fixes index lag).
*/
async function maybeAutoTitleSessionStore(sessionStore, sessionKey, modelRef) {
	if (!shouldAutoTitleSessionKey(sessionKey)) return;
	let messages = await sessionStore.load(sessionKey);
	if (!messages.length) return;
	let meta = await sessionStore.getMetadata(sessionKey);
	if (!meta) {
		await sessionStore.saveMessages(sessionKey, messages);
		meta = await sessionStore.getMetadata(sessionKey);
	}
	if (!meta) {
		log.warn({ sessionKey }, "Session title: metadata missing after save");
		return;
	}
	if (meta.name && meta.name.trim().length > 0) return;
	let title = null;
	const ref = modelRef?.trim();
	if (ref) {
		const controller = new AbortController();
		const timeout = setTimeout(() => controller.abort(), 25e3);
		try {
			title = await generateSessionTitleFromMessages(ref, messages, controller.signal);
		} finally {
			clearTimeout(timeout);
		}
	}
	if (!title) title = fallbackTitleFromMessages(messages);
	if (!title) return;
	try {
		await sessionStore.updateMetadata(sessionKey, { name: title });
	} catch (err) {
		log.warn({
			err,
			sessionKey
		}, "Session title: updateMetadata failed");
	}
}
//#endregion
export { fallbackTitleFromMessages, generateSessionTitleFromMessages, isWebchatSessionKey, maybeAutoTitleSessionStore, sanitizeSessionTitle, shouldAutoTitleSessionKey };

//# sourceMappingURL=session-title.js.map