"""Ebbinghaus Decay Engine — confidence scoring with forgetting curve.

Implements R = e^(-t/S) where:
- R = retrievability (0 to 1)
- t = time elapsed since last access
- S = memory strength (based on access_count, source reliability)

Final retrieval score:
  final_score = similarity * decay_factor * confidence_boost
"""

from __future__ import annotations

import math
from datetime import datetime, timezone

from mnemosynth.core.types import MemoryNode


class DecayEngine:
    """Applies Ebbinghaus forgetting curve to memory retrieval scoring."""

    def __init__(self, config):
        self.config = config
        self.half_life_days = config.decay.half_life_days
        self.min_confidence = config.decay.min_confidence
        self.enabled = config.decay.enabled

    def compute_decay(self, memory: MemoryNode) -> float:
        """Compute the decay factor for a memory.

        Returns a float between 0.0 and 1.0.
        1.0 = fresh memory, 0.0 = fully decayed.
        """
        if not self.enabled:
            return 1.0

        now = datetime.now(timezone.utc)
        elapsed = (now - memory.last_accessed).total_seconds()
        elapsed_days = elapsed / 86400.0

        # Memory strength increases with access count
        strength = self.half_life_days * (1 + 0.2 * memory.access_count)

        # Ebbinghaus formula: R = e^(-t/S)
        decay_factor = math.exp(-elapsed_days / strength)

        return max(self.min_confidence, decay_factor)

    def compute_final_score(
        self,
        memory: MemoryNode,
        similarity: float = 1.0,
    ) -> float:
        """Compute the final retrieval score combining similarity, decay, and confidence.

        final_score = similarity * decay_factor * confidence_boost

        Where confidence_boost = confidence * (1 + 0.1 * corroboration_count)
        """
        decay_factor = self.compute_decay(memory)

        # Confidence boost from corroboration
        confidence_boost = memory.confidence * (1 + 0.1 * memory.corroboration_count)
        confidence_boost = min(1.0, confidence_boost)

        # Sentiment boost (high-emotion memories are retrieved more easily)
        sentiment_boost = 1.0
        if self.config.sentiment.enabled and abs(memory.sentiment_score) > 0.5:
            sentiment_boost = self.config.sentiment.boost_factor

        final_score = similarity * decay_factor * confidence_boost * sentiment_boost
        return min(1.0, final_score)

    def apply_decay_to_list(
        self,
        memories: list[MemoryNode],
        similarities: list[float] | None = None,
    ) -> list[tuple[MemoryNode, float]]:
        """Apply decay scoring to a list of memories and sort by final score."""
        if similarities is None:
            similarities = [1.0] * len(memories)

        scored = []
        for mem, sim in zip(memories, similarities):
            score = self.compute_final_score(mem, similarity=sim)
            scored.append((mem, score))

        scored.sort(key=lambda x: x[1], reverse=True)
        return scored

    def should_archive(self, memory: MemoryNode) -> bool:
        """Check if a memory has decayed below the archival threshold."""
        decay = self.compute_decay(memory)
        return decay <= self.min_confidence
