"""
Fee-adjusted spread calculation.
"""
from dataclasses import dataclass
from decimal import Decimal

from models import Tick
from config import FEES, SPREAD_THRESHOLD_BPS


@dataclass(slots=True)
class SpreadResult:
    """Result of a spread calculation between two exchanges."""
    symbol: str
    spread_bps: Decimal          # Net spread in basis points (after fees)
    gross_spread_bps: Decimal    # Gross spread before fees
    buy_exchange: str
    sell_exchange: str
    buy_price: Decimal           # Ask price on buy exchange
    sell_price: Decimal          # Bid price on sell exchange
    buy_tick: Tick
    sell_tick: Tick
    is_opportunity: bool         # True if spread >= threshold

    def potential_profit_pct(self) -> Decimal:
        """Return potential profit as a percentage."""
        return self.spread_bps / 100

    def __repr__(self) -> str:
        direction = "BUY" if self.is_opportunity else "---"
        return (
            f"Spread({self.symbol}: {direction} {self.buy_exchange}@{self.buy_price} "
            f"-> SELL {self.sell_exchange}@{self.sell_price} "
            f"= {self.spread_bps:.2f}bps net)"
        )


def calculate_spread(
    lead_tick: Tick,
    lag_tick: Tick,
    fees: dict[str, Decimal] | None = None,
    threshold_bps: Decimal | None = None,
) -> SpreadResult:
    """
    Calculate the fee-adjusted spread between two exchanges.

    The strategy is: Buy on lead exchange (at ask), Sell on lag exchange (at bid).

    Args:
        lead_tick: Tick from the lead exchange (e.g., Binance)
        lag_tick: Tick from the lag exchange (e.g., Coinbase)
        fees: Fee dictionary (exchange -> fee as decimal). Defaults to config.FEES.
        threshold_bps: Minimum spread to consider an opportunity. Defaults to config value.

    Returns:
        SpreadResult with the calculated spread and opportunity flag.
    """
    if fees is None:
        fees = FEES
    if threshold_bps is None:
        threshold_bps = SPREAD_THRESHOLD_BPS

    # Gross spread (before fees): sell price - buy price
    gross_spread = lag_tick.bid - lead_tick.ask
    gross_spread_bps = (gross_spread / lead_tick.ask) * 10000

    # Fee-adjusted calculation
    # Effective buy price = ask * (1 + fee)
    # Effective sell price = bid * (1 - fee)
    lead_fee = fees.get(lead_tick.exchange, Decimal("0.001"))
    lag_fee = fees.get(lag_tick.exchange, Decimal("0.001"))

    effective_buy = lead_tick.ask * (1 + lead_fee)
    effective_sell = lag_tick.bid * (1 - lag_fee)

    # Net spread after fees
    net_spread = effective_sell - effective_buy
    net_spread_bps = (net_spread / effective_buy) * 10000

    is_opportunity = net_spread_bps >= threshold_bps

    return SpreadResult(
        symbol=lead_tick.symbol,
        spread_bps=net_spread_bps,
        gross_spread_bps=gross_spread_bps,
        buy_exchange=lead_tick.exchange,
        sell_exchange=lag_tick.exchange,
        buy_price=lead_tick.ask,
        sell_price=lag_tick.bid,
        buy_tick=lead_tick,
        sell_tick=lag_tick,
        is_opportunity=is_opportunity,
    )


def calculate_reverse_spread(
    lead_tick: Tick,
    lag_tick: Tick,
    fees: dict[str, Decimal] | None = None,
    threshold_bps: Decimal | None = None,
) -> SpreadResult:
    """
    Calculate spread for the reverse direction: Buy on lag, Sell on lead.

    This captures opportunities where the lag exchange is cheaper than the lead.
    """
    if fees is None:
        fees = FEES
    if threshold_bps is None:
        threshold_bps = SPREAD_THRESHOLD_BPS

    # Reverse: buy on lag (at ask), sell on lead (at bid)
    gross_spread = lead_tick.bid - lag_tick.ask
    gross_spread_bps = (gross_spread / lag_tick.ask) * 10000

    lag_fee = fees.get(lag_tick.exchange, Decimal("0.001"))
    lead_fee = fees.get(lead_tick.exchange, Decimal("0.001"))

    effective_buy = lag_tick.ask * (1 + lag_fee)
    effective_sell = lead_tick.bid * (1 - lead_fee)

    net_spread = effective_sell - effective_buy
    net_spread_bps = (net_spread / effective_buy) * 10000

    is_opportunity = net_spread_bps >= threshold_bps

    return SpreadResult(
        symbol=lead_tick.symbol,
        spread_bps=net_spread_bps,
        gross_spread_bps=gross_spread_bps,
        buy_exchange=lag_tick.exchange,
        sell_exchange=lead_tick.exchange,
        buy_price=lag_tick.ask,
        sell_price=lead_tick.bid,
        buy_tick=lag_tick,
        sell_tick=lead_tick,
        is_opportunity=is_opportunity,
    )
