'use strict';

const WALLE_SESSION_CACHE_PARSER_VERSION = 2;

function extractText(value) {
  if (value === undefined || value === null) return '';
  if (typeof value === 'string') return value;
  if (Array.isArray(value)) {
    return value
      .map((block) => {
        if (!block) return '';
        if (typeof block === 'string') return block;
        return block.text || block.content || '';
      })
      .filter(Boolean)
      .join('\n');
  }
  if (typeof value === 'object') return value.text || value.content || '';
  return String(value);
}

function messageText(message) {
  if (!message || typeof message !== 'object') return '';
  if (message.content !== undefined) return extractText(message.content);
  if (message.text !== undefined) return extractText(message.text);
  if (message.message?.content !== undefined) return extractText(message.message.content);
  return '';
}

function normalizeMessage(message) {
  const role = String(message?.role || message?.message?.role || '').trim();
  if (role !== 'user' && role !== 'assistant') return null;
  const content = messageText(message).trim();
  if (!content) return null;
  const out = {
    role,
    content,
    timestamp: message.timestamp || message.created_at || message.createdAt || '',
  };
  const model = message.model_id || message.model || message.message?.model || '';
  if (model) {
    out.model = model;
    out.model_id = model;
  }
  const latency = message.latency_ms ?? message.latencyMs;
  if (latency !== undefined && latency !== null) out.latency_ms = latency;
  if (Array.isArray(message.toolCalls)) out.toolCalls = JSON.parse(JSON.stringify(message.toolCalls));
  if (Array.isArray(message.tool_calls)) out.toolCalls = JSON.parse(JSON.stringify(message.tool_calls));
  if (message.metadata && typeof message.metadata === 'object') {
    out.metadata = JSON.parse(JSON.stringify(message.metadata));
  }
  if (message.attachments) {
    try {
      out.attachments = typeof message.attachments === 'string'
        ? JSON.parse(message.attachments)
        : JSON.parse(JSON.stringify(message.attachments));
    } catch {
      out.attachments = message.attachments;
    }
  }
  return out;
}

function firstText(messages, role) {
  const found = messages.find((m) => m.role === role && m.content);
  return found ? found.content : '';
}

function lastText(messages, role) {
  for (let i = messages.length - 1; i >= 0; i--) {
    if (messages[i].role === role && messages[i].content) return messages[i].content;
  }
  return '';
}

function statSize(fileStat) {
  return Number.isFinite(fileStat?.size) ? fileStat.size : 0;
}

function statTimestamp(fileStat) {
  if (!fileStat?.mtime) return '';
  if (typeof fileStat.mtime === 'string') return fileStat.mtime;
  if (fileStat.mtime instanceof Date) return fileStat.mtime.toISOString();
  return '';
}

function walleConversationCacheIdsForSource(source) {
  const ids = [];
  const add = (value) => {
    const clean = String(value || '').trim();
    if (clean && !ids.includes(clean)) ids.push(clean);
  };

  // Prefer the active provider transcript identity. CTM-id cache rows are a
  // durable fallback, but they can outlive a Wall-E restart that moved the
  // active transcript from <ctm>.jsonl to walle-<ctm>.jsonl.
  add(source?.agentSessionId);
  add(source?.chatSessionId);
  add(source?.conversationSessionId);
  add(source?.ctmSessionId);
  add(source?.id);
  return ids;
}

function walleConversationCacheFreshness(conversation, source, fileStat, options = {}) {
  if (!conversation || typeof conversation !== 'object') {
    return { fresh: false, reason: 'missing-cache', cachedSize: 0, expectedSize: 0 };
  }
  const minParserVersion = Number.isFinite(Number(options.minParserVersion))
    ? Number(options.minParserVersion)
    : WALLE_SESSION_CACHE_PARSER_VERSION;
  const parserVersion = Number(conversation.import_parser_version || 0) || 0;
  if (minParserVersion > 0 && parserVersion < minParserVersion) {
    return {
      fresh: false,
      reason: 'parser-version-stale',
      parserVersion,
      expectedParserVersion: minParserVersion,
      cachedSize: Number(conversation.file_size || 0) || 0,
      expectedSize: statSize(fileStat),
      sourcePath: source?.jsonlPath || '',
    };
  }
  const expectedSize = statSize(fileStat);
  if (!expectedSize) {
    return {
      fresh: true,
      reason: 'no-active-file-stat',
      cachedSize: Number(conversation.file_size || 0) || 0,
      expectedSize: 0,
    };
  }
  const cachedSize = Number(conversation.file_size || 0) || 0;
  if (cachedSize !== expectedSize) {
    return {
      fresh: false,
      reason: 'active-jsonl-size-mismatch',
      cachedSize,
      expectedSize,
      sourcePath: source?.jsonlPath || '',
    };
  }
  return { fresh: true, reason: 'active-jsonl-size-match', cachedSize, expectedSize };
}

