from __future__ import annotations

from dataclasses import dataclass
from decimal import Decimal, ROUND_DOWN

from src.execution.cost_model import CostModel, TradeCostInput
from src.execution.order_intent import OrderIntent

STRATEGY_ID = "candidate_swing_trend_retest_research"
ALLOWED_COINS = {"BTC", "ETH", "SOL", "LINK"}


@dataclass(frozen=True)
class SwingRetestContext:
    coin: str
    symbol: str
    current_price: Decimal
    sma_fast: Decimal
    sma_slow: Decimal
    swing_high: Decimal
    retest_low: Decimal
    atr_pct: Decimal
    trend_strength_pct: Decimal
    pullback_from_high_pct: Decimal
    reclaim_pct: Decimal
    volume_24h: Decimal
    spread_pct: Decimal
    expected_slippage_pct: Decimal
    funding_rate_hourly_pct: Decimal
    market_breadth: int
    relative_strength_rank: int
    wallet_equity_usdc: Decimal
    data_quality_allowed: bool = True


def _size_for_notional(notional: Decimal, price: Decimal) -> Decimal:
    if price <= 0:
        return Decimal("0")
    return (notional / price).quantize(Decimal("0.00000001"), rounding=ROUND_DOWN)


def swing_retest_blockers(ctx: SwingRetestContext) -> list[str]:
    coin = ctx.coin.upper()
    blockers: list[str] = []
    if coin not in ALLOWED_COINS:
        blockers.append("coin_not_primary_swing_universe")
    if not ctx.data_quality_allowed:
        blockers.append("data_quality")
    if ctx.volume_24h < Decimal("30000000"):
        blockers.append("weak_liquidity")
    if ctx.spread_pct > Decimal("0.08"):
        blockers.append("spread_too_wide")
    if ctx.market_breadth < 3:
        blockers.append("weak_market_breadth")
    if ctx.relative_strength_rank > 3:
        blockers.append("weak_relative_strength")
    if not (ctx.current_price > ctx.sma_fast > ctx.sma_slow):
        blockers.append("trend_not_confirmed")
    if ctx.trend_strength_pct < Decimal("1.50"):
        blockers.append("trend_strength_too_low")
    if ctx.pullback_from_high_pct < Decimal("0.80"):
        blockers.append("no_meaningful_pullback")
    if ctx.pullback_from_high_pct > Decimal("8.00"):
        blockers.append("pullback_too_deep")
    if ctx.current_price <= ctx.retest_low:
        blockers.append("retest_not_held")
    if ctx.reclaim_pct < Decimal("0.25"):
        blockers.append("reclaim_too_weak")
    if ctx.funding_rate_hourly_pct > Decimal("0.02"):
        blockers.append("crowded_positive_funding")

    expected_move_pct = max(Decimal("2.20"), ctx.atr_pct * Decimal("2.20"))
    cost = CostModel(min_slippage_pct=ctx.expected_slippage_pct).estimate(
        TradeCostInput(
            notional_usd=Decimal("25"),
            expected_move_pct=expected_move_pct,
            half_spread_pct=ctx.spread_pct / Decimal("2"),
            depth_penalty_pct=ctx.expected_slippage_pct,
            hold_hours=Decimal("24"),
            funding_rate_hourly_pct=ctx.funding_rate_hourly_pct,
        )
    )
    if expected_move_pct < cost.roundtrip_cost_pct * Decimal("5"):
        blockers.append("swing_edge_below_5x_cost")
    return blockers


def build_swing_trend_retest_intent(ctx: SwingRetestContext, *, client_order_id: str) -> OrderIntent | None:
    if swing_retest_blockers(ctx):
        return None
    stop_distance_pct = max(Decimal("1.50"), ctx.atr_pct * Decimal("1.10"))
    risk_pct = Decimal("0.20")
    risk_usd = ctx.wallet_equity_usdc * risk_pct / Decimal("100")
    notional = min(ctx.wallet_equity_usdc * Decimal("0.20"), risk_usd / (stop_distance_pct / Decimal("100")))
    if notional < Decimal("10"):
        return None
    stop_loss = min(ctx.retest_low * Decimal("0.995"), ctx.current_price * (Decimal("1") - stop_distance_pct / Decimal("100")))
    size = _size_for_notional(notional, ctx.current_price)
    if size <= 0:
        return None
    return OrderIntent(
        strategy_id=STRATEGY_ID,
        symbol=ctx.symbol,
        coin=ctx.coin.upper(),
        side="buy",
        reduce_only=False,
        order_type="market",
        tif="Ioc",
        size=size,
        price=None,
        trigger_price=None,
        stop_loss=stop_loss.quantize(Decimal("0.00000001"), rounding=ROUND_DOWN),
        take_profit=None,
        client_order_id=client_order_id,
        reason="swing_trend_retest_reclaim",
        risk_usd=risk_usd,
        estimated_notional_usd=notional,
    )
