"""Configuration for GreyMatter solo mode.

Loads from ~/.greymatter/config.yaml with sensible defaults.
"""
import os
from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional

import yaml


@dataclass
class Config:
    """GreyMatter configuration."""

    db_path: Path = field(default_factory=lambda: Path("~/.greymatter/greymatter.db").expanduser())
    soul_path: Path = field(default_factory=lambda: Path("~/.greymatter/soul/default.md").expanduser())
    variants_path: Path = field(default_factory=lambda: Path("~/.greymatter/soul/variants").expanduser())
    data_dir: Path = field(default_factory=lambda: Path("~/.greymatter").expanduser())
    ollama_url: str = "http://localhost:11434"
    default_model: str = "nemotron-3-nano"
    anthropic_api_key: Optional[str] = None
    openrouter_api_key: Optional[str] = None
    # Scanner settings
    scanner_mode: str = "redact"         # "warn", "redact", or "block"
    scanner_min_confidence: float = 0.7
    scanner_enabled: bool = True
    # Encryption at rest — enabled by default
    encryption_key: Optional[str] = None  # Hex key for SQLCipher; auto-generated if None
    encryption_enabled: bool = True       # Set False to disable encryption at rest
    # Redis caching — optional, for frequently accessed patterns
    redis_url: Optional[str] = None       # e.g. redis://localhost:6379/0


def load_config(config_path: Optional[Path] = None) -> Config:
    """Load config from YAML file with defaults.

    Args:
        config_path: Path to config YAML. Defaults to ~/.greymatter/config.yaml.

    Returns:
        Config with loaded or default values.
    """
    if config_path is None:
        config_path = Path("~/.greymatter/config.yaml").expanduser()

    config = Config()
    data = {}

    if config_path.exists():
        with open(config_path, encoding="utf-8") as f:
            data = yaml.safe_load(f) or {}

        if "db_path" in data:
            config.db_path = Path(data["db_path"]).expanduser()
        if "soul_path" in data:
            config.soul_path = Path(data["soul_path"]).expanduser()
        if "variants_path" in data:
            config.variants_path = Path(data["variants_path"]).expanduser()
        if "data_dir" in data:
            config.data_dir = Path(data["data_dir"]).expanduser()
        if "ollama_url" in data:
            config.ollama_url = data["ollama_url"]
        if "default_model" in data:
            config.default_model = data["default_model"]

        # Scanner config (may be nested under "security" or "scanner")
        scanner_data = data.get("security", data.get("scanner", {}))
        if isinstance(scanner_data, dict):
            if "mode" in scanner_data:
                config.scanner_mode = scanner_data["mode"]
            if "min_confidence" in scanner_data:
                config.scanner_min_confidence = float(scanner_data["min_confidence"])
            if "enabled" in scanner_data:
                config.scanner_enabled = bool(scanner_data["enabled"])

    # Encryption config (nested under "encryption" key in YAML)
    enc_data = data.get("encryption", {})
    if isinstance(enc_data, dict):
        if "enabled" in enc_data:
            config.encryption_enabled = bool(enc_data["enabled"])
        if "key" in enc_data:
            config.encryption_key = enc_data["key"]

    # Redis caching config
    redis_data = data.get("cache", data.get("redis", {}))
    if isinstance(redis_data, dict) and "url" in redis_data:
        config.redis_url = redis_data["url"]
    elif isinstance(redis_data, str):
        config.redis_url = redis_data

    # Environment variables override config file
    config.anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY", config.anthropic_api_key)
    config.openrouter_api_key = os.environ.get("OPENROUTER_API_KEY", config.openrouter_api_key)
    config.redis_url = os.environ.get("GM_REDIS_URL", os.environ.get("REDIS_URL", config.redis_url))

    # GM_DB_KEY env var overrides config file encryption key
    env_key = os.environ.get("GM_DB_KEY")
    if env_key:
        config.encryption_key = env_key
        config.encryption_enabled = True

    # GM_ENCRYPTION_DISABLED=1 disables encryption (for development/testing)
    if os.environ.get("GM_ENCRYPTION_DISABLED", "").strip() in ("1", "true", "yes"):
        config.encryption_enabled = False

    # Auto-generate encryption key if enabled but no key provided
    if config.encryption_enabled and not config.encryption_key:
        config.encryption_key = _ensure_keyfile(config.data_dir)

    return config


