#!/usr/bin/env python3
"""
config.py - Echo Ginnie Configuration (WO#GINNIE_01)
=====================================================

Defines configuration for Echo Ginnie sessions:
- Workspace paths and sandbox boundaries
- Backend selection (OpenAI, Anthropic, local GGUF)
- Tool registration
"""

import os
import yaml
from dataclasses import dataclass, field
from pathlib import Path
from typing import Dict, List, Optional, Any


@dataclass
class BackendConfig:
    """Configuration for an LLM backend."""
    name: str
    type: str  # "openai", "anthropic", "local_gguf"
    api_key: Optional[str] = None
    model: str = ""
    endpoint: Optional[str] = None
    model_path: Optional[Path] = None  # For local GGUF
    prompt_format: str = "mistral"  # "mistral", "chatml" (Qwen), "llama2"

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "BackendConfig":
        return cls(
            name=data.get("name", "default"),
            type=data.get("type", "openai"),
            api_key=data.get("api_key"),
            model=data.get("model", "gpt-4o-mini"),
            endpoint=data.get("endpoint"),
            model_path=Path(data["model_path"]) if data.get("model_path") else None,
            prompt_format=data.get("prompt_format", "mistral"),
        )


@dataclass
class GinnieConfig:
    """Main Echo Ginnie configuration."""

    # Workspace
    repo_root: Path = field(default_factory=lambda: Path.home() / "helix-cdc")
    sandbox_roots: List[Path] = field(default_factory=list)
    scratch_dir: Path = field(default_factory=lambda: Path("/tmp/echo_ginnie_scratch"))

    # Backends
    backends: Dict[str, BackendConfig] = field(default_factory=dict)
    default_backend: str = "local"  # Prefer local GGUF over cloud APIs

    # Tools
    enabled_tools: List[str] = field(default_factory=lambda: [
        "helix_healthcheck",
        "helix_perf_suite",
        "krisper_score",
        "bio_poetica_expand",
        "sandbox_read",
        "sandbox_write",
        "sandbox_list",
    ])

    # Jazz timing
    jazz_enabled: bool = True
    valley_threshold: float = 40.0
    max_wait: float = 0.3
    scar_threshold: int = 5

    # Logging
    receipts_dir: Path = field(default_factory=lambda: Path.home() / "helix-cdc/receipts")
    logs_dir: Path = field(default_factory=lambda: Path.home() / "helix-cdc/logs/ginnie")

    def __post_init__(self):
        """Set up defaults."""
        if not self.sandbox_roots:
            self.sandbox_roots = [self.repo_root]

        # Default backends from environment
        if not self.backends:
            # OpenAI
            if os.getenv("OPENAI_API_KEY"):
                self.backends["openai"] = BackendConfig(
                    name="openai",
                    type="openai",
                    api_key=os.getenv("OPENAI_API_KEY"),
                    model="gpt-4o-mini",
                )

            # Anthropic
            if os.getenv("ANTHROPIC_API_KEY"):
                self.backends["anthropic"] = BackendConfig(
                    name="anthropic",
                    type="anthropic",
                    api_key=os.getenv("ANTHROPIC_API_KEY"),
                    model="claude-sonnet-4-20250514",
                )

            # Local GGUF (if present) - use working decoded model
            local_model = Path.home() / "helix-cdc/tmp/gguf_roundtrip_test/decoded_mistral-7b-instruct-v0.2.Q4_K_M.gguf"
            if local_model.exists():
                self.backends["local"] = BackendConfig(
                    name="local",
                    type="local_gguf",
                    model_path=local_model,
                )

    @classmethod
    def load(cls, config_path: Optional[Path] = None) -> "GinnieConfig":
        """Load config from YAML file."""
        if config_path is None:
            config_path = Path.home() / ".echo_ginnie.yaml"

        if not config_path.exists():
            return cls()

        with open(config_path) as f:
            data = yaml.safe_load(f) or {}

        config = cls()

        if "repo_root" in data:
            config.repo_root = Path(data["repo_root"])
        if "sandbox_roots" in data:
            config.sandbox_roots = [Path(p) for p in data["sandbox_roots"]]
        if "scratch_dir" in data:
            config.scratch_dir = Path(data["scratch_dir"])
        if "default_backend" in data:
            config.default_backend = data["default_backend"]
        if "backends" in data:
            for name, backend_data in data["backends"].items():
                backend_data["name"] = name
                config.backends[name] = BackendConfig.from_dict(backend_data)
        if "enabled_tools" in data:
            config.enabled_tools = data["enabled_tools"]
        if "jazz_enabled" in data:
            config.jazz_enabled = data["jazz_enabled"]
        if "valley_threshold" in data:
            config.valley_threshold = data["valley_threshold"]

        return config

    def get_backend(self, name: Optional[str] = None) -> Optional[BackendConfig]:
        """Get a backend configuration."""
        name = name or self.default_backend
        return self.backends.get(name)

    def is_path_allowed(self, path: Path) -> bool:
        """Check if a path is within sandbox boundaries."""
        path = path.resolve()

        # Check sandbox roots
        for root in self.sandbox_roots:
            try:
                path.relative_to(root.resolve())
                return True
            except ValueError:
                continue

        # Check scratch dir
        try:
            path.relative_to(self.scratch_dir.resolve())
            return True
        except ValueError:
            pass

        return False


# Global config instance
_config: Optional[GinnieConfig] = None


def get_config() -> GinnieConfig:
    """Get or create global config."""
    global _config
    if _config is None:
        _config = GinnieConfig.load()
    return _config


def set_config(config: GinnieConfig) -> None:
    """Set global config."""
    global _config
    _config = config
