"""
Odds conversion and market math utilities.

Pure functions — no API calls, no dependencies beyond stdlib.

    from altsportsdata.odds import decimal_to_american, to_probability, remove_vig

    decimal_to_american(2.50)   # → +150
    to_probability(2.50)        # → 40.0
    remove_vig([52.4, 37.6, 25.0])  # → sums to 100.0
"""

from typing import List


def decimal_to_american(odds: float) -> int:
    """
    Convert decimal odds to American format.

    Args:
        odds: Decimal odds (must be > 1.0)

    Returns:
        American odds as int (+150, -200, etc.)

    Examples::

        decimal_to_american(2.50)  # → +150
        decimal_to_american(1.50)  # → -200
    """
    if odds < 1.0:
        raise ValueError(f"Decimal odds must be >= 1.0, got {odds}")
    if odds == 1.0:
        return -10000
    if odds >= 2.0:
        return int(round((odds - 1) * 100))
    return int(round(-100 / (odds - 1)))


def american_to_decimal(odds: int) -> float:
    """
    Convert American odds to decimal format.

    Args:
        odds: American odds (+150, -200, etc.)

    Returns:
        Decimal odds as float

    Examples::

        american_to_decimal(150)   # → 2.50
        american_to_decimal(-200)  # → 1.50
    """
    if odds == 0:
        raise ValueError("American odds cannot be 0")
    if odds > 0:
        return round(1 + odds / 100, 4)
    return round(1 + 100 / abs(odds), 4)


def to_probability(decimal_odds: float) -> float:
    """
    Convert decimal odds to implied probability percentage.

    Args:
        decimal_odds: Decimal odds (must be > 0)

    Returns:
        Implied probability as percentage (0-100)

    Examples::

        to_probability(2.50)  # → 40.0
        to_probability(1.50)  # → 66.67
    """
    if decimal_odds <= 0:
        raise ValueError(f"Decimal odds must be > 0, got {decimal_odds}")
    return round(100 / decimal_odds, 2)


def to_decimal(probability: float) -> float:
    """
    Convert implied probability percentage to decimal odds.

    Args:
        probability: Probability as percentage (0-100, exclusive)

    Returns:
        Decimal odds

    Examples::

        to_decimal(40.0)   # → 2.50
        to_decimal(66.67)  # → 1.50
    """
    if probability <= 0 or probability >= 100:
        raise ValueError(f"Probability must be between 0 and 100 (exclusive), got {probability}")
    return round(100 / probability, 4)


def remove_vig(probabilities: List[float]) -> List[float]:
    """
    Remove vig (overround) from a list of probabilities, normalizing to sum to 100%.

    Args:
        probabilities: Raw implied probabilities (typically sum > 100%)

    Returns:
        Fair probabilities summing to 100.0

    Examples::

        remove_vig([52.4, 37.6, 25.0])  # → [45.57, 32.70, 21.74]
    """
    if not probabilities:
        return []
    total = sum(probabilities)
    if total == 0:
        raise ValueError("Probabilities must not all be zero")
    return [round(p * 100 / total, 2) for p in probabilities]


def margin(probabilities: List[float]) -> float:
    """
    Calculate the market overround (vig/margin) as a percentage.

    A fair market sums to 100%. The margin is how much over 100% the raw
    probabilities sum to.

    Args:
        probabilities: Raw implied probabilities

    Returns:
        Overround percentage (e.g. 14.8 means 114.8% total)

    Examples::

        margin([52.4, 37.6, 25.0])  # → 15.0
    """
    if not probabilities:
        return 0.0
    return round(sum(probabilities) - 100, 2)
