"""Memory config parsing helpers."""
from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING, Any

from logos_adk.errors import ConfigError
from logos_adk.memory import LongTermMemory, Memory, SessionMemory, SharedMemory
from logos_adk.memory.backends import (
    InMemoryBackend,
    PostgreSQLBackend,
    ShardedMemoryBackend,
    SQLiteBackend,
)
from logos_adk.postgres_utils import resolve_database_url

if TYPE_CHECKING:
    from logos_adk.learning.memory_learning import MemoryLearningEngine


def _default_sqlite_path(scope: str) -> str:
    filename = "memory.db" if scope == "memory" else f"{scope}_memory.db"
    return str(Path(".logos_adk") / filename)


def build_memory_backend(
    config: Any,
    *,
    scope: str = "memory",
) -> InMemoryBackend | SQLiteBackend | PostgreSQLBackend | ShardedMemoryBackend:
    """Build a supported memory backend from config."""
    if not isinstance(config, dict):
        raise ConfigError("memory backend config must be a mapping")

    database_url = resolve_database_url(config.get("dsn"))
    backend_name = config.get("backend")
    if backend_name is None:
        backend_name = "postgresql" if database_url else "sqlite"
    if backend_name in {"in_memory", "memory", "inmemory"}:
        return InMemoryBackend()
    if backend_name in {"postgres", "postgresql"}:
        if not database_url:
            raise ConfigError(
                "PostgreSQL memory backend requires a DSN or DATABASE_URL/LOGOS_ADK_DATABASE_URL"
            )
        table_name = config.get("table_name", f"{scope}_memories")
        if not isinstance(table_name, str) or not table_name:
            raise ConfigError("PostgreSQL memory backend requires a non-empty table_name")
        return PostgreSQLBackend(database_url, table_name=table_name)
    if backend_name == "sqlite":
        path = config.get("path", _default_sqlite_path(scope))
        if not isinstance(path, str) or not path:
            raise ConfigError("SQLite memory backend requires a non-empty path")
        return SQLiteBackend(path)
    if backend_name == "sharded":
        shards = config.get("shards")
        if not isinstance(shards, list) or not shards:
            raise ConfigError("Sharded memory backend requires a non-empty 'shards' list")
        shard_backends = []
        for index, shard in enumerate(shards):
            if not isinstance(shard, dict):
                raise ConfigError("memory backend shard config must be a mapping")
            shard_scope = f"{scope}_shard_{index}"
            shard_backends.append(build_memory_backend(shard, scope=shard_scope))
        return ShardedMemoryBackend(shard_backends)

    raise ConfigError(f"Unsupported memory backend: {backend_name}")


def build_memory_learning(config: Any) -> MemoryLearningEngine | None:
    """Build optional adaptive memory-learning policy."""
    from logos_adk.learning.memory_learning import MemoryLearningEngine

    if config in (None, False):
        return None
    if config is True:
        return MemoryLearningEngine()
    if not isinstance(config, dict):
        raise ConfigError("memory learning config must be true/false or a mapping")
    return MemoryLearningEngine(
        archive_threshold=float(config.get("archive_threshold", 0.25)),
        delete_threshold=float(config.get("delete_threshold", 0.1)),
        max_active_entries=int(config.get("max_active_entries", 50)),
        archive_after_days=int(config.get("archive_after_days", 30)),
        max_context_limit=int(config.get("max_context_limit", 10)),
    )


def load_memories_from_config(config: Any) -> list[Memory]:
    """Build memory strategies from YAML config."""
    if config is None:
        return []

    if config == "session":
        return [SessionMemory()]

    if not isinstance(config, dict):
        raise ConfigError("memory config must be 'session' or a mapping")

    memories: list[Memory] = []

    session_config = config.get("session")
    if session_config is not None:
        if session_config is True:
            session_config = {}
        if not isinstance(session_config, dict):
            raise ConfigError("memory.session must be a mapping")
        memories.append(
            SessionMemory(
                strategy=session_config.get("strategy", "sliding_window"),
                max_messages=session_config.get("max_messages", 20),
                max_tokens=session_config.get("max_tokens"),
                learning=build_memory_learning(session_config.get("learning")),
            )
        )

    long_term_config = config.get("long_term")
    if long_term_config is not None:
        memories.append(
            LongTermMemory(
                backend=build_memory_backend(long_term_config, scope="long_term"),
                retrieval=long_term_config.get("retrieval", "keyword"),
                limit=long_term_config.get("limit", 5),
                embed_model=long_term_config.get("embed_model"),
                learning=build_memory_learning(long_term_config.get("learning")),
            )
        )

    shared_config = config.get("shared")
    if shared_config is not None:
        memories.append(
            SharedMemory(
                backend=build_memory_backend(shared_config, scope="shared"),
                retrieval=shared_config.get("retrieval", "keyword"),
                limit=shared_config.get("limit", 5),
                embed_model=shared_config.get("embed_model"),
                learning=build_memory_learning(shared_config.get("learning")),
            )
        )

    unknown_keys = set(config) - {"session", "long_term", "shared"}
    if unknown_keys:
        unknown = ", ".join(sorted(unknown_keys))
        raise ConfigError(f"Unknown memory config sections: {unknown}")

    return memories
