"""High-Fidelity Bybit V5 Mock Client for Testing and Offline Simulation."""
from __future__ import annotations

import math
import random
import time
from decimal import Decimal
from typing import Any, Optional

from backend.app.schemas.market import Candle, OrderBook, OrderBookLevel, Ticker
from backend.app.schemas.risk import InstrumentFilters


class MockBybitClient:
    """Simulates Bybit V5 REST endpoints with realistic synthetic market dynamics."""

    def __init__(self, initial_btc_price: float = 65000.0) -> None:
        self.current_prices = {
            "BTCUSDT": Decimal(str(initial_btc_price)),
            "ETHUSDT": Decimal("3500.0"),
            "SOLUSDT": Decimal("150.0"),
        }
        self.orders: dict[str, dict[str, Any]] = {}
        self.mock_equity = Decimal("10000.0")

    def get_server_time(self) -> int:
        return int(time.time() * 1000)

    def get_klines(self, symbol: str, interval: str = "15m", limit: int = 200) -> list[Candle]:
        base_price = float(self.current_prices.get(symbol, Decimal("65000.0")))
        candles: list[Candle] = []
        now_ms = int(time.time() * 1000)
        step_ms = 15 * 60 * 1000  # 15 min default

        # Deterministic pseudo-random seed for repeatable testing
        rng = random.Random(42)
        price = base_price * 0.95

        for i in range(limit):
            t = now_ms - (limit - i) * step_ms
            drift = 0.0002
            volatility = 0.004
            pct_change = drift + volatility * (rng.random() - 0.48)
            open_p = price
            close_p = price * (1 + pct_change)
            high_p = max(open_p, close_p) * (1 + 0.002 * rng.random())
            low_p = min(open_p, close_p) * (1 - 0.002 * rng.random())
            volume = 10.0 + 50.0 * rng.random()

            candles.append(
                Candle(
                    symbol=symbol,
                    interval=interval,
                    timestamp=t,
                    open=Decimal(f"{open_p:.2f}"),
                    high=Decimal(f"{high_p:.2f}"),
                    low=Decimal(f"{low_p:.2f}"),
                    close=Decimal(f"{close_p:.2f}"),
                    volume=Decimal(f"{volume:.4f}"),
                    is_closed=True,
                )
            )
            price = close_p

        # Update last price to match latest candle close
        self.current_prices[symbol] = candles[-1].close
        return candles

    def get_ticker(self, symbol: str) -> Ticker:
        price = self.current_prices.get(symbol, Decimal("65000.0"))
        spread = price * Decimal("0.0001")
        bid = price - spread
        ask = price + spread
        return Ticker(
            symbol=symbol,
            bid_price=bid,
            ask_price=ask,
            last_price=price,
            volume_24h=Decimal("12500.5"),
            price_24h_pcnt=Decimal("0.015"),
            high_24h=price * Decimal("1.02"),
            low_24h=price * Decimal("0.98"),
            timestamp=int(time.time() * 1000),
        )

    def get_orderbook(self, symbol: str, limit: int = 50) -> OrderBook:
        ticker = self.get_ticker(symbol)
        bids = [
            OrderBookLevel(price=ticker.bid_price - Decimal(str(i * 0.5)), size=Decimal("1.5"))
            for i in range(limit)
        ]
        asks = [
            OrderBookLevel(price=ticker.ask_price + Decimal(str(i * 0.5)), size=Decimal("1.5"))
            for i in range(limit)
        ]
        return OrderBook(symbol=symbol, bids=bids, asks=asks, timestamp=int(time.time() * 1000))

    def get_instrument_filters(self, symbol: str) -> InstrumentFilters:
        return InstrumentFilters(
            symbol=symbol,
            min_order_qty=Decimal("0.001"),
            qty_step=Decimal("0.001"),
            min_notional=Decimal("5.0"),
            tick_size=Decimal("0.1"),
            max_leverage=Decimal("10.0"),
        )

    def get_wallet_balance(self, account_type: str = "UNIFIED") -> dict[str, Any]:
        return {
            "retCode": 0,
            "retMsg": "OK",
            "result": {
                "list": [
                    {
                        "totalEquity": str(self.mock_equity),
                        "totalAvailableBalance": str(self.mock_equity),
                        "coin": [{"coin": "USDT", "walletBalance": str(self.mock_equity)}],
                    }
                ]
            },
        }

    def place_order(
        self,
        symbol: str,
        side: str,
        order_type: str,
        qty: str,
        price: Optional[str] = None,
        stop_loss: Optional[str] = None,
        take_profit: Optional[str] = None,
        client_order_id: Optional[str] = None,
        reduce_only: bool = False,
    ) -> dict[str, Any]:
        oid = f"mock_{int(time.time()*1000)}"
        order_data = {
            "orderId": oid,
            "orderLinkId": client_order_id or oid,
            "symbol": symbol,
            "side": side,
            "orderType": order_type,
            "qty": qty,
            "price": price or str(self.current_prices.get(symbol, Decimal("65000.0"))),
            "stopLoss": stop_loss,
            "takeProfit": take_profit,
            "orderStatus": "Filled" if order_type.upper() == "MARKET" else "New",
        }
        self.orders[client_order_id or oid] = order_data
        return {"retCode": 0, "retMsg": "OK", "result": order_data}

    def cancel_order(self, symbol: str, client_order_id: str) -> dict[str, Any]:
        if client_order_id in self.orders:
            self.orders[client_order_id]["orderStatus"] = "Cancelled"
            return {"retCode": 0, "retMsg": "OK", "result": {"orderLinkId": client_order_id}}
        return {"retCode": 110001, "retMsg": "Order not found"}
