'use strict';

const { estimateTokens, estimateMessagesTokens } = require('./token-counter');

const DEFAULT_CONTEXT_WINDOW = 200000; // Haiku 4.5 — 200K context
const COMPACTION_THRESHOLD = 0.75;     // Compact at 75% of context window
const KEEP_RECENT_MESSAGES = 4;        // Always keep last 4 messages verbatim

/**
 * Check whether the conversation should be compacted.
 * @param {Array} messages
 * @param {number} systemPromptTokens - estimated tokens in the system prompt
 * @param {number} contextWindow
 * @returns {boolean}
 */
function shouldCompact(messages, systemPromptTokens, contextWindow = DEFAULT_CONTEXT_WINDOW) {
  const totalTokens = (systemPromptTokens || 0) + estimateMessagesTokens(messages);
  return totalTokens > COMPACTION_THRESHOLD * contextWindow;
}

/**
 * Immediately truncate a tool result based on tool type.
 * Called right after each tool execution to keep results lean.
 * @param {string} toolName
 * @param {string} resultStr - JSON string of the tool result
 * @returns {string} - truncated JSON string
 */
function compactToolResult(toolName, resultStr) {
  if (!resultStr) return resultStr;

  // Plug-prompt-catalog Item E: try registered plugin hooks first.
  // Plugins can register per-tool compaction logic via
  // registerCompactionHook(); we consult them BEFORE the built-in
  // heuristics. Hook returning null = defer to built-in below.
  try {
    const { tryCompact } = require('./compaction-hooks');
    const hookResult = tryCompact(toolName, resultStr);
    if (hookResult && typeof hookResult.compacted === 'string') {
      return hookResult.compacted;
    }
  } catch {}

  // think tool: keep full — cheap and important for reasoning
  if (toolName === 'think') return resultStr;

  // search_memories: parse and keep top 5 results
  if (toolName === 'search_memories') {
    try {
      const parsed = JSON.parse(resultStr);
      if (parsed.memories && Array.isArray(parsed.memories)) {
        parsed.memories = parsed.memories.slice(0, 5);
        parsed.count = parsed.memories.length;
        parsed._compacted = true;
        return JSON.stringify(parsed);
      }
    } catch {
      // If parse fails, fall through to default truncation
    }
  }

  // Slack / MCP calls: truncate to 2000 chars
  if (toolName === 'mcp_call' || toolName.startsWith('slack_')) {
    if (resultStr.length > 2000) {
      return resultStr.slice(0, 2000) + '... [truncated]';
    }
    return resultStr;
  }

  // calendar_events: keep all events but strip verbose fields (notes, location, full attendee list)
  if (toolName === 'calendar_events') {
    try {
      const parsed = JSON.parse(resultStr);
      if (parsed.events && Array.isArray(parsed.events)) {
        parsed.events = parsed.events.map(e => ({
          title: e.title,
          start: e.start,
          end: e.end,
          calendar: e.calendar,
          allDay: e.allDay || undefined,
          attendees: e.attendees && e.attendees.length > 3
            ? e.attendees.slice(0, 3).concat(`+${e.attendees.length - 3} more`)
            : e.attendees,
        }));
        parsed._compacted = true;
        return JSON.stringify(parsed);
      }
    } catch {}
  }

  // Default: truncate to 3000 chars
  if (resultStr.length > 3000) {
    return resultStr.slice(0, 3000) + '... [truncated]';
  }
  return resultStr;
}

/**
 * Progressively compact messages by summarizing older history.
 * Keeps the last KEEP_RECENT_MESSAGES verbatim and summarizes everything else.
 *
 * @param {Array} messages - conversation messages
 * @param {object} provider - Unified LLM provider (must have provider.chat())
 * @param {object} opts - { contextWindow, model }
 * @returns {Promise<{ messages: Array, summary: string, tokensBeforeCompaction: number, tokensAfterCompaction: number }>}
 */
async function compactMessages(messages, provider, opts = {}) {
  const tokensBeforeCompaction = estimateMessagesTokens(messages);

  if (messages.length <= KEEP_RECENT_MESSAGES) {
    return {
      messages,
      summary: null,
      tokensBeforeCompaction,
      tokensAfterCompaction: tokensBeforeCompaction,
    };
  }

  // Split: older messages to summarize, recent to keep verbatim
  const olderMessages = messages.slice(0, messages.length - KEEP_RECENT_MESSAGES);
  const recentMessages = messages.slice(messages.length - KEEP_RECENT_MESSAGES);

  // Build a text representation of older messages for summarization
  const olderText = olderMessages.map(m => {
    const role = m.role || 'unknown';
    let content = '';
    if (typeof m.content === 'string') {
      content = m.content;
    } else if (Array.isArray(m.content)) {
      content = m.content.map(block => {
        if (block.type === 'text') return block.text;
        if (block.type === 'tool_use') return `[Tool: ${block.name}(${JSON.stringify(block.input).slice(0, 200)})]`;
        if (block.type === 'tool_result') return `[Result: ${(typeof block.content === 'string' ? block.content : JSON.stringify(block.content)).slice(0, 300)}]`;
        return `[${block.type}]`;
      }).join('\n');
    }
    return `${role}: ${content}`;
  }).join('\n\n');

  // Call Claude to summarize
  const model = opts.model || 'claude-haiku-4-5-20251001';
  const summaryPrompt = `Summarize this conversation history into a compact reference block. Preserve:
- All decisions made and their rationale
- Action items and pending tasks
- Key identifiers (names, IDs, URLs, file paths)
- Any constraints or commitments stated
- The current topic/thread being discussed
- Tool results and their key findings

Be concise but preserve ALL important details. Output only the summary, no preamble.

CONVERSATION HISTORY:
${olderText.slice(0, 50000)}`;

  let summary;
  try {
    const summaryResponse = await provider.chat({
      model,
      maxTokens: 1024,
      messages: [{ role: 'user', content: summaryPrompt }],
    });
    summary = summaryResponse.content || '';
  } catch (err) {
    console.error('[compactor] Summarization failed, using naive truncation:', err.message);
    // Fallback: just take the last portion of older text
    summary = 'Previous conversation summary (auto-truncated):\n' + olderText.slice(-2000);
  }

  // Build compacted messages: summary as first message, then recent messages
  const compactedMessages = [
    { role: 'user', content: `[Conversation history summary]\n${summary}` },
    { role: 'assistant', content: 'Understood. I have the conversation context. Continuing from where we left off.' },
    ...recentMessages,
  ];

  const tokensAfterCompaction = estimateMessagesTokens(compactedMessages);
  console.log(`[compactor] Compacted ${messages.length} messages (${tokensBeforeCompaction} tokens) -> ${compactedMessages.length} messages (${tokensAfterCompaction} tokens)`);

  return {
    messages: compactedMessages,
    summary,
    tokensBeforeCompaction,
    tokensAfterCompaction,
  };
}

module.exports = {
  DEFAULT_CONTEXT_WINDOW,
  COMPACTION_THRESHOLD,
  KEEP_RECENT_MESSAGES,
  shouldCompact,
  compactToolResult,
  compactMessages,
};
