"""Proactive memory surfacing - the prefrontal cortex."""

from __future__ import annotations

import time
from collections import deque

from neurovault.search.multi_signal import MultiSignalSearchEngine, RecallResult
from neurovault.storage import NeurovaultStorage


class ContextMonitor:
    """Track user's current context for prediction triggers."""

    def __init__(self):
        self._context: dict[str, dict] = {}
        self._last_prediction = 0.0
        self._cooldown = 120

    def update(self, context_type: str, value: str) -> None:
        self._context[context_type] = {
            "value": value,
            "timestamp": time.time(),
        }

    def should_predict(self) -> bool:
        if not self._context:
            return False
        if time.time() - self._last_prediction < self._cooldown:
            return False
        for ctx in self._context.values():
            if time.time() - ctx["timestamp"] < 10:
                return True
        return False

    def get_query(self) -> str:
        parts = [ctx["value"] for ctx in self._context.values() if time.time() - ctx["timestamp"] < 300]
        return " ".join(parts)

    def mark_predicted(self) -> None:
        self._last_prediction = time.time()

    @property
    def recent_entities(self) -> list[str]:
        return [ctx["value"] for ctx in self._context.values() if time.time() - ctx["timestamp"] < 600]


class PredictionEngine:
    """Proactively surface relevant memories."""

    THRESHOLD = 0.5
    MAX_PREDICTIONS = 5

    def __init__(self, storage: NeurovaultStorage, search_engine: MultiSignalSearchEngine, context: ContextMonitor):
        self._storage = storage
        self._search = search_engine
        self._context = context
        self._surfaced: deque[bytes] = deque(maxlen=100)

    def predict(self, persona_id: bytes) -> list[RecallResult]:
        if not self._context.should_predict():
            return []

        query = self._context.get_query()
        if not query or len(query) < 3:
            return []

        candidates = self._search.search(query=query, persona_id=persona_id, limit=20, mode="multi")

        predictions: list[RecallResult] = []
        for result in candidates:
            if result.memory.id in self._surfaced:
                continue

            score = self._anticipation_score(result)
            if score >= self.THRESHOLD:
                result.score = score
                result.match_type = "prediction"
                predictions.append(result)
                self._surfaced.append(result.memory.id)

        predictions.sort(key=lambda r: -r.score)
        self._context.mark_predicted()
        return predictions[: self.MAX_PREDICTIONS]

    def _anticipation_score(self, result: RecallResult) -> float:
        score = 0.0
        memory = result.memory

        hours_ago = (time.time() * 1000 - memory.created_at) / (3600 * 1000)
        if hours_ago < 1:
            score += 0.3
        elif hours_ago < 24:
            score += 0.2
        elif hours_ago < 168:
            score += 0.1

        score += getattr(memory, "importance", 0.5) * 0.3

        arousal = 0.0
        if hasattr(memory, "metadata") and isinstance(memory.metadata, dict):
            arousal = float(memory.metadata.get("emotional_arousal", 0.0))
        score += arousal * 0.2

        if result.entities:
            context_entities = {entity.lower() for entity in self._context.recent_entities}
            overlap = sum(1 for entity in result.entities if entity.name.lower() in context_entities)
            score += overlap * 0.15

        source_weights = {"user": 0.1, "agent": 0.05, "consolidation": 0.08}
        score += source_weights.get(memory.source, 0.02)

        return min(score, 1.0)
