"""Consolidated memory system for Timmy — single source of truth.

Architecture:
- Database: Single `memories` table with unified schema
- Embeddings: timmy.memory.embeddings (extracted)
- CRUD: store_memory, search_memories, delete_memory, etc.
- Tool functions: memory_search, memory_read, memory_write, memory_forget
- Classes: HotMemory, VaultMemory, MemorySystem, SemanticMemory, MemorySearcher
"""

import logging

# ── Re-exports from submodules ───────────────────────────────────────────────
# Every public name that used to live here is re-exported so that existing
# ``from timmy.memory_system import X`` statements keep working.
from timmy.memory.consolidation import (  # noqa: F401
    _DEFAULT_HOT_MEMORY_TEMPLATE,
    HotMemory,
    VaultMemory,
)
from timmy.memory.crud import (  # noqa: F401
    _build_search_filters,
    _fetch_memory_candidates,
    _row_to_entry,
    _score_and_filter,
    delete_memory,
    get_memory_context,
    get_memory_stats,
    prune_memories,
    recall_last_reflection,
    recall_personal_facts,
    recall_personal_facts_with_ids,
    search_memories,
    store_last_reflection,
    store_memory,
    store_personal_fact,
    update_personal_fact,
)
from timmy.memory.db import (  # noqa: F401
    DB_PATH,
    HOT_MEMORY_PATH,
    PROJECT_ROOT,
    SOUL_PATH,
    VAULT_PATH,
    MemoryChunk,
    MemoryEntry,
    _drop_legacy_table,
    _ensure_schema,
    _get_table_columns,
    _migrate_chunks,
    _migrate_episodes,
    _migrate_schema,
    get_conn,
    get_connection,
)
from timmy.memory.embeddings import (  # noqa: F401
    EMBEDDING_DIM,
    EMBEDDING_MODEL,
    _cosine_similarity,
    _get_embedding_model,
    _keyword_overlap,
    _simple_hash_embedding,
    cosine_similarity,
    embed_text,
)
from timmy.memory.semantic import (  # noqa: F401
    MemorySearcher,
    SemanticMemory,
    memory_searcher,
    semantic_memory,
)
from timmy.memory.tools import (  # noqa: F401
    DECISION_LOG,
    NOTES_DIR,
    jot_note,
    log_decision,
    memory_forget,
    memory_read,
    memory_search,
    memory_store,
)

logger = logging.getLogger(__name__)


# ── MemorySystem (central coordinator) ───────────────────────────────────────


class MemorySystem:
    """Central memory system coordinating all tiers."""

    def __init__(self) -> None:
        self.hot = HotMemory()
        self.vault = VaultMemory()
        self.session_decisions: list[str] = []

    def end_session(self, summary: str) -> None:
        """End session - stores session summary in database."""
        # Store session end fact in database instead of updating MEMORY.md
        try:
            store_memory(
                content=f"Session ended: {summary}",
                source="system",
                context_type="conversation",
                metadata={"event": "session_end"},
            )
        except Exception as exc:
            logger.warning("Failed to store session end: %s", exc)

        logger.info("MemorySystem: Session ended")

    def record_decision(self, decision: str) -> None:
        """Record a key decision during session (retained for API compatibility)."""
        self.session_decisions.append(decision)

    def update_user_fact(self, key: str, value: str) -> None:
        """Update user fact in the database."""
        # Store as a fact in the database
        try:
            store_memory(
                content=f"{key}: {value}",
                source="system",
                context_type="fact",
                metadata={"key": key, "value": value},
            )
        except Exception as exc:
            logger.warning("Failed to store user fact: %s", exc)

    def read_soul(self) -> str:
        """Read soul.md — Timmy's core identity. Returns empty string if missing."""
        try:
            if SOUL_PATH.exists():
                return SOUL_PATH.read_text()
        except OSError as exc:
            logger.debug("Failed to read soul.md: %s", exc)
        return ""

    def get_system_context(self) -> str:
        """Get full context for system prompt injection."""
        context_parts = []

        # 0. Soul identity (immutable, always first)
        soul_content = self.read_soul()
        if soul_content:
            context_parts.append("## Soul Identity\n" + soul_content)

        # 1. Hot memory (computed from DB facts)
        hot_content = self.hot.read()
        context_parts.append("## Hot Memory\n" + hot_content)

        # 2. User facts are now included in hot memory via recall_personal_facts

        return "\n\n---\n\n".join(context_parts)


# ── Lazy singleton ───────────────────────────────────────────────────────────
_memory_system: MemorySystem | None = None


def get_memory_system() -> MemorySystem:
    """Return the module-level MemorySystem, creating it on first access."""
    global _memory_system
    if _memory_system is None:
        _memory_system = MemorySystem()
    return _memory_system


def reset_memory_system() -> None:
    """Reset the singleton for test isolation."""
    global _memory_system
    _memory_system = None


def __getattr__(name: str):
    """Module-level __getattr__ for lazy backward-compatible access."""
    if name == "memory_system":
        return get_memory_system()
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
