"""Memory Digest Engine — builds compressed <memory_digest> XML blocks.

Fetches top-K memories from each store, applies decay scoring,
and compresses into a <150 token context block for LLM injection.
"""

from __future__ import annotations

from dataclasses import dataclass


@dataclass
class DreamReport:
    """Report from a dream consolidation run."""
    promoted: int = 0
    contradictions_resolved: int = 0
    decayed: int = 0


class DigestEngine:
    """Builds compressed memory digest blocks for LLM context injection.

    The digest is a structured XML block containing the most relevant
    memories from all three types, compressed to fit within a token budget.
    """

    def __init__(self, episodic, semantic, procedural, decay, config):
        self.episodic = episodic
        self.semantic = semantic
        self.procedural = procedural
        self.decay = decay
        self.config = config

    def build(self, query: str, max_tokens: int = 150) -> str:
        """Build a <memory_digest> XML block for the given query.

        Fetches relevant memories from all three stores,
        ranks by decay-weighted relevance, and compresses.
        """
        top_k = self.config.digest.top_k

        # Fetch from each store
        episodic_results = self.episodic.search(query, limit=top_k)
        semantic_results = self.semantic.search(query, limit=top_k)
        procedural_results = self.procedural.search(query, limit=top_k)

        # Build digest sections
        sections = []

        # Semantic facts (highest priority — stable knowledge)
        if semantic_results:
            facts = []
            for mem in semantic_results[:3]:
                score = self.decay.compute_final_score(mem)
                if score > 0.2:
                    facts.append(self._truncate(mem.content, 80))
            if facts:
                sections.append(f'  <semantic>{" ".join(facts)}</semantic>')

        # Episodic context (recent events)
        if episodic_results:
            events = []
            for mem in episodic_results[:2]:
                score = self.decay.compute_final_score(mem)
                if score > 0.2:
                    events.append(self._truncate(mem.content, 80))
            if events:
                sections.append(f'  <episodic>{" ".join(events)}</episodic>')

        # Procedural knowledge (tools/workflows)
        if procedural_results:
            procs = []
            for mem in procedural_results[:2]:
                procs.append(self._truncate(mem.content, 60))
            if procs:
                sections.append(f'  <procedural>{" ".join(procs)}</procedural>')

        if not sections:
            return ""

        # Determine freshness label
        freshness = "recent"
        if episodic_results:
            from datetime import datetime, timezone
            age = (datetime.now(timezone.utc) - episodic_results[0].created_at).days
            if age > 30:
                freshness = "historical"
            elif age > 7:
                freshness = "moderate"

        # Determine confidence label
        all_results = episodic_results + semantic_results + procedural_results
        avg_confidence = (
            sum(m.confidence for m in all_results) / len(all_results) if all_results else 0.5
        )
        confidence_label = "high" if avg_confidence > 0.7 else "medium" if avg_confidence > 0.4 else "low"

        digest = f'<memory_digest confidence="{confidence_label}" freshness="{freshness}">\n'
        digest += "\n".join(sections)
        digest += "\n</memory_digest>"

        # Final truncation to respect token budget
        # Rough estimate: 1 token ≈ 4 chars
        max_chars = max_tokens * 4
        if len(digest) > max_chars:
            digest = digest[:max_chars - 20] + "\n</memory_digest>"

        return digest

    def build_summary(self) -> str:
        """Build a general summary of all stored knowledge."""
        stats = {
            "episodic": self.episodic.count(),
            "semantic": self.semantic.count(),
            "procedural": self.procedural.count(),
        }

        total = sum(stats.values())
        if total == 0:
            return "No memories stored yet."

        return (
            f"Memory Summary: {total} total memories — "
            f"{stats['episodic']} episodic events, "
            f"{stats['semantic']} semantic facts, "
            f"{stats['procedural']} procedures/tools."
        )

    @staticmethod
    def _truncate(text: str, max_len: int) -> str:
        """Truncate text to max length."""
        if len(text) <= max_len:
            return text
        return text[: max_len - 3] + "..."
