# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Configuration management for Familiar.
Loads from config.yaml and environment variables.

Phase 2.1 (v2.1): Added config validation with unknown key warnings
"""

import logging
import os
from dataclasses import dataclass, field, fields
from pathlib import Path
from typing import Any, Dict, List, Optional, Set

import yaml

logger = logging.getLogger(__name__)

# Canonical paths — single source of truth lives in paths.py.
# Re-exported here for backward compatibility.
from familiar.core.paths import (  # noqa: E402
    AGENT_DIR,
    CONFIG_FILE,
    DATA_DIR,
    HISTORY_FILE,  # noqa: F401 — re-exported for backward compat (memory.py)
    HOME_DIR,
    MEMORY_FILE,  # noqa: F401 — re-exported for backward compat (memory.py)
    SKILLS_DIR,
)

# Bundled skills shipped with the package
BUNDLED_SKILLS_DIR = Path(__file__).parent.parent / "skills"


def _get_known_keys(dataclass_type) -> Set[str]:
    """Get all known field names from a dataclass."""
    return {f.name for f in fields(dataclass_type)}


def _validate_config_section(
    data: Dict[str, Any], dataclass_type, section_name: str, warnings_list: List[str]
) -> None:
    """
    Validate a config section for unknown keys.

    Args:
        data: The config data dictionary
        dataclass_type: The expected dataclass type
        section_name: Name of the section (for error messages)
        warnings_list: List to append warnings to
    """
    if not data:
        return

    known_keys = _get_known_keys(dataclass_type)
    unknown_keys = set(data.keys()) - known_keys

    for key in unknown_keys:
        msg = f"Unknown config key '{key}' in section '{section_name}'. Did you mean one of: {sorted(known_keys)[:5]}...?"
        warnings_list.append(msg)
        logger.warning(msg)


@dataclass
class LLMConfig:
    default_provider: str = "anthropic"
    anthropic_model: str = "claude-sonnet-4-6"
    openai_model: str = "gpt-4o"
    gemini_model: str = "gemini-2.5-flash"
    ollama_model: str = "llama3.2"
    ollama_base_url: str = "http://localhost:11434"
    max_tokens: int = 4096
    temperature: float = 0.7

    # Lightweight model for background tasks (planning, memory extraction).
    # Set to "" to use the main provider for everything.
    # Good options: "qwen2.5:1.5b", "qwen2.5:3b", "gemma2:2b", "phi3:mini", "smollm2:1.7b"
    lightweight_model: str = ""
    lightweight_provider: str = ""  # "ollama", "openai", etc. Defaults to auto-detect.


@dataclass
class ResilienceConfig:
    """Configuration for retry and resilience behavior."""

    enabled: bool = True
    max_attempts: int = 3
    base_delay: float = 1.0  # Initial delay in seconds
    max_delay: float = 60.0  # Maximum delay between retries
    exponential_base: float = 2.0  # Backoff multiplier
    jitter: float = 0.1  # Random jitter factor (0-1)


@dataclass
class ObservabilityConfig:
    """Configuration for tracing and metrics."""

    tracing_enabled: bool = True
    cost_tracking_enabled: bool = True
    log_traces: bool = True  # Log trace summaries on completion
    log_level: str = "INFO"  # Trace log level
    # Future: OpenTelemetry export endpoint
    otlp_endpoint: Optional[str] = None


@dataclass
class ChannelConfig:
    telegram_enabled: bool = False
    telegram_token: Optional[str] = None
    telegram_allowed_users: list = field(default_factory=list)  # Empty = allow all
    owner_telegram_id: Optional[int] = None  # Auto-set during onboarding

    discord_enabled: bool = False
    discord_token: Optional[str] = None
    discord_allowed_users: list = field(default_factory=list)
    owner_discord_id: Optional[int] = None

    matrix_enabled: bool = False
    matrix_homeserver: Optional[str] = None
    matrix_user: Optional[str] = None
    matrix_allowed_users: list = field(default_factory=list)
    matrix_allowed_rooms: list = field(default_factory=list)
    owner_matrix_id: Optional[str] = None

    signal_enabled: bool = False
    signal_phone: Optional[str] = None
    signal_allowed_contacts: list = field(default_factory=list)
    owner_signal_id: Optional[str] = None

    whatsapp_enabled: bool = False
    whatsapp_bridge_host: str = "localhost"
    whatsapp_bridge_port: int = 3001
    whatsapp_allowed_numbers: list = field(default_factory=list)
    owner_whatsapp_id: Optional[str] = None

    imessage_enabled: bool = False
    imessage_allowed_contacts: list = field(default_factory=list)

    teams_enabled: bool = False
    teams_app_id: Optional[str] = None
    teams_app_password: Optional[str] = None
    teams_tenant_id: Optional[str] = None

    mastodon_enabled: bool = False
    mastodon_instance_url: Optional[str] = None
    mastodon_access_token: Optional[str] = None
    mastodon_allowed_users: list = field(default_factory=list)
    owner_mastodon_handle: Optional[str] = None

    bluesky_enabled: bool = False
    bluesky_handle: Optional[str] = None
    bluesky_app_password: Optional[str] = None
    bluesky_pds_url: str = "https://bsky.social"
    bluesky_allowed_users: list = field(default_factory=list)
    owner_bluesky_handle: Optional[str] = None

    cli_enabled: bool = True


@dataclass
class MCPServerConfigSimple:
    """Simplified MCP server config for YAML loading."""

    name: str = ""
    transport: str = "stdio"
    command: Optional[str] = None
    args: list = field(default_factory=list)
    url: Optional[str] = None
    env: dict = field(default_factory=dict)
    enabled: bool = True
    requires_confirmation: bool = False


@dataclass
class MCPConfigSimple:
    """MCP configuration for main config file."""

    enabled: bool = False
    servers: list = field(default_factory=list)
    tool_prefix_separator: str = ":"
    connect_timeout: float = 30.0
    reconnect_on_failure: bool = True


@dataclass
class SecurityConfigSettings:
    """Security and encryption settings."""

    # Encryption at rest
    encrypt_sessions: bool = False
    encrypt_memory: bool = False
    encrypt_history: bool = False
    encryption_key_env: str = "FAMILIAR_ENCRYPTION_KEY"

    # Session settings
    session_timeout_minutes: int = 60
    require_reauth_for_sensitive: bool = False
    max_failed_attempts: int = 5
    lockout_duration_minutes: int = 30

    # Trust settings
    auto_trust_upgrade: bool = True
    max_trust_level: str = "owner"

    # Confirmation settings (capabilities that always require confirmation)
    always_confirm: list = field(
        default_factory=lambda: ["write:email", "write:files", "http:requests", "shell:execute"]
    )


@dataclass
class ComplianceConfigSettings:
    """Compliance mode settings."""

    mode: str = "none"  # none, hipaa, soc2, pci_dss, gdpr
    retention_years: int = 1
    require_encryption: bool = False
    log_phi_access: bool = False


@dataclass
class AgentConfig:
    name: str = "Agent"
    persona: str = "a helpful AI assistant"

    # Memory settings
    memory_enabled: bool = True
    max_conversation_history: int = 50

    # Skills
    skills_enabled: bool = True
    skills_dirs: list = field(
        default_factory=lambda: [
            str(SKILLS_DIR),  # User skills (~/.familiar/skills/)
            str(BUNDLED_SKILLS_DIR),  # Bundled skills (shipped with package)
        ]
    )

    # Scheduler
    scheduler_enabled: bool = True
    heartbeat_interval_minutes: int = 60  # Proactive check-ins

    # Security mode
    security_mode: str = "balanced"  # paranoid, balanced, permissive
    job_class: str = ""  # "" = no class, "helper", "social_worker", "business_buddy"
    allow_shell_commands: bool = True
    allow_file_write: bool = True
    # PII/PHI detection — opt-in for healthcare deployments
    enable_pii_detection: bool = False
    sandboxed_directories: list = field(default_factory=lambda: [str(HOME_DIR)])

    # Planning & memory extraction
    planning_enabled: bool = True
    memory_extraction_enabled: bool = True

    # Guardrails
    guardrail_max_tokens: int = 200_000
    guardrail_max_cost_request: float = 2.00
    guardrail_max_cost_hour: float = 20.00
    guardrail_max_cost_day: float = 100.00
    max_iterations: int = 15
    guardrail_rpm: int = 30
    guardrail_timeout: float = 300.0
    blocked_tools: list = field(default_factory=list)
    require_approval_tools: list = field(default_factory=list)

    # Saga orchestration
    saga_default_timeout: float = 300.0

    # Mesh networking
    mesh_enabled: bool = False
    hipaa_enabled: bool = False

    # Distributed skill bus (requires redis extra)
    distributed_bus_enabled: bool = False

    # Eval capture — record conversation traces for eval dataset building
    eval_capture_enabled: bool = False


@dataclass
class ProactiveConfig:
    """Proactive messaging settings (briefings, heartbeats, quiet hours)."""

    briefing_enabled: bool = False
    briefing_time: str = "08:00"
    heartbeat_enabled: bool = False
    quiet_hours_start: str = "22:00"
    quiet_hours_end: str = "07:00"


@dataclass
class BackupSettings:
    """Backup configuration block — maps to backup: section in config.yaml."""

    enabled: bool = False  # Backups off by default; operator opts in
    schedule: str = "daily"  # hourly | daily | weekly
    retention_days: int = 30
    storage: str = "local"  # local | s3
    path: str = ""  # "" → ~/.familiar/backups
    s3_bucket: str = ""
    s3_prefix: str = "backups/"
    encrypt: bool = False
    encryption_key: str = ""  # Fernet key — empty = no encryption
    compress: bool = True
    include_audit: bool = True
    include_history: bool = True


@dataclass
class Config:
    llm: LLMConfig = field(default_factory=LLMConfig)
    channels: ChannelConfig = field(default_factory=ChannelConfig)
    agent: AgentConfig = field(default_factory=AgentConfig)
    resilience: ResilienceConfig = field(default_factory=ResilienceConfig)
    observability: ObservabilityConfig = field(default_factory=ObservabilityConfig)
    mcp: MCPConfigSimple = field(default_factory=MCPConfigSimple)
    security: SecurityConfigSettings = field(default_factory=SecurityConfigSettings)
    compliance: ComplianceConfigSettings = field(default_factory=ComplianceConfigSettings)
    proactive: ProactiveConfig = field(default_factory=ProactiveConfig)
    backup: BackupSettings = field(default_factory=BackupSettings)

    def validate(self) -> list[str]:
        """Check runtime prerequisites and return a list of human-readable warnings.

        Called automatically by load_config() at startup.  Returns an empty
        list when everything is configured correctly.  Never raises — validation
        failures are surfaced as warnings so the agent can still start and
        guide the user through setup.

        Checks:
          1. Provider/API-key alignment: if the configured provider requires a
             key (Anthropic, OpenAI) that key must be present in the environment.
          2. Ollama reachability: if Ollama is the default provider, it should
             respond on its base URL (non-blocking 2 s probe).
          3. Channel credential completeness: Telegram requires a bot token,
             Discord requires a bot token.
          4. Lightweight model sanity: if set, the model must be in the known
             registry or Ollama must be reachable.
        """
        issues: list[str] = []
        provider = self.llm.default_provider.lower()

        # ── 1. Provider ↔ API key ──────────────────────────────────────────
        if provider in ("anthropic", "claude"):
            if not os.environ.get("ANTHROPIC_API_KEY"):
                issues.append(
                    "Provider is 'anthropic' but ANTHROPIC_API_KEY is not set. "
                    "Set it in .env or export it before starting."
                )
        elif provider in ("openai", "gpt", "gpt-4o", "gpt-4o-mini"):
            if not os.environ.get("OPENAI_API_KEY"):
                issues.append(
                    "Provider is 'openai' but OPENAI_API_KEY is not set. "
                    "Set it in .env or export it before starting."
                )
        elif provider in ("ollama", "llama", "llama3.2", "mistral", "deepseek", "qwen"):
            # Probe Ollama with a short timeout — non-fatal if it fails
            try:
                import urllib.request

                req = urllib.request.Request(
                    self.llm.ollama_base_url.rstrip("/") + "/api/tags",
                    method="GET",
                )
                with urllib.request.urlopen(req, timeout=2):
                    pass  # reachable
            except Exception:
                issues.append(
                    f"Provider is '{provider}' but Ollama is not reachable at "
                    f"{self.llm.ollama_base_url}. Start it with: ollama serve"
                )

        # ── 2. Channel credentials ─────────────────────────────────────────
        if self.channels.telegram_enabled and not os.environ.get("TELEGRAM_BOT_TOKEN"):
            issues.append("Telegram channel is enabled but TELEGRAM_BOT_TOKEN is not set.")

        discord_enabled = getattr(self.channels, "discord_enabled", False)
        if discord_enabled and not os.environ.get("DISCORD_BOT_TOKEN"):
            issues.append("Discord channel is enabled but DISCORD_BOT_TOKEN is not set.")

        return issues


def ensure_dirs():
    """Create necessary directories."""
    AGENT_DIR.mkdir(exist_ok=True)
    DATA_DIR.mkdir(exist_ok=True)
    SKILLS_DIR.mkdir(exist_ok=True)


def _typed_setattr(obj: object, key: str, value: object) -> None:
    """Set a dataclass field with basic type coercion for primitives."""
    field_types = {f.name: f.type for f in fields(obj)}
    expected = field_types.get(key)
    if expected is None:
        return
    # Basic coercion for primitives
    try:
        if expected is int and not isinstance(value, int):
            value = int(value)
        elif expected is float and not isinstance(value, (int, float)):
            value = float(value)
        elif expected is bool and not isinstance(value, bool):
            value = str(value).lower() in ("true", "1", "yes")
        elif expected is str and not isinstance(value, str):
            value = str(value)
    except (ValueError, TypeError):
        logger.warning(f"Config: invalid type for {key}, expected {expected}, skipping")
        return
    setattr(obj, key, value)


def load_config(validate: bool = True) -> Config:
    """
    Load configuration from file and environment.

    Args:
        validate: If True, warn about unknown config keys

    Returns:
        Config object with loaded settings
    """
    ensure_dirs()

    config = Config()
    config_warnings: List[str] = []

    # Load from YAML if exists
    if CONFIG_FILE.exists():
        with open(CONFIG_FILE) as f:
            data = yaml.safe_load(f) or {}

        # Validate top-level keys
        if validate:
            known_sections = {
                "llm",
                "channels",
                "agent",
                "resilience",
                "observability",
                "mcp",
                "security",
                "compliance",
                "proactive",
                "backup",
            }
            unknown_sections = set(data.keys()) - known_sections
            for section in unknown_sections:
                msg = (
                    f"Unknown config section '{section}'. Valid sections: {sorted(known_sections)}"
                )
                config_warnings.append(msg)
                logger.warning(msg)

        if "llm" in data:
            if validate:
                _validate_config_section(data["llm"], LLMConfig, "llm", config_warnings)
            for k, v in data["llm"].items():
                if hasattr(config.llm, k):
                    _typed_setattr(config.llm, k, v)

        if "channels" in data:
            if validate:
                _validate_config_section(
                    data["channels"], ChannelConfig, "channels", config_warnings
                )
            for k, v in data["channels"].items():
                if hasattr(config.channels, k):
                    _typed_setattr(config.channels, k, v)

        if "agent" in data:
            if validate:
                _validate_config_section(data["agent"], AgentConfig, "agent", config_warnings)
            for k, v in data["agent"].items():
                if hasattr(config.agent, k):
                    _typed_setattr(config.agent, k, v)

        if "resilience" in data:
            if validate:
                _validate_config_section(
                    data["resilience"], ResilienceConfig, "resilience", config_warnings
                )
            for k, v in data["resilience"].items():
                if hasattr(config.resilience, k):
                    _typed_setattr(config.resilience, k, v)

        if "observability" in data:
            if validate:
                _validate_config_section(
                    data["observability"], ObservabilityConfig, "observability", config_warnings
                )
            for k, v in data["observability"].items():
                if hasattr(config.observability, k):
                    _typed_setattr(config.observability, k, v)

        if "mcp" in data:
            mcp_data = data["mcp"]
            if validate:
                _validate_config_section(mcp_data, MCPConfigSimple, "mcp", config_warnings)
            config.mcp.enabled = mcp_data.get("enabled", False)
            config.mcp.tool_prefix_separator = mcp_data.get("tool_prefix_separator", ":")
            config.mcp.connect_timeout = mcp_data.get("connect_timeout", 30.0)
            config.mcp.reconnect_on_failure = mcp_data.get("reconnect_on_failure", True)

            # Load server configs
            if "servers" in mcp_data:
                config.mcp.servers = []
                for server_data in mcp_data["servers"]:
                    if validate:
                        _validate_config_section(
                            server_data, MCPServerConfigSimple, "mcp.servers[]", config_warnings
                        )
                    server = MCPServerConfigSimple(
                        name=server_data.get("name", ""),
                        transport=server_data.get("transport", "stdio"),
                        command=server_data.get("command"),
                        args=server_data.get("args", []),
                        url=server_data.get("url"),
                        env=server_data.get("env", {}),
                        enabled=server_data.get("enabled", True),
                        requires_confirmation=server_data.get("requires_confirmation", False),
                    )
                    config.mcp.servers.append(server)

        # Load security settings
        if "security" in data:
            if validate:
                _validate_config_section(
                    data["security"], SecurityConfigSettings, "security", config_warnings
                )
            for k, v in data["security"].items():
                if hasattr(config.security, k):
                    _typed_setattr(config.security, k, v)

        # Load compliance settings
        if "compliance" in data:
            if validate:
                _validate_config_section(
                    data["compliance"], ComplianceConfigSettings, "compliance", config_warnings
                )
            for k, v in data["compliance"].items():
                if hasattr(config.compliance, k):
                    _typed_setattr(config.compliance, k, v)

        # Load proactive settings
        if "proactive" in data:
            if validate:
                _validate_config_section(
                    data["proactive"], ProactiveConfig, "proactive", config_warnings
                )
            for k, v in data["proactive"].items():
                if hasattr(config.proactive, k):
                    _typed_setattr(config.proactive, k, v)

        # Load backup settings
        if "backup" in data:
            if validate:
                _validate_config_section(data["backup"], BackupSettings, "backup", config_warnings)
            for k, v in data["backup"].items():
                if hasattr(config.backup, k):
                    _typed_setattr(config.backup, k, v)

        # Store warnings for access
        config._validation_warnings = config_warnings

        # Print warnings to stderr so users see them even without logging
        if config_warnings:
            import sys

            print(f"\n⚠️  Configuration warnings ({len(config_warnings)}):", file=sys.stderr)
            for w in config_warnings:
                print(f"   • {w}", file=sys.stderr)
            print(file=sys.stderr)

    # Override with environment variables
    if os.environ.get("ANTHROPIC_API_KEY"):
        pass  # Just needs to exist
    if os.environ.get("OPENAI_API_KEY"):
        pass

    if os.environ.get("TELEGRAM_BOT_TOKEN"):
        config.channels.telegram_enabled = True
        config.channels.telegram_token = os.environ["TELEGRAM_BOT_TOKEN"]

    if os.environ.get("DISCORD_BOT_TOKEN"):
        config.channels.discord_enabled = True
        config.channels.discord_token = os.environ["DISCORD_BOT_TOKEN"]

    if os.environ.get("MATRIX_HOMESERVER"):
        config.channels.matrix_enabled = True
        config.channels.matrix_homeserver = os.environ["MATRIX_HOMESERVER"]
    if os.environ.get("MATRIX_USER"):
        config.channels.matrix_user = os.environ["MATRIX_USER"]
    if os.environ.get("OWNER_MATRIX_ID"):
        config.channels.owner_matrix_id = os.environ["OWNER_MATRIX_ID"]

    if os.environ.get("SIGNAL_PHONE"):
        config.channels.signal_enabled = True
        config.channels.signal_phone = os.environ["SIGNAL_PHONE"]
    if os.environ.get("OWNER_SIGNAL_ID"):
        config.channels.owner_signal_id = os.environ["OWNER_SIGNAL_ID"]

    if os.environ.get("MASTODON_INSTANCE_URL"):
        config.channels.mastodon_enabled = True
        config.channels.mastodon_instance_url = os.environ["MASTODON_INSTANCE_URL"]
    if os.environ.get("MASTODON_ACCESS_TOKEN"):
        config.channels.mastodon_enabled = True
        config.channels.mastodon_access_token = os.environ["MASTODON_ACCESS_TOKEN"]
    if os.environ.get("OWNER_MASTODON_HANDLE"):
        config.channels.owner_mastodon_handle = os.environ["OWNER_MASTODON_HANDLE"]

    if os.environ.get("BLUESKY_HANDLE"):
        config.channels.bluesky_enabled = True
        config.channels.bluesky_handle = os.environ["BLUESKY_HANDLE"]
    if os.environ.get("BLUESKY_APP_PASSWORD"):
        config.channels.bluesky_enabled = True
        config.channels.bluesky_app_password = os.environ["BLUESKY_APP_PASSWORD"]
    if os.environ.get("BLUESKY_PDS_URL"):
        config.channels.bluesky_pds_url = os.environ["BLUESKY_PDS_URL"]
    if os.environ.get("OWNER_BLUESKY_HANDLE"):
        config.channels.owner_bluesky_handle = os.environ["OWNER_BLUESKY_HANDLE"]

    if os.environ.get("WHATSAPP_BRIDGE_HOST"):
        config.channels.whatsapp_enabled = True
        config.channels.whatsapp_bridge_host = os.environ["WHATSAPP_BRIDGE_HOST"]
    if os.environ.get("WHATSAPP_BRIDGE_PORT"):
        config.channels.whatsapp_enabled = True
        try:
            config.channels.whatsapp_bridge_port = int(os.environ["WHATSAPP_BRIDGE_PORT"])
        except ValueError as e:
            logger.debug("Value conversion error suppressed: %s", e)
    if os.environ.get("OWNER_WHATSAPP_ID"):
        config.channels.owner_whatsapp_id = os.environ["OWNER_WHATSAPP_ID"]

    if os.environ.get("OWNER_TELEGRAM_ID"):
        try:
            config.channels.owner_telegram_id = int(os.environ["OWNER_TELEGRAM_ID"])
            # Owner is always in allowed list
            if config.channels.owner_telegram_id not in config.channels.telegram_allowed_users:
                config.channels.telegram_allowed_users.append(config.channels.owner_telegram_id)
        except ValueError as e:
            logger.debug("Value conversion error suppressed: %s", e)
    if os.environ.get("DEFAULT_PROVIDER"):
        config.llm.default_provider = os.environ["DEFAULT_PROVIDER"]
    elif os.environ.get("LLM_DEFAULT_PROVIDER"):
        config.llm.default_provider = os.environ["LLM_DEFAULT_PROVIDER"]

    if os.environ.get("OLLAMA_MODEL"):
        config.llm.ollama_model = os.environ["OLLAMA_MODEL"]

    if os.environ.get("FAMILIAR_LIGHTWEIGHT_MODEL"):
        config.llm.lightweight_model = os.environ["FAMILIAR_LIGHTWEIGHT_MODEL"]

    if os.environ.get("FAMILIAR_LIGHTWEIGHT_PROVIDER"):
        config.llm.lightweight_provider = os.environ["FAMILIAR_LIGHTWEIGHT_PROVIDER"]

    if os.environ.get("AGENT_NAME"):
        config.agent.name = os.environ["AGENT_NAME"]

    # Run startup credential validation — warns clearly about missing API keys
    # so users don't discover the gap on their first actual message.
    issues = config.validate()
    if issues:
        import sys

        print("\n⚠️  Startup configuration issues:", file=sys.stderr)
        for issue in issues:
            print(f"   • {issue}", file=sys.stderr)
        print(file=sys.stderr)

    return config


def save_default_config():
    """Save default configuration file."""
    ensure_dirs()

    default = {
        "llm": {
            "default_provider": "anthropic",
            "anthropic_model": "claude-sonnet-4-6",
            "openai_model": "gpt-4o",
            "ollama_model": "llama3.2",
            "ollama_base_url": "http://localhost:11434",
            "max_tokens": 4096,
        },
        "channels": {
            "telegram_enabled": False,
            "telegram_token": None,
            "telegram_allowed_users": [],
            "discord_enabled": False,
            "cli_enabled": True,
        },
        "agent": {
            "name": "Familiar",
            "persona": "a helpful AI assistant running on a Raspberry Pi",
            "memory_enabled": True,
            "max_conversation_history": 50,
            "skills_enabled": True,
            "scheduler_enabled": True,
            "heartbeat_interval_minutes": 60,
            "allow_shell_commands": True,
            "allow_file_write": True,
            "job_class": "",
        },
        "resilience": {
            "enabled": True,
            "max_attempts": 3,
            "base_delay": 1.0,
            "max_delay": 60.0,
            "exponential_base": 2.0,
            "jitter": 0.1,
        },
        "observability": {
            "tracing_enabled": True,
            "cost_tracking_enabled": True,
            "log_traces": True,
            "log_level": "INFO",
        },
        "mcp": {
            "enabled": False,
            "tool_prefix_separator": ":",
            "connect_timeout": 30.0,
            "reconnect_on_failure": True,
            "servers": [
                {
                    "name": "filesystem",
                    "transport": "stdio",
                    "command": "uvx",
                    "args": ["mcp-server-filesystem", "/home"],
                    "enabled": False,
                },
                {
                    "name": "github",
                    "transport": "stdio",
                    "command": "npx",
                    "args": ["-y", "@modelcontextprotocol/server-github"],
                    "env": {"GITHUB_TOKEN": "${GITHUB_TOKEN}"},
                    "enabled": False,
                },
            ],
        },
        "security": {
            "encrypt_sessions": False,
            "encrypt_memory": False,
            "encrypt_history": False,
            "encryption_key_env": "FAMILIAR_ENCRYPTION_KEY",
            "session_timeout_minutes": 60,
            "require_reauth_for_sensitive": False,
            "auto_trust_upgrade": True,
        },
        "compliance": {
            "mode": "none",  # none, hipaa, soc2, pci_dss, gdpr
            "retention_years": 1,
            "require_encryption": False,
            "log_phi_access": False,
        },
    }

    with open(CONFIG_FILE, "w") as f:
        yaml.dump(default, f, default_flow_style=False)

    print(f"Created default config at {CONFIG_FILE}")


if __name__ == "__main__":
    save_default_config()
