from __future__ import annotations

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


@dataclass
class PaperExchange:
    state_path: Path
    positions: dict[str, dict[str, Any]] = field(default_factory=dict)
    orders: list[dict[str, Any]] = field(default_factory=list)
    leverage: dict[str, float] = field(default_factory=dict)

    def __post_init__(self) -> None:
        self.state_path = Path(self.state_path).expanduser()
        self._load()

    def _load(self) -> None:
        if not self.state_path.exists():
            return
        data = json.loads(self.state_path.read_text(encoding="utf-8"))
        self.positions = data.get("positions", {})
        self.orders = data.get("orders", [])
        self.leverage = data.get("leverage", {})

    def _save(self) -> None:
        self.state_path.parent.mkdir(parents=True, exist_ok=True)
        self.state_path.write_text(json.dumps({
            "positions": self.positions,
            "orders": self.orders,
            "leverage": self.leverage,
        }, indent=2, sort_keys=True), encoding="utf-8")

    @staticmethod
    def _coin(symbol: str) -> str:
        return symbol.split("/")[0]

    def set_leverage(self, leverage: int | float, symbol: str) -> dict[str, Any]:
        self.leverage[symbol] = float(leverage)
        self._save()
        return {"status": "ok", "paper": True, "action": "set_leverage", "symbol": symbol, "leverage": leverage}

    def create_order(self, symbol: str, order_type: str, side: str, amount: float, price: float | None = None, params: dict[str, Any] | None = None) -> dict[str, Any]:
        if price is None:
            raise ValueError("PaperExchange benötigt einen expliziten Preis")
        params = params or {}
        coin = self._coin(symbol)
        signed_amount = float(amount) if side == "buy" else -float(amount)
        current = self.positions.get(coin, {"contracts": 0.0, "entryPrice": 0.0, "side": "long"})
        old_contracts = float(current.get("contracts", 0.0))
        new_contracts = old_contracts + signed_amount
        if abs(new_contracts) < 1e-12:
            new_contracts = 0.0
        if new_contracts == 0.0:
            entry_price = 0.0
            pos_side = "long"
        elif old_contracts == 0.0 or (old_contracts > 0) != (new_contracts > 0):
            entry_price = float(price)
            pos_side = "long" if new_contracts > 0 else "short"
        else:
            entry_price = ((float(current.get("entryPrice", price)) * abs(old_contracts)) + (float(price) * abs(signed_amount))) / abs(new_contracts)
            pos_side = "long" if new_contracts > 0 else "short"

        self.positions[coin] = {
            "contracts": abs(new_contracts),
            "entryPrice": entry_price,
            "side": pos_side,
            "symbol": symbol,
            **({} if new_contracts == 0.0 else dict(params.get("paper_position_state") or {})),
        }
        order = {"status": "ok", "paper": True, "symbol": symbol, "type": order_type, "side": side, "amount": float(amount), "price": float(price), "params": params}
        self.orders.append(order)
        self._save()
        return order

    def update_position_state(self, coin: str, metadata: dict[str, Any]) -> None:
        coin = coin.upper()
        if coin in self.positions and float(self.positions[coin].get("contracts", 0.0)) > 0:
            self.positions[coin].update(metadata)
            self._save()

    def fetch_positions(self) -> list[dict[str, Any]]:
        positions = []
        for coin, pos in sorted(self.positions.items()):
            contracts = float(pos.get("contracts", 0.0))
            if contracts == 0.0:
                continue
            symbol = pos.get("symbol") or f"{coin}/USDC:USDC"
            row = {
                "symbol": symbol,
                "contracts": contracts,
                "entryPrice": float(pos.get("entryPrice", 0.0)),
                "leverage": int(self.leverage.get(symbol, 1)),
                "side": pos.get("side", "long"),
            }
            for key in ("entryTs", "highPrice", "atrSlPx", "beActive", "trailingActive"):
                if key in pos:
                    row[key] = pos[key]
            positions.append(row)
        return positions
