"""confidence_calibrator.py — Adjust signal confidence based on factor scores.

Phase 2: Queries the factor leaderboard and adjusts signal confidence
up or down based on how many profit-driving vs drag factors are present.

Usage::

    from src.learning.confidence_calibrator import calibrate_confidence
    adjusted = calibrate_confidence(signal_context)
"""

from __future__ import annotations

from typing import Any


# Per-factor adjustment magnitudes (top-5 get boost, bottom-5 get penalty)
_BOOST_PER_FACTOR = 0.03
_PENALTY_PER_FACTOR = 0.03
_MAX_ADJUSTMENT = 0.15
_MIN_CONFIDENCE = 0.30
_MAX_CONFIDENCE = 0.95


def calibrate_confidence(
    raw_confidence: float,
    signal_context: dict[str, Any] | None = None,
    *,
    top_factors: list[str] | None = None,
    bottom_factors: list[str] | None = None,
) -> float:
    """Calibrate signal confidence based on factor leaderboard.

    Args:
        raw_confidence: Original signal confidence (0.0-1.0).
        signal_context: Dict with feature values of the current signal
            (e.g., {"agent_aligned": 1, "regime": "TRENDING", "adx_value": 28.5}).
        top_factors: Pre-computed list of top-5 profit factor names.
        bottom_factors: Pre-computed list of bottom-5 drag factor names.

    Returns adjusted confidence in [MIN_CONFIDENCE, MAX_CONFIDENCE].
    """
    ctx = signal_context or {}

    # If no factor data available, return raw confidence
    if top_factors is None and bottom_factors is None:
        try:
            from src.learning.factor_scorer import score_all_factors
            factors = score_all_factors()
            top_factors = [f["feature"] for f in factors[:5]]
            bottom_factors = [f["feature"] for f in factors[-5:]]
        except Exception:
            return raw_confidence

    top = top_factors or []
    bottom = bottom_factors or []

    adjustment = 0.0

    # Boost for each top factor present with favorable value
    for feature in top:
        if feature in ctx:
            val = ctx[feature]
            # For categorical: being in the "good" group
            # For numeric: being in the high bucket
            # Simplified: just check presence
            adjustment += _BOOST_PER_FACTOR

    # Penalty for each bottom factor present
    for feature in bottom:
        if feature in ctx:
            adjustment -= _PENALTY_PER_FACTOR

    # Clamp total adjustment
    adjustment = max(-_MAX_ADJUSTMENT, min(_MAX_ADJUSTMENT, adjustment))

    calibrated = raw_confidence + adjustment
    return max(_MIN_CONFIDENCE, min(_MAX_CONFIDENCE, calibrated))


def get_factor_adjustment_report(
    signal_context: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Generate a factor adjustment report for display/logging.

    Returns:
        {"top_matches": [...], "bottom_matches": [...], "adjustment": float,
         "raw_confidence": float, "calibrated_confidence": float}
    """
    try:
        from src.learning.factor_scorer import score_all_factors
        factors = score_all_factors()
        if not factors:
            return {"error": "no factor data"}

        top = factors[:5]
        bottom = factors[-5:]
        ctx = signal_context or {}

        top_matches = [f["feature"] for f in top if f["feature"] in ctx]
        bottom_matches = [f["feature"] for f in bottom if f["feature"] in ctx]

        adjustment = len(top_matches) * _BOOST_PER_FACTOR - len(bottom_matches) * _PENALTY_PER_FACTOR
        adjustment = max(-_MAX_ADJUSTMENT, min(_MAX_ADJUSTMENT, adjustment))

        return {
            "top_matches": top_matches,
            "bottom_matches": bottom_matches,
            "adjustment": round(adjustment, 4),
            "top_factor_names": [f["feature"] for f in top],
            "bottom_factor_names": [f["feature"] for f in bottom],
        }
    except Exception:
        return {"error": "calibration failed"}
