#!/usr/bin/env python3
"""
The Hippocampus — Memory Encoding, Retrieval & Consolidation
Episodic memory for events, semantic memory for facts, retrieval for context.
"""
import hashlib
import json
from datetime import datetime
from brain.state import load_memory, save_memory, ABSENCE_CATEGORIES

MAX_EPISODIC = 100  # Working memory limit
RETENTION_THRESHOLD = 0.3

def make_memory_id(content, timestamp):
    return hashlib.md5(f"{content}{timestamp}".encode()).hexdigest()[:12]

def encode_episodic(signal, encoding_strength=1.0):
    """
    Encode a signal as an episodic memory.
    Higher encoding_strength = stronger initial consolidation.
    """
    memory = load_memory()

    entry = {
        "id": make_memory_id(signal["raw_input"], signal["received_at"]),
        "signal_id": signal["signal_id"],
        "timestamp": signal["received_at"],
        "content": signal["raw_input"],
        "content_preview": signal["raw_input"][:80] + "..." if len(signal["raw_input"]) > 80 else signal["raw_input"],
        "emotional_tags": signal["amygdala"]["emotional_tags"],
        "salience": signal["amygdala"]["salience"],
        "topics": signal["cortex"]["topics"],
        "intent": signal["cortex"]["intent"],
        "retention": min(encoding_strength, 1.0),
        "access_count": 0,
        "consolidated": False,
        "decayed": False,
    }

    memory["episodic"].insert(0, entry)
    # Trim to working memory limit
    memory["episodic"] = memory["episodic"][:MAX_EPISODIC]

    # Index for fast retrieval
    signal["hippocampus"]["memory_id"] = entry["id"]

    save_memory(memory)
    return entry

def retrieve_recent(n=5):
    """Get last n episodic memories."""
    memory = load_memory()
    return memory["episodic"][:n]

def search_memory(query, n=5):
    """
    Search episodic + semantic memory for relevance.
    Returns: list of (score, entry) sorted by relevance.
    """
    memory = load_memory()
    query_lower = query.lower()
    results = []

    for entry in memory["episodic"]:
        if entry.get("decayed"):
            continue
        score = 0
        if query_lower in entry["content"].lower():
            score += 3
        if any(t in query_lower for t in entry.get("emotional_tags", [])):
            score += 1
        if any(topic in query_lower for topic in entry.get("topics", [])):
            score += 2
        if score > 0:
            results.append((score, entry))

    # Semantic search
    for category, facts in memory.get("semantic", {}).items():
        if query_lower in category.lower():
            for fact in facts:
                results.append((2, {"type": "semantic", "content": fact}))

    results.sort(key=lambda x: x[0], reverse=True)
    return [r[1] for r in results[:n]]

def match_confidence(query):
    """
    How confident is the hippocampus that it has seen this exact signal before?
    Returns 0.0–1.0.
    """
    memory = load_memory()
    query_lower = query.lower()

    for entry in memory["episodic"][:20]:  # Check recent only
        if entry.get("decayed"):
            continue
        # Exact content match
        if entry["content"].lower() == query_lower:
            entry["access_count"] = entry.get("access_count", 0) + 1
            save_memory(memory)
            return 0.95
        # High similarity
        if query_lower in entry["content"].lower() or entry["content"].lower() in query_lower:
            entry["access_count"] = entry.get("access_count", 0) + 1
            save_memory(memory)
            return 0.7

    return 0.0

def consolidate():
    """
    Daily consolidation pass: decay old memories, promote strong ones.
    Called by scheduled agent.
    """
    memory = load_memory()
    promoted = []
    decayed = []

    for entry in memory["episodic"]:
        if entry.get("decayed"):
            continue

        # Bump retention for frequently accessed memories
        if entry.get("access_count", 0) >= 3 and not entry.get("consolidated"):
            entry["consolidated"] = True
            entry["retention"] = min(entry.get("retention", 0.5) + 0.2, 1.0)
            promoted.append(entry["id"])

        # Decay low-retention memories
        entry["retention"] = entry.get("retention", 0.5) - 0.02
        if entry["retention"] <= RETENTION_THRESHOLD:
            entry["decayed"] = True
            decayed.append(entry["id"])

    save_memory(memory)
    return {"promoted": promoted, "decayed": decayed}

def process(signal, force_encode=False):
    """
    Process a signal through hippocampus.
    Reads: signal['raw_input'], signal['amygdala'], signal['cortex']
    Writes: signal['hippocampus'] block
    Returns: modified signal
    """
    raw = signal["raw_input"]
    amygdala = signal["amygdala"]

    # Assess encoding worthiness
    salience = amygdala["salience"]
    high_emotion = any(t not in ["neutral", "peaceful"] for t in amygdala["emotional_tags"])
    substantive = len(raw.split()) > 30

    should_encode = force_encode or salience >= 0.5 or (high_emotion and substantive)

    # Encoding strength modulated by K-index (from rcomplex/environment)
    base_strength = 0.5 if salience < 0.3 else (0.8 if salience < 0.7 else 1.0)
    k_index = signal.get("rcomplex", {}).get("k_index", 0)
    if k_index >= 7:
        base_strength *= 0.5  # Geomagnetic disturbance reduces encoding

    signal["hippocampus"]["should_encode"] = should_encode
    signal["hippocampus"]["encoding_strength"] = round(base_strength, 3)
    signal["hippocampus"]["match_confidence"] = round(match_confidence(raw), 3)
    signal["hippocampus"]["retrieved_context"] = retrieve_recent(3)

    if should_encode:
        encode_episodic(signal, encoding_strength=base_strength)

    return signal
