'use strict';

/**
 * Conversation history composer — Item F of prompt-catalog deep-dive.
 *
 * Inspired by OpenClaw's `src/gateway/agent-prompt.ts:
 * buildAgentMessageFromConversationEntries`. A uniform helper for
 * building "history as context + current message" payloads, so chat,
 * boot-check, heartbeat, and future loop ticks all share one
 * composition shape.
 *
 * Use cases:
 *   - chat: take last N user/assistant entries, format with sender
 *     prefixes, present to the LLM as a single user message
 *   - boot-check: NO history — just the BOOT.md content (this module
 *     trivially handles that case)
 *   - heartbeat: NO history (intentionally context-isolated per
 *     OpenClaw's heartbeat anti-rehash rule)
 *
 * The composer is intentionally pure (no DB, no I/O) so it tests as a
 * plain unit and behaves identically across loops.
 *
 * Entry shape:
 *   { role: 'user' | 'assistant' | 'tool', sender?: string, body: string }
 *
 * - body MUST be a string. If callers have rich content (arrays of
 *   text blocks), they extract text first via `extractTextFromBody`.
 * - sender is optional metadata used in formatted history; defaults
 *   to capitalized role.
 */

/**
 * Coerce a possibly-rich body to a flat string. Mirrors OpenClaw's
 * `extractTextFromChatContent`. Handles:
 *   - strings (passthrough)
 *   - content-array shapes [{type: 'text', text: '...'}, ...]
 *   - undefined / null (returns '')
 */
function extractTextFromBody(body) {
  if (typeof body === 'string') return body;
  if (Array.isArray(body)) {
    return body
      .map((b) => {
        if (typeof b === 'string') return b;
        if (b && typeof b.text === 'string') return b.text;
        return '';
      })
      .filter(Boolean)
      .join('\n');
  }
  if (body && typeof body.text === 'string') return body.text;
  return '';
}

/**
 * Format a single history entry for inclusion in the user message.
 * sender prefix + body, e.g. "user: Hi" or "assistant: Hello".
 */
function formatEntry(entry) {
  const text = extractTextFromBody(entry?.body);
  const sender = entry?.sender || (entry?.role
    ? entry.role[0].toUpperCase() + entry.role.slice(1)
    : 'unknown');
  return `${sender}: ${text}`;
}

/**
 * Build a single user message from a conversation entry list.
 *
 * Logic:
 *   1. Find the last user/tool entry → that's the "current message"
 *   2. Everything before it is "history"
 *   3. Compose: history (formatted) + "\n\n" + current body
 *
 * Returns the empty string if no entries are provided.
 *
 * @param {Array<{role, sender?, body}>} entries
 * @param {object} [opts]
 * @param {number} [opts.maxHistoryEntries] - Cap history length (default 20)
 * @param {string} [opts.historyHeader] - Prefix for the history block
 * @returns {string}
 */
function buildAgentMessage(entries, opts = {}) {
  if (!Array.isArray(entries) || entries.length === 0) return '';

  // Find last user/tool entry as the current
  let currentIdx = -1;
  for (let i = entries.length - 1; i >= 0; i--) {
    const role = entries[i]?.role;
    if (role === 'user' || role === 'tool') {
      currentIdx = i;
      break;
    }
  }
  if (currentIdx < 0) currentIdx = entries.length - 1;

  const current = entries[currentIdx];
  const currentText = extractTextFromBody(current?.body);
  if (!currentText && currentIdx === 0) return '';

  const historyEntries = entries.slice(0, currentIdx);
  const maxH = opts.maxHistoryEntries ?? 20;
  const trimmedHistory =
    historyEntries.length > maxH ? historyEntries.slice(-maxH) : historyEntries;

  if (trimmedHistory.length === 0) return currentText;

  const header = opts.historyHeader || 'Conversation so far:';
  const formatted = trimmedHistory.map(formatEntry).join('\n');
  return `${header}\n${formatted}\n\nCurrent message:\n${currentText}`;
}

/**
 * Build a "history-only" preview — useful for prompt inspection /
 * debugging endpoints, never includes the current message.
 */
function buildHistoryPreview(entries, opts = {}) {
  if (!Array.isArray(entries) || entries.length === 0) return '';
  const maxH = opts.maxHistoryEntries ?? 20;
  const trimmed = entries.length > maxH ? entries.slice(-maxH) : entries;
  return trimmed.map(formatEntry).join('\n');
}

module.exports = {
  buildAgentMessage,
  buildHistoryPreview,
  formatEntry,
  extractTextFromBody,
};
