'''Shared production-style historical ranking helpers for research runners.'''

from __future__ import annotations

from datetime import date


def _period_percentiles(
    observations: list[dict], value_key: str
) -> dict[tuple[str, str], float]:
    '''Rank one deterministic ticker observation per historical period.'''
    by_period: dict[tuple, list[dict]] = {}
    seen: set[tuple[str, str]] = set()
    for row in observations:
        identity = (str(row['symbol']), str(row['date']))
        if identity in seen:
            raise ValueError(f'Duplicate universe rank observation: {identity}')
        seen.add(identity)
        if row.get(value_key) is None:
            continue
        period = tuple(row['ranking_period'])
        by_period.setdefault(period, []).append(row)

    result: dict[tuple[str, str], float] = {}
    for group in by_period.values():
        ordered = sorted(
            group,
            key=lambda row: (float(row[value_key]), str(row['symbol'])),
        )
        denominator = len(ordered) - 1
        for rank, row in enumerate(ordered):
            result[(str(row['symbol']), str(row['date']))] = round(
                rank / denominator * 100.0 if denominator > 0 else 100.0,
                2,
            )
    return result


def _live_universe_rank_map(
    observations: list[dict],
    benchmark_closes: dict[date, float],
    momentum_weight: float,
) -> dict[tuple[str, str], dict[str, float | None]]:
    '''Historical equivalent of production compute_activation_ranks.

    Every ticker contributes at most once per session. Residual momentum starts
    only once 252 benchmark closes were point-in-time available; earlier dates
    use the same raw-momentum fallback as production.
    '''
    identities = [(str(row['symbol']), str(row['date'])) for row in observations]
    if len(identities) != len(set(identities)):
        raise ValueError('Universe ranking requires one observation per ticker/date')

    raw_pct = _period_percentiles(observations, 'momentum')
    residual_pct = _period_percentiles(observations, 'residual_momentum')
    vol_pct = _period_percentiles(observations, 'vol_6m')
    benchmark_ords = sorted(value.toordinal() for value in benchmark_closes)
    residual_start_ord = benchmark_ords[251] if len(benchmark_ords) >= 252 else None

    ranks: dict[tuple[str, str], dict[str, float | None]] = {}
    for row in observations:
        identity = (str(row['symbol']), str(row['date']))
        asof_ord = date.fromisoformat(identity[1]).toordinal()
        momentum_pct = (
            residual_pct.get(identity)
            if residual_start_ord is not None and asof_ord >= residual_start_ord
            else raw_pct.get(identity)
        )
        volatility_pct = vol_pct.get(identity)
        strategy_rank = (
            round(
                momentum_pct * momentum_weight
                + volatility_pct * (1.0 - momentum_weight),
                2,
            )
            if momentum_pct is not None and volatility_pct is not None
            else momentum_pct
        )
        ranks[identity] = {
            'momentum_percentile': momentum_pct,
            'volatility_percentile': volatility_pct,
            'strategy_rank': strategy_rank,
        }
    return ranks
