/**
 * Session Store — persists chat session messages in SQLite.
 *
 * Each session holds an ordered array of {role, content} messages.
 * Used for: context recovery after CLI process crash, background task
 * context loading, and session history.
 */

import crypto from 'crypto';
import { getDb } from './db.mjs';

/**
 * Create a new session.
 * @param {string} source - 'chat', 'background', etc.
 * @returns {string} sessionId (UUID)
 */
export function createSession(source = 'chat') {
  const id = crypto.randomUUID();
  getDb().prepare(`
    INSERT INTO sessions (id, source, messages_json, token_estimate)
    VALUES (?, ?, '[]', 0)
  `).run(id, source);
  return id;
}

/**
 * Get a session by ID.
 * @param {string} sessionId
 * @returns {{ id, source, messages, token_estimate, created_at, updated_at } | null}
 */
export function getSession(sessionId) {
  const row = getDb().prepare('SELECT * FROM sessions WHERE id = ?').get(sessionId);
  if (!row) return null;
  return {
    ...row,
    messages: JSON.parse(row.messages_json)
  };
}

/**
 * Get session messages as an array.
 * @param {string} sessionId
 * @returns {Array<{role: string, content: string}>}
 */
export function getSessionMessages(sessionId) {
  const row = getDb().prepare('SELECT messages_json FROM sessions WHERE id = ?').get(sessionId);
  if (!row) return [];
  return JSON.parse(row.messages_json);
}

/**
 * Append a message to a session and update token estimate.
 * @param {string} sessionId
 * @param {string} role - 'user' or 'assistant'
 * @param {string} content
 */
export function appendMessage(sessionId, role, content) {
  const row = getDb().prepare('SELECT messages_json, token_estimate FROM sessions WHERE id = ?').get(sessionId);
  if (!row) throw new Error(`Session ${sessionId} not found`);

  const messages = JSON.parse(row.messages_json);
  messages.push({ role, content });

  const newTokens = estimateTokens([{ role, content }]);
  const totalTokens = (row.token_estimate || 0) + newTokens;

  getDb().prepare(`
    UPDATE sessions
    SET messages_json = ?, token_estimate = ?, updated_at = datetime('now')
    WHERE id = ?
  `).run(JSON.stringify(messages), totalTokens, sessionId);
}

/**
 * Replace all session messages (used after compaction).
 * @param {string} sessionId
 * @param {Array<{role: string, content: string}>} messages
 */
export function setSessionMessages(sessionId, messages) {
  const tokens = estimateTokens(messages);
  getDb().prepare(`
    UPDATE sessions
    SET messages_json = ?, token_estimate = ?, updated_at = datetime('now')
    WHERE id = ?
  `).run(JSON.stringify(messages), tokens, sessionId);
}

/**
 * Compact a session if token estimate exceeds threshold.
 * Summarizes the oldest 70% of messages, keeps recent 30%.
 * @param {string} sessionId
 * @param {function} askLLMFn - async (prompt, opts) => string
 */
export async function compactSession(sessionId, askLLMFn) {
  const session = getSession(sessionId);
  if (!session) return;
  if (session.token_estimate < 80000) return;

  const messages = session.messages;
  if (messages.length < 4) return;

  const splitIdx = Math.floor(messages.length * 0.7);
  const toSummarize = messages.slice(0, splitIdx);
  const toKeep = messages.slice(splitIdx);

  const text = toSummarize.map(m =>
    `${m.role === 'user' ? 'Human' : 'Assistant'}: ${m.content.slice(0, 500)}`
  ).join('\n\n');

  const summary = await askLLMFn(
    `Summarize this conversation history concisely. Capture: key topics discussed, decisions made, tasks requested, and important context. Keep it under 400 words.\n\n${text}`,
    { model: 'fast' }
  );

  if (summary && summary.trim()) {
    const compacted = [
      { role: 'assistant', content: `[Session summary — ${toSummarize.length} messages compacted]\n\n${summary}` },
      ...toKeep
    ];
    setSessionMessages(sessionId, compacted);
  }
}

/**
 * Delete sessions older than maxAgeMins.
 * @param {number} maxAgeMins
 * @returns {number} count of deleted sessions
 */
export function expireSessions(maxAgeMins = 1440) {
  const result = getDb().prepare(`
    DELETE FROM sessions
    WHERE updated_at < datetime('now', ? || ' minutes')
  `).run(`-${maxAgeMins}`);
  return result.changes;
}

/**
 * Estimate token count for an array of messages.
 * Rough heuristic: chars / 4.
 * @param {Array<{role: string, content: string}>} messages
 * @returns {number}
 */
export function estimateTokens(messages) {
  let chars = 0;
  for (const msg of messages) {
    chars += (msg.content || '').length;
    chars += (msg.role || '').length + 4; // role overhead
  }
  return Math.ceil(chars / 4);
}

/**
 * Delete a specific session.
 * @param {string} sessionId
 */
export function deleteSession(sessionId) {
  getDb().prepare('DELETE FROM sessions WHERE id = ?').run(sessionId);
}

/**
 * List all sessions.
 * @param {{ limit?: number }} opts
 * @returns {Array}
 */
export function listSessions({ limit = 50 } = {}) {
  return getDb().prepare(
    'SELECT id, source, token_estimate, created_at, updated_at FROM sessions ORDER BY updated_at DESC LIMIT ?'
  ).all(limit);
}
