# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
LLM-powered memory consolidation summarizer.

Provides a summarize_fn for MemorySystem.consolidate() that replaces the
naive " | ".join fallback with an LLM call that synthesizes related
memories into coherent summaries.

Usage:
    from .memory_consolidation import create_llm_summarizer

    summarizer = create_llm_summarizer(provider)
    memory_system.consolidate(threshold=0.3, summarize_fn=summarizer)
"""

import logging
from typing import Callable, Optional

logger = logging.getLogger(__name__)

_SUMMARIZE_SYSTEM = (
    "You are a memory consolidation assistant. "
    "Given a list of related memory fragments, combine them into a single concise summary. "
    "Preserve key facts, names, dates, and important details. "
    "Remove redundancy and merge overlapping information. "
    "Respond with ONLY the summary text, no preamble or explanation. "
    "Keep the summary under 200 words."
)


def _naive_fallback(memories) -> str:
    """Fallback summarizer matching EpisodicMemory._create_summary()."""
    contents = [m.content for m in memories]
    if len(contents) <= 3:
        return " | ".join(contents)
    summary = " | ".join(contents[:3])
    return f"{summary} (and {len(contents) - 3} more related items)"


def create_llm_summarizer(
    provider: Optional[object] = None,
    max_memories: int = 20,
) -> Callable:
    """
    Create an LLM-powered summarize_fn for MemorySystem.consolidate().

    Args:
        provider: An LLM provider with a .chat() method. If None, returns
                  the naive fallback summarizer.
        max_memories: Cap on memories sent to the LLM to avoid token overflow.

    Returns:
        A callable(List[Memory]) -> str suitable for consolidate(summarize_fn=...).
    """
    if provider is None:
        return _naive_fallback

    def summarize(memories) -> str:
        if not memories:
            return ""

        # Cap input to avoid token overflow
        capped = memories[:max_memories]

        # Build the prompt from memory contents
        fragments = []
        for i, m in enumerate(capped, 1):
            fragments.append(f"{i}. {m.content}")
        user_msg = "Consolidate these memory fragments:\n\n" + "\n".join(fragments)

        try:
            response = provider.chat(
                messages=[{"role": "user", "content": user_msg}],
                tools=[],
                system=_SUMMARIZE_SYSTEM,
                max_tokens=512,
            )
            text = (response.text or "").strip()
            if text:
                return text
        except Exception as e:
            logger.warning(f"LLM consolidation failed, using fallback: {e}")

        # Fallback on any failure
        return _naive_fallback(capped)

    return summarize
