"""
Feature band encoders
======================
Discrete bucketers for assignment-context features (distance, time-of-day, load,
order density). These are small pure helpers used by the Thompson-sampling
strategy bandit (`strategy_bandit.py`) and the /riderassign bandit context.

NOTE: The ID3 SUCCESS/RISK decision tree that used to live here has been retired —
it only ever produced response metadata and never affected any assignment. Only
the generic feature-band encoders remain.
"""

from datetime import datetime


def distance_band(km: float) -> str:
    """Total route distance -> discrete band."""
    if km <= 5.0:   return "SHORT"
    if km <= 15.0:  return "MID"
    if km <= 30.0:  return "LONG"
    return "VERY_LONG"


def time_band(ts_str: str) -> str:
    """ISO timestamp -> time-of-day band."""
    try:
        hour = datetime.fromisoformat(ts_str).hour
        if 6  <= hour < 10: return "MORNING_RUSH"
        if 10 <= hour < 12: return "LATE_MORNING"
        if 12 <= hour < 14: return "LUNCH_RUSH"
        if 14 <= hour < 17: return "AFTERNOON"
        if 17 <= hour < 20: return "EVENING_RUSH"
        if 20 <= hour < 23: return "NIGHT"
        return "LATE_NIGHT"
    except Exception:
        return "UNKNOWN"


def load_band(avg_load: float) -> str:
    """Average orders-per-rider -> load band."""
    if avg_load <= 2.0:  return "LIGHT"
    if avg_load <= 5.0:  return "MODERATE"
    if avg_load <= 8.0:  return "HEAVY"
    return "OVERLOADED"


def order_density_band(num_orders: int, num_riders: int) -> str:
    """Orders per available rider -> density band."""
    if num_riders == 0:
        return "NO_RIDERS"
    ratio = num_orders / num_riders
    if ratio <= 2.0:  return "SPARSE"
    if ratio <= 5.0:  return "NORMAL"
    if ratio <= 9.0:  return "DENSE"
    return "OVERLOADED"
