"""Helpers for shadow-only non-core allocator v2 diagnostics."""
from __future__ import annotations

import math


def compute_native_rank_pct(rank_index: int, total: int) -> float:
    if total <= 1:
        return 1.0
    clamped_index = max(0, min(rank_index, total - 1))
    return max(0.0, 1.0 - (clamped_index / float(total - 1)))


def normalize_hold_days_est(hold_days_est: int | float | None) -> float:
    hold_days = max(0.0, float(hold_days_est or 0.0))
    return min(hold_days, 30.0) / 30.0


def normalize_liquidity_penalty(
    requested_cash_est: int | float | None,
    avg_dollar_volume: int | float | None,
) -> float:
    requested_cash = max(0.0, float(requested_cash_est or 0.0))
    adv = max(0.0, float(avg_dollar_volume or 0.0))
    denom = max(adv * 0.02, 1.0)
    return min(requested_cash / denom, 1.0)


def classify_parking_class(parking_symbol: str | None) -> str:
    normalized = str(parking_symbol or "").strip().lower()
    if normalized == "sgov":
        return "cash"
    if normalized == "gld":
        return "gold"
    if normalized in {"qqqm", "qqq", "tqqq", "spy"}:
        return "equity_beta"
    return "cash"


def classify_non_core_overlap_class(
    family: str,
    *,
    trade_symbol_mode: str | None = None,
    engine_id: str | None = None,
    symbol: str | None = None,
) -> str:
    normalized_family = str(family or "").strip().lower()
    normalized_engine = str(engine_id or "").strip().lower()
    normalized_symbol = str(symbol or "").strip().lower()
    normalized_mode = str(trade_symbol_mode or "").strip().lower()

    if normalized_family == "risk_off_alpha" and normalized_symbol == "gld":
        return "gold"
    if normalized_family == "idle_alpha" and (
        normalized_engine == "idle_macro_breadth_smh_postalloc"
        or normalized_mode == "sector_etf"
        or normalized_symbol == "smh"
    ):
        return "equity_beta_high"
    if normalized_family in {"idle_alpha", "form4", "ownership"}:
        return "equity_beta_med"
    if normalized_family == "risk_off_alpha":
        return "gold" if normalized_symbol == "gld" else "cash"
    return "cash"


def compute_overlap_penalty(candidate_class: str, parking_class: str) -> float:
    candidate = str(candidate_class or "").strip().lower()
    parking = str(parking_class or "").strip().lower()
    if parking == "cash" or candidate == "cash":
        return 0.0
    if candidate == "gold" and parking == "gold":
        return 1.0
    if candidate == "equity_beta_high" and parking == "equity_beta":
        return 1.0
    if candidate == "equity_beta_med" and parking == "equity_beta":
        return 0.6
    return 0.0


def normalize_parking_proxy(
    parking_symbol: str | None,
    *,
    parking_momentum_20: int | float | None,
    hold_days_est: int | float | None,
    sgov_annual_rate: int | float | None,
) -> float:
    normalized_symbol = str(parking_symbol or "").strip().lower()
    hold_days = max(0.0, float(hold_days_est or 0.0))
    if normalized_symbol == "sgov":
        annual_rate = max(0.0, float(sgov_annual_rate or 0.0))
        return min(((annual_rate * hold_days / 252.0) / 0.02), 1.0)

    momentum = max(0.0, float(parking_momentum_20 or 0.0))
    scaled_momentum = momentum * (min(hold_days, 20.0) / 20.0)
    return min(scaled_momentum / 0.10, 1.0)


def compute_marginal_score(
    *,
    native_rank_pct: int | float | None,
    hold_norm: int | float | None,
    liquidity_penalty_norm: int | float | None,
    overlap_penalty_norm: int | float | None,
    parking_proxy_norm: int | float | None,
    native_strength: int | float = 1.0,
    hold_penalty: int | float = 0.20,
    liquidity_penalty: int | float = 0.25,
    overlap_penalty: int | float = 0.20,
    parking_opportunity_penalty: int | float = 0.35,
) -> float:
    values = (
        float(native_rank_pct or 0.0),
        float(hold_norm or 0.0),
        float(liquidity_penalty_norm or 0.0),
        float(overlap_penalty_norm or 0.0),
        float(parking_proxy_norm or 0.0),
    )
    if any(math.isnan(value) or math.isinf(value) for value in values):
        return 0.0
    return (
        float(native_strength) * float(native_rank_pct or 0.0)
        - float(hold_penalty) * float(hold_norm or 0.0)
        - float(liquidity_penalty) * float(liquidity_penalty_norm or 0.0)
        - float(overlap_penalty) * float(overlap_penalty_norm or 0.0)
        - float(parking_opportunity_penalty) * float(parking_proxy_norm or 0.0)
    )
