import type { InferenceContext } from './inference/index.js';
import { taskModelCompletion } from './task-model.js';

const NAMING_SYSTEM_PROMPT =
  'You name chat sessions. Reply with ONLY the title. 4 to 6 words. No quotes, no punctuation, no prefix.';

const MAX_TITLE_CHARS = 80;

function cleanTitle(raw: string): string {
  let name = raw.trim();
  const quotes = ['"', "'", '`', '‘', '’', '“', '”'];
  while (name.length >= 2 && quotes.includes(name[0]!) && quotes.includes(name[name.length - 1]!)) {
    name = name.slice(1, -1).trim();
  }
  name = name.replace(/^title\s*:\s*/i, '').trim();
  if (name.length > MAX_TITLE_CHARS) {
    name = name.slice(0, MAX_TITLE_CHARS).trim();
  }
  return name;
}

export async function maybeAutoNameChat(
  ctx: InferenceContext,
  chatId: string,
  sessionId: string
): Promise<void> {
  const counts = await ctx.sql<{ n: number }[]>`
    SELECT COUNT(*)::int AS n
    FROM messages
    WHERE chat_id = ${chatId}
      AND role = 'assistant'
      AND status = 'complete'
      AND content <> ''
  `;
  if ((counts[0]?.n ?? 0) < 1) return;

  const chatRows = await ctx.sql<
    { id: string; name: string | null; session_id: string; model: string | null }[]
  >`
    SELECT c.id, c.name, c.session_id, s.model
    FROM chats c JOIN sessions s ON s.id = c.session_id
    WHERE c.id = ${chatId}
  `;
  const chat = chatRows[0];
  if (!chat) return;
  if (chat.name !== null && chat.name !== '') return;

  const firstMsgs = await ctx.sql<{ role: string; content: string }[]>`
    SELECT role, content FROM messages
    WHERE chat_id = ${chatId}
      AND role IN ('user', 'assistant')
      AND status IN ('complete', 'ok')
      AND content <> ''
    ORDER BY created_at ASC
    LIMIT 2
  `;
  const userMsg = firstMsgs.find(m => m.role === 'user');
  const assistantMsg = firstMsgs.find(m => m.role === 'assistant');
  if (!assistantMsg) return;

  let namingInput = '';
  if (userMsg) namingInput += `User: ${userMsg.content.slice(0, 1000)}\n\n`;
  namingInput += `Assistant: ${assistantMsg.content.slice(0, 1000)}`;

  const raw = await taskModelCompletion({
    system: NAMING_SYSTEM_PROMPT,
    user: namingInput,
    maxTokens: 30,
    temperature: 0.3,
    fallbackModel: chat.model ?? undefined,
  });
  const name = cleanTitle(raw);
  if (!name) {
    ctx.log.warn({ chatId, raw }, 'auto-name: empty title from model');
    return;
  }

  const updated = await ctx.sql<{ id: string; name: string; session_id: string; updated_at: string }[]>`
    UPDATE chats
    SET name = ${name}, updated_at = clock_timestamp()
    WHERE id = ${chatId}
      AND (name IS NULL OR name = '')
    RETURNING id, name, session_id, updated_at
  `;
  if (updated.length === 0) return;

  ctx.publish(sessionId, {
    type: 'chat_renamed',
    chat_id: chatId,
    name,
  });
  ctx.publishUser({
    type: 'chat_updated',
    chat_id: chatId,
    session_id: sessionId,
    name,
    updated_at: updated[0]!.updated_at,
  });
  ctx.log.info({ chatId, name }, 'chat auto-named');

  // Propagate to the parent session if it's still on its default name.
  // The WHERE guard makes the check atomic — if the user has already
  // renamed (or a prior chat already propagated), this UPDATE matches
  // zero rows and we do nothing. First chat wins; manual renames win.
  const renamedSession = await ctx.sql<{ id: string; name: string }[]>`
    UPDATE sessions
    SET name = ${name}
    WHERE id = ${sessionId} AND name = 'New session'
    RETURNING id, name
  `;
  if (renamedSession.length > 0) {
    ctx.publishUser({
      type: 'session_renamed',
      session_id: sessionId,
      name,
    });
    ctx.log.info({ sessionId, name }, 'session auto-named from chat');
  }
}
