from __future__ import annotations

import time
from collections import defaultdict, deque


class PaperTrader:
    """Long-only moving-average paper trader with a fixed virtual cash balance."""

    def __init__(self, cash: float = 10_000, fast_window: int = 5, slow_window: int = 20, allocation: float = 0.25):
        if not 0 < allocation <= 1 or fast_window >= slow_window:
            raise ValueError("allocation must be in (0, 1] and fast_window must be below slow_window")
        self.initial_cash = float(cash)
        self.cash = float(cash)
        self.fast_window = fast_window
        self.slow_window = slow_window
        self.allocation = allocation
        self.history: dict[str, deque[float]] = defaultdict(lambda: deque(maxlen=slow_window))
        self.positions: dict[str, dict[str, float]] = {}
        self.trades: list[dict] = []

    def step(self, pair: str, price: float) -> str | None:
        prices = self.history[pair]
        prices.append(float(price))
        if len(prices) < self.slow_window:
            return None
        fast = sum(list(prices)[-self.fast_window:]) / self.fast_window
        slow = sum(prices) / self.slow_window
        position = self.positions.get(pair)
        if fast > slow and position is None:
            spend = self.cash * self.allocation
            quantity = spend / price
            self.cash -= spend
            self.positions[pair] = {"quantity": quantity, "entry_price": float(price)}
            self.trades.append({"time": int(time.time()), "pair": pair, "side": "buy", "price": float(price), "quantity": quantity})
            return "buy"
        if fast < slow and position is not None:
            quantity = position["quantity"]
            self.cash += quantity * price
            del self.positions[pair]
            self.trades.append({"time": int(time.time()), "pair": pair, "side": "sell", "price": float(price), "quantity": quantity})
            return "sell"
        return None

    def snapshot(self, prices: dict[str, float]) -> dict:
        market_value = sum(pos["quantity"] * prices.get(pair, pos["entry_price"]) for pair, pos in self.positions.items())
        equity = self.cash + market_value
        return {
            "initial_cash": self.initial_cash,
            "cash": self.cash,
            "market_value": market_value,
            "equity": equity,
            "return_pct": (equity / self.initial_cash - 1) * 100,
            "positions": self.positions.copy(),
            "trades": self.trades[-20:],
            "strategy": {"name": "Moving-average crossover", "fast_window": self.fast_window, "slow_window": self.slow_window, "allocation": self.allocation},
        }
