from __future__ import annotations

import json
from dataclasses import dataclass
from pathlib import Path
from typing import Any


@dataclass(frozen=True)
class PairConfig:
    symbol: str
    strategy: str
    interval: int = 15
    candles: int = 200
    enabled: bool = True
    min_order_base: float = 0.0
    max_order_quote: float | None = None
    strategy_params: dict[str, Any] | None = None


@dataclass(frozen=True)
class RiskConfig:
    quote_currency: str = "USD"
    dry_run: bool = True
    fixed_order_quote: float = 25.0
    max_order_quote: float = 100.0
    quote_balance_fraction: float = 0.05
    price_precision: int = 2
    volume_precision: int = 8
    cooldown_seconds: int = 900
    allow_repeated_side: bool = False


@dataclass(frozen=True)
class KrakenConfig:
    api_key_env: str = "KRAKEN_API_KEY"
    api_secret_env: str = "KRAKEN_API_SECRET"
    base_url: str = "https://api.kraken.com"
    timeout_seconds: int = 20


@dataclass(frozen=True)
class BotConfig:
    kraken: KrakenConfig
    risk: RiskConfig
    state_file: Path
    pairs: list[PairConfig]


def load_config(path: str | Path) -> BotConfig:
    config_path = Path(path)
    raw = json.loads(config_path.read_text(encoding="utf-8"))

    symbols_seen: set[str] = set()
    pairs: list[PairConfig] = []
    for item in raw.get("pairs", []):
        symbol = item["symbol"].upper()
        if symbol in symbols_seen:
            raise ValueError(f"Pair '{symbol}' is configured more than once")
        symbols_seen.add(symbol)
        if not item.get("strategy"):
            raise ValueError(f"Pair '{symbol}' must define exactly one strategy")
        pairs.append(
            PairConfig(
                symbol=symbol,
                strategy=item["strategy"],
                interval=int(item.get("interval", 15)),
                candles=int(item.get("candles", 200)),
                enabled=bool(item.get("enabled", True)),
                min_order_base=float(item.get("min_order_base", 0.0)),
                max_order_quote=(
                    float(item["max_order_quote"]) if item.get("max_order_quote") is not None else None
                ),
                strategy_params=item.get("strategy_params", {}),
            )
        )

    if not pairs:
        raise ValueError("At least one pair must be configured")

    kraken = KrakenConfig(**raw.get("kraken", {}))
    risk = RiskConfig(**raw.get("risk", {}))
    state_file = Path(raw.get("state_file", "state/bot_state.json"))
    if not state_file.is_absolute():
        state_file = config_path.parent / state_file
    return BotConfig(kraken=kraken, risk=risk, state_file=state_file, pairs=pairs)
