"""Configuration management for Memory SDK.

Uses TOML for human-readable config with sensible cognitive model defaults.
Supports dot-notation access (e.g., config.get('memory.decay_rate')).
"""

import os
import tomllib
from pathlib import Path
from typing import Any


class MemoryConfig:
    """Configuration with cognitive model defaults."""

    def __init__(self, config_path: Path | None = None) -> None:
        self._config = self._load_defaults()

        for path in self._get_config_paths(config_path):
            if path.exists():
                self._load_from_file(path)
                break

    def _get_config_paths(self, custom_path: Path | None) -> list[Path]:
        paths: list[Path] = []
        if custom_path:
            paths.append(custom_path)
        if env_path := os.getenv("MEMORY_CONFIG"):
            paths.append(Path(env_path))
        paths.append(Path.cwd() / "memory.toml")
        paths.append(Path(__file__).parent / "config.toml")
        return paths

    def _load_defaults(self) -> dict[str, Any]:
        return {
            "database": {
                "url": os.getenv(
                    "DATABASE_URL",
                    "postgresql://localhost:5432/memory_sdk",
                ),
            },
            "memory": {
                "initial_strength": 0.3,
                "consolidation_threshold": 0.5,
                "decay_rate": 0.1,
            },
            "working_memory": {
                "capacity": 7,
                "min_capacity": 5,
                "max_capacity": 9,
            },
            "activation": {
                "activation_decay": 0.7,
                "activation_threshold": 0.1,
                "max_spread_depth": 2,
                "spread_weight_factor": 0.1,
            },
            "retrieval": {
                "activation_boost": 0.01,
                "retrieval_boost": 0.1,
                "wm_retrieval_boost": 0.2,
                "max_retrievals": 3,
                "selection_threshold": 0.3,
                "search_limit": 10,
                "spread_seed_count": 3,
                "wm_similarity_threshold": 0.3,
                "entity_similarity_threshold": 0.3,
                "rrf_k": 60,
            },
            "strengthening": {
                "rehearsal_factor": 1.05,
                "max_strength": 1.0,
                "consolidation_boost": 1.2,
            },
            "forgetting": {
                "displacement_decay": 0.8,
                "forget_decay": 0.5,
                "grace_period_hours": 24,
            },
            "consolidation": {
                "batch_size": 50,
            },
            "associations": {
                "temporal_weight": 0.5,
                "entity_weight": 0.7,
                "concept_weight": 0.4,
                "max_links_per_entity": 5,
            },
            "persona": {
                "auto_extract": True,
                "confidence_threshold": 0.6,
                "predicate_similarity_threshold": 0.8,
                "ranking_strategy": "hybrid",
            },
            "decay": {
                "significance_reduction": 0.7,
                "valence_reduction": 0.5,
            },
            "extraction": {
                "anthropic_model": os.getenv(
                    "ANTHROPIC_MODEL", "claude-haiku-4-5-20251001"
                ),
                "openai_model": os.getenv(
                    "OPENAI_MODEL", "gpt-5-mini"
                ),
                "ollama_model": os.getenv(
                    "OLLAMA_MODEL", "qwen3.5"
                ),
                "anthropic_base_url": os.getenv(
                    "ANTHROPIC_BASE_URL", ""
                ),
                "openai_base_url": os.getenv(
                    "OPENAI_BASE_URL", ""
                ),
                "ollama_base_url": os.getenv(
                    "OLLAMA_BASE_URL",
                    "http://localhost:11434/v1",
                ),
                "max_tokens": 4096,
                "max_concepts": 5,
                "max_relations": 3,
                "instructions": "",
            },
            "embedding": {
                "model": "nomic-ai/nomic-embed-text-v1.5-Q",
                "dimensions": 768,
            },
            "server": {
                "host": os.getenv("SERVER_HOST", "localhost"),
                "port": int(os.getenv("SERVER_PORT", "8000")),
                "user_id": os.getenv("MEMORY_USER_ID", ""),
            },
            "session": {
                "summary_strength": 0.8,
                "summary_significance": 0.7,
                "summary_decay_rate": 0.02,
                "summary_max_tokens": 1024,
            },
        }

    def _load_from_file(self, path: Path) -> None:
        with open(path, "rb") as f:
            file_config = tomllib.load(f)
            self._deep_merge(self._config, file_config)

    def _deep_merge(self, base: dict[str, Any], override: dict[str, Any]) -> None:
        for key, value in override.items():
            if key in base and isinstance(base[key], dict) and isinstance(value, dict):
                self._deep_merge(base[key], value)
            else:
                base[key] = value

    # -- Convenience properties --

    @property
    def database_url(self) -> str:
        return str(self._config["database"]["url"])

    @property
    def anthropic_api_key(self) -> str | None:
        return os.getenv("ANTHROPIC_API_KEY")

    @property
    def working_memory_capacity(self) -> int:
        return int(self._config["working_memory"]["capacity"])

    @property
    def consolidation_threshold(self) -> float:
        return float(self._config["memory"]["consolidation_threshold"])

    @property
    def embedding_dimensions(self) -> int:
        return int(self._config["embedding"]["dimensions"])

    @property
    def extraction_instructions(self) -> str:
        """Get extraction instructions. ENV overrides TOML config."""
        env = os.getenv("MEMORY_EXTRACTION_INSTRUCTIONS")
        if env is not None:
            return env
        return str(self.get("extraction.instructions", ""))

    @property
    def server_user_id(self) -> str:
        """Get server user ID. ENV MEMORY_USER_ID overrides TOML config."""
        env = os.getenv("MEMORY_USER_ID")
        if env is not None:
            return env
        return str(self.get("server.user_id", ""))

    def get(self, path: str, default: Any = None) -> Any:
        """Get config value by dot notation (e.g., 'memory.decay_rate')."""
        keys = path.split(".")
        value: Any = self._config

        for key in keys:
            if isinstance(value, dict) and key in value:
                value = value[key]
            else:
                return default
        return value

    def __repr__(self) -> str:
        return (
            f"MemoryConfig(wm={self.working_memory_capacity}, "
            f"consolidation={self.consolidation_threshold})"
        )


# Global config instance
config = MemoryConfig()
