"""
Configuration management for jbqlab.

This module provides configuration management through:
- Default configuration values
- Config file loading (YAML/JSON)
- Environment variable overrides
"""

import json
import os
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any


@dataclass
class EngineConfig:
    """Configuration for the backtest engine."""

    transaction_cost: float = 0.001  # 0.1%
    slippage: float = 0.0005  # 0.05%
    initial_capital: float = 100_000.0
    risk_free_rate: float = 0.0


@dataclass
class OutputConfig:
    """Configuration for output files."""

    save_metrics: bool = True
    save_equity_curve: bool = True
    save_positions: bool = True
    save_plot: bool = True
    plot_dpi: int = 150
    date_format: str = "%Y-%m-%d"


@dataclass
class JBQLabConfig:
    """Main configuration class for jbqlab.

    Can be loaded from:
    - Default values
    - Config file (jbqlab.json or jbqlab.yaml)
    - Environment variables (JBQLAB_*)
    """

    engine: EngineConfig = field(default_factory=EngineConfig)
    output: OutputConfig = field(default_factory=OutputConfig)

    # Global settings
    verbose: bool = True
    parallel_workers: int = 1  # For future parallel optimization

    @classmethod
    def load(cls, config_path: str | Path | None = None) -> "JBQLabConfig":
        """Load configuration from file and environment.

        Args:
            config_path: Optional path to config file. If None, searches
                for jbqlab.json in current directory.

        Returns:
            Loaded configuration.
        """
        config = cls()

        # Try to load from file
        if config_path is None:
            for default_path in ["jbqlab.json", "jbqlab.yaml", ".jbqlab.json"]:
                if Path(default_path).exists():
                    config_path = default_path
                    break

        if config_path is not None:
            config = cls._load_from_file(Path(config_path))

        # Apply environment variable overrides
        config = cls._apply_env_overrides(config)

        return config

    @classmethod
    def _load_from_file(cls, path: Path) -> "JBQLabConfig":
        """Load configuration from JSON or YAML file."""
        if not path.exists():
            raise FileNotFoundError(f"Config file not found: {path}")

        with open(path) as f:
            if path.suffix in (".yaml", ".yml"):
                try:
                    import yaml
                    data = yaml.safe_load(f)
                except ImportError as err:
                    raise ImportError("PyYAML required to load YAML config files") from err
            else:
                data = json.load(f)

        return cls._from_dict(data)

    @classmethod
    def _from_dict(cls, data: dict[str, Any]) -> "JBQLabConfig":
        """Create config from dictionary."""
        config = cls()

        if "engine" in data:
            for key, value in data["engine"].items():
                if hasattr(config.engine, key):
                    setattr(config.engine, key, value)

        if "output" in data:
            for key, value in data["output"].items():
                if hasattr(config.output, key):
                    setattr(config.output, key, value)

        if "verbose" in data:
            config.verbose = data["verbose"]

        if "parallel_workers" in data:
            config.parallel_workers = data["parallel_workers"]

        return config

    @classmethod
    def _apply_env_overrides(cls, config: "JBQLabConfig") -> "JBQLabConfig":
        """Apply environment variable overrides."""
        env_mappings = {
            "JBQLAB_TRANSACTION_COST": ("engine", "transaction_cost", float),
            "JBQLAB_SLIPPAGE": ("engine", "slippage", float),
            "JBQLAB_INITIAL_CAPITAL": ("engine", "initial_capital", float),
            "JBQLAB_RISK_FREE_RATE": ("engine", "risk_free_rate", float),
            "JBQLAB_VERBOSE": (None, "verbose", lambda x: x.lower() == "true"),
        }

        for env_var, (section, attr, converter) in env_mappings.items():
            value = os.environ.get(env_var)
            if value is not None:
                converted = converter(value)
                if section:
                    setattr(getattr(config, section), attr, converted)
                else:
                    setattr(config, attr, converted)

        return config

    def to_dict(self) -> dict[str, Any]:
        """Convert config to dictionary."""
        return {
            "engine": {
                "transaction_cost": self.engine.transaction_cost,
                "slippage": self.engine.slippage,
                "initial_capital": self.engine.initial_capital,
                "risk_free_rate": self.engine.risk_free_rate,
            },
            "output": {
                "save_metrics": self.output.save_metrics,
                "save_equity_curve": self.output.save_equity_curve,
                "save_positions": self.output.save_positions,
                "save_plot": self.output.save_plot,
                "plot_dpi": self.output.plot_dpi,
                "date_format": self.output.date_format,
            },
            "verbose": self.verbose,
            "parallel_workers": self.parallel_workers,
        }

    def save(self, path: str | Path) -> None:
        """Save configuration to file."""
        path = Path(path)
        with open(path, "w") as f:
            json.dump(self.to_dict(), f, indent=2)


# Global default config instance
_default_config: JBQLabConfig | None = None


def get_config() -> JBQLabConfig:
    """Get the current configuration.

    Loads config on first call, then returns cached instance.
    """
    global _default_config
    if _default_config is None:
        _default_config = JBQLabConfig.load()
    return _default_config


def set_config(config: JBQLabConfig) -> None:
    """Set the global configuration."""
    global _default_config
    _default_config = config