function selectFreshWalleSessionConversationCache({
  source,
  fileStat,
  getConversation,
  parseMessages,
  minParserVersion,
} = {}) {
  if (!source || typeof getConversation !== 'function' || typeof parseMessages !== 'function') {
    return { messages: null, sourceId: '', stale: [], checkedIds: [] };
  }
  const ids = walleConversationCacheIdsForSource(source);
  const stale = [];
  for (const id of ids) {
    let conversation = null;
    try {
      conversation = getConversation(id);
    } catch (e) {
      stale.push({ id, reason: 'cache-read-error', error: e.message });
      continue;
    }
    if (!conversation || !conversation.messages) continue;
    const freshness = walleConversationCacheFreshness(conversation, source, fileStat, { minParserVersion });
    if (!freshness.fresh) {
      stale.push({ id, ...freshness });
      continue;
    }
    let messages = null;
    try {
      messages = parseMessages(conversation);
    } catch (e) {
      stale.push({ id, reason: 'cache-parse-error', error: e.message });
      continue;
    }
    if (Array.isArray(messages) && messages.length > 0) {
      return { messages, sourceId: id, stale, checkedIds: ids };
    }
  }
  return { messages: null, sourceId: '', stale, checkedIds: ids };
}

function buildWalleSessionConversation({
  source,
  history,
  fileStat,
  hostname,
} = {}) {
  const messages = (Array.isArray(history) ? history : [])
    .map(normalizeMessage)
    .filter(Boolean);
  const firstUser = firstText(messages, 'user');
  const lastUser = lastText(messages, 'user');
  const firstAssistant = firstText(messages, 'assistant');
  const firstTimestamp = messages.find((m) => m.timestamp)?.timestamp || '';
  const modelId = source?.model_id || source?.model || messages.find((m) => m.model_id || m.model)?.model_id || messages.find((m) => m.model)?.model || '';
  const modelProvider = source?.model_provider || source?.provider || '';

  return {
    session_id: source?.conversationSessionId || source?.agentSessionId || source?.chatSessionId || source?.ctmSessionId || source?.id || '',
    project_path: source?.cwd || source?.project_path || source?.projectPath || '',
    messages,
    user_msg_count: messages.filter((m) => m.role === 'user').length,
    assistant_msg_count: messages.filter((m) => m.role === 'assistant').length,
    title: source?.title || source?.label || firstUser,
    first_message: firstUser,
    git_branch: source?.git_branch || source?.gitBranch || '',
    file_size: statSize(fileStat),
    session_created_at: firstTimestamp || source?.created_at || source?.createdAt || '',
    hostname: hostname || source?.hostname || '',
    model_provider: modelProvider,
    model_id: modelId,
    import_parser_version: WALLE_SESSION_CACHE_PARSER_VERSION,
    last_user_content: lastUser,
    first_assistant_text: firstAssistant,
    rename_name: source?.rename_name || source?.renameName || '',
    modified_at: statTimestamp(fileStat),
  };
}

function persistWalleSessionConversation({
  db,
  source,
  history,
  fileStat,
  hostname,
} = {}) {
  if (!db || typeof db.importSessionConversation !== 'function') return 0;
  const payload = buildWalleSessionConversation({ source, history, fileStat, hostname });
  if (!payload.session_id || payload.messages.length === 0) return 0;
  db.importSessionConversation(payload);
  if (typeof db.replaceSessionMessages === 'function') {
    db.replaceSessionMessages(payload.session_id, payload.messages);
  }
  return payload.messages.length;
}

module.exports = {
  buildWalleSessionConversation,
  selectFreshWalleSessionConversationCache,
  WALLE_SESSION_CACHE_PARSER_VERSION,
  walleConversationCacheFreshness,
  walleConversationCacheIdsForSource,
  normalizeMessage,
  persistWalleSessionConversation,
};