def _ensure_keyfile(data_dir: Path) -> Optional[str]:
    """Load or generate the encryption key file.

    On first run, generates a 32-byte random key and stores it at
    ~/.greymatter/.keyfile with 0o600 permissions. On subsequent runs,
    reads the existing key.

    Returns:
        Hex-encoded encryption key, or None if key generation fails.
    """
    keyfile = data_dir / ".keyfile"
    try:
        if keyfile.exists():
            key = keyfile.read_text(encoding="utf-8").strip()
            if key:
                return key
        # Generate new key
        key = os.urandom(32).hex()
        data_dir.mkdir(parents=True, exist_ok=True)
        keyfile.write_text(key, encoding="utf-8")
        keyfile.chmod(0o600)
        return key
    except OSError:
        return None


# --- Cluster Configuration ---


@dataclass
class SyncConfig:
    """Sync engine configuration."""
    active_interval: float = 30.0       # Seconds between syncs (active mode)
    idle_interval: float = 300.0        # Seconds between syncs (idle mode)
    batch_size: int = 500               # Rows per sync batch
    retry_max: int = 5                  # Max retry attempts
    retry_backoff_base: float = 2.0     # Exponential backoff base (seconds)
    retention_days: int = 7             # Days to keep sync log entries
    full_sync_on_join: bool = True      # Run full sync when joining cluster


@dataclass
class CertConfig:
    """Certificate configuration."""
    cert_dir: str = "~/.greymatter/certs"
    ca_cert: str = "ca.crt"
    node_cert: str = "node.crt"
    node_key: str = "node.key"
    ca_validity_days: int = 3650        # 10 years
    node_validity_days: int = 365       # 1 year

    @property
    def cert_path(self) -> Path:
        return Path(self.cert_dir).expanduser()


@dataclass
class ClusterConfig:
    """Cluster-mode configuration."""
    node_id: str = ""                   # Auto-generated if empty
    discovery_config: str = ""          # Path to discovery.json
    tailscale_tag: str = "greymatter"   # Tailscale device tag for discovery
    hub_url: str = ""                   # Direct hub URL (overrides discovery)
    join_token: str = ""                # Pre-shared join token

    sync: SyncConfig = field(default_factory=SyncConfig)
    certs: CertConfig = field(default_factory=CertConfig)

    @classmethod
    def from_env(cls) -> "ClusterConfig":
        """Create config from environment variables."""
        config = cls(
            node_id=os.environ.get("GM_NODE_ID", ""),
            discovery_config=os.environ.get("GM_DISCOVERY_CONFIG", ""),
            tailscale_tag=os.environ.get("GM_TAILSCALE_TAG", "greymatter"),
            hub_url=os.environ.get("GM_HUB_URL", ""),
            join_token=os.environ.get("GM_JOIN_TOKEN", ""),
        )
        if v := os.environ.get("GM_SYNC_INTERVAL"):
            config.sync.active_interval = float(v)
        if v := os.environ.get("GM_SYNC_IDLE_INTERVAL"):
            config.sync.idle_interval = float(v)
        if v := os.environ.get("GM_SYNC_BATCH_SIZE"):
            config.sync.batch_size = int(v)
        if v := os.environ.get("GM_SYNC_RETENTION_DAYS"):
            config.sync.retention_days = int(v)
        if v := os.environ.get("GM_CERT_DIR"):
            config.certs.cert_dir = v
        return config
