/** LTM retrieval — search + prompt context injection (English, LLM-to-LLM) */

import * as Log from "@saeeol/core/util/log"
import * as Store from "@/ltm/store"
import * as Embedder from "@/provider/local/embedder"
import type { Memory, LTMConfig } from "@/ltm/types"

const log = Log.create({ service: "ltm/retrieval" })

/** Search memories relevant to a query */
export async function search(
  query: string,
  config: LTMConfig,
): Promise<Memory[]> {
  try {
    const vector = await Embedder.embedOne(query)
    return Store.search(vector, {
      topK: config.retrieval.topK,
      minScore: config.retrieval.minScore,
    })
  } catch (e) {
    log.error("retrieval search failed", { error: e })
    return []
  }
}

/** Format memories as prompt context block (English, consumed by LLM) */
export function format(memories: Memory[]): string {
  if (memories.length === 0) return ""

  const lines = memories.map((m) => {
    const date = new Date(m.metadata.timestamp).toISOString().slice(0, 10)
    const tag = m.type
    const summary = m.summary.slice(0, 200)
    return `- [${date}] (${tag}) ${summary}`
  })

  return `[long-term memory — ${memories.length} recalled]\n${lines.join("\n")}`
}

/** Inject relevant memories into the session prompt */
export async function inject(
  query: string,
  config: LTMConfig,
): Promise<string | undefined> {
  const memories = await search(query, config)
  if (memories.length === 0) return undefined

  const formatted = format(memories)
  const tokenEstimate = Math.ceil(formatted.length / 4)

  if (tokenEstimate > config.retrieval.maxTokens) {
    const kept = Math.max(1, Math.floor(memories.length * config.retrieval.maxTokens / tokenEstimate))
    const trimmed = memories.slice(0, kept)
    const reduced = format(trimmed)
    log.info("retrieval trimmed", { original: memories.length, trimmed: trimmed.length })
    return reduced
  }

  log.info("retrieval injected", { count: memories.length, tokens: tokenEstimate })
  return formatted
}
