"""edge_sizer.py — Position sizing based on strategy×regime historical edge.

Decouples lot size from single-signal confidence:
  - Confidence → gate only (threshold check to open)
  - Lot size → f(strategy historical win_rate in current regime)

This ensures winning and losing trades have symmetric sizing within each
strategy×regime pair, eliminating the "win small, lose big" asymmetry.

Sizing tiers:
  proven   (wr >= 0.55, n >= 20) → full lot
  positive (wr >= 0.50, n >= 10) → standard lot
  marginal (wr >= 0.40, n >= 5)  → reduced lot
  cold     (insufficient data)     → minimum lot (conservative)

Usage::

    from src.learning.edge_sizer import get_strategy_regime_lot
    lots = get_strategy_regime_lot("ema_macd", "TRENDING")
"""

from __future__ import annotations

from typing import Any

import config_manager


# ── Sizing tiers ───────────────────────────────────────────────────

FULL_LOT = 0.03      # proven edge
STANDARD_LOT = 0.02  # positive edge
REDUCED_LOT = 0.01   # marginal edge
MINIMUM_LOT = 0.01   # cold start / insufficient data

# ── Default lot from config ────────────────────────────────────────

def _default_lot() -> float:
    try:
        v = config_manager.get_effective_param("max_position_size", 0.03)
        return min(0.05, max(0.01, float(v or 0.03)))
    except Exception:
        return 0.03


# ── Main API ───────────────────────────────────────────────────────

def get_strategy_regime_lot(
    strategy_id: str = "",
    regime: str = "UNKNOWN",
    *,
    default_lot: float | None = None,
) -> float:
    """Return the recommended lot size based on strategy×regime historical edge.

    Args:
        strategy_id: e.g., "ema_macd", "rsi_extreme"
        regime: e.g., "TRENDING", "RANGING"
        default_lot: Override the default lot (uses config if None).

    Returns lot size, always in [MINIMUM_LOT, FULL_LOT].
    """
    base = default_lot if default_lot is not None else _default_lot()
    sid = str(strategy_id or "").strip()
    reg = str(regime or "UNKNOWN").strip().upper()

    # Get per-regime win rate from strategy_intelligence
    wr, n = _get_regime_win_rate(sid, reg)

    if n >= 20 and wr >= 0.55:
        return FULL_LOT
    elif n >= 10 and wr >= 0.50:
        return STANDARD_LOT
    elif n >= 5 and wr >= 0.40:
        return REDUCED_LOT
    elif n >= 5 and wr < 0.40:
        # Known bad — minimum lot or skip (caller decides)
        return MINIMUM_LOT
    else:
        # Insufficient data — conservative default
        return min(STANDARD_LOT, base)

    return base


def get_sizing_context(strategy_id: str = "", regime: str = "") -> dict[str, Any]:
    """Return full sizing context for logging and decision transparency."""
    sid = str(strategy_id or "").strip()
    reg = str(regime or "UNKNOWN").strip().upper()
    wr, n = _get_regime_win_rate(sid, reg)
    lots = get_strategy_regime_lot(sid, reg)

    return {
        "strategy_id": sid,
        "regime": reg,
        "win_rate": wr,
        "sample_size": n,
        "recommended_lots": lots,
        "tier": _tier_name(wr, n),
    }


# ── Internal ───────────────────────────────────────────────────────

def _get_regime_win_rate(strategy_id: str, regime: str) -> tuple[float, int]:
    """Query strategy_intelligence for per-regime win rate."""
    try:
        from src.strategy_intelligence import get_intelligence_hub
        hub = get_intelligence_hub()
        # Try to find tracker by strategy_id
        for instance_id, tracker in hub._trackers.items():
            if tracker.strategy_id == strategy_id:
                rf = tracker.regime_fitness.get(regime)
                if rf is not None and rf.total >= 3:
                    return rf.win_rate, rf.total
                # Fall back to overall win rate
                return tracker._win_rate_all, tracker._total_trades
    except Exception:
        pass

    # Fallback: query trading_brain
    try:
        from src.learning.trading_brain import get_brain
        brain = get_brain()
        stats = brain.query_win_rate(strategy_id=strategy_id, regime=regime)
        if stats.get("n", 0) >= 3:
            return stats["win_rate"], stats["n"]
        # Try overall for this strategy
        overall = brain.query_win_rate(strategy_id=strategy_id)
        if overall.get("n", 0) >= 3:
            return overall["win_rate"], overall["n"]
    except Exception:
        pass

    return 0.0, 0


def _tier_name(wr: float, n: int) -> str:
    if n >= 20 and wr >= 0.55:
        return "proven"
    elif n >= 10 and wr >= 0.50:
        return "positive"
    elif n >= 5 and wr >= 0.40:
        return "marginal"
    elif n >= 5:
        return "cold_negative"
    else:
        return "cold_start"
