"""Tests for weather system."""

import random

from mudlib.weather import (
    WeatherCondition,
    WeatherState,
    advance_weather,
    get_weather_description,
)


def test_weather_condition_enum():
    assert WeatherCondition.clear.value == "clear"
    assert WeatherCondition.cloudy.value == "cloudy"
    assert WeatherCondition.rain.value == "rain"
    assert WeatherCondition.storm.value == "storm"
    assert WeatherCondition.snow.value == "snow"
    assert WeatherCondition.fog.value == "fog"


def test_weather_state_dataclass():
    state = WeatherState(condition=WeatherCondition.rain, intensity=0.5)
    assert state.condition == WeatherCondition.rain
    assert state.intensity == 0.5


def test_get_weather_description_returns_non_empty():
    state = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
    description = get_weather_description(state)
    assert isinstance(description, str)
    assert len(description) > 0


def test_get_weather_description_varies_by_condition():
    clear = get_weather_description(
        WeatherState(condition=WeatherCondition.clear, intensity=0.5)
    )
    rain = get_weather_description(
        WeatherState(condition=WeatherCondition.rain, intensity=0.5)
    )
    snow = get_weather_description(
        WeatherState(condition=WeatherCondition.snow, intensity=0.5)
    )

    # Different conditions should produce different descriptions
    assert clear != rain
    assert rain != snow
    assert clear != snow


def test_get_weather_description_varies_by_intensity():
    light_rain = get_weather_description(
        WeatherState(condition=WeatherCondition.rain, intensity=0.2)
    )
    heavy_rain = get_weather_description(
        WeatherState(condition=WeatherCondition.rain, intensity=0.9)
    )

    # Different intensities should produce different descriptions
    assert light_rain != heavy_rain


def test_advance_weather_returns_new_state():
    current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
    rng = random.Random(42)
    new_state = advance_weather(current, season="summer", rng=rng)

    assert isinstance(new_state, WeatherState)
    assert isinstance(new_state.condition, WeatherCondition)
    assert 0.0 <= new_state.intensity <= 1.0


def test_advance_weather_is_deterministic_with_seed():
    current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)

    rng1 = random.Random(42)
    state1 = advance_weather(current, season="summer", rng=rng1)

    rng2 = random.Random(42)
    state2 = advance_weather(current, season="summer", rng=rng2)

    assert state1.condition == state2.condition
    assert state1.intensity == state2.intensity


def test_advance_weather_transitions_naturally():
    # Clear can become cloudy
    rng = random.Random(42)
    for _ in range(100):
        current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
        new_state = advance_weather(current, season="summer", rng=rng)
        if new_state.condition == WeatherCondition.cloudy:
            break
    else:
        raise AssertionError("clear never transitioned to cloudy in 100 iterations")

    # Cloudy can become rain or clear
    rng = random.Random(43)
    found_rain = False
    found_clear = False
    for _ in range(100):
        current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
        new_state = advance_weather(current, season="summer", rng=rng)
        if new_state.condition == WeatherCondition.rain:
            found_rain = True
        if new_state.condition == WeatherCondition.clear:
            found_clear = True
        if found_rain and found_clear:
            break
    assert found_rain or found_clear


def test_storm_transitions_to_rain_or_cloudy():
    # Storm should always transition away (doesn't last)
    rng = random.Random(44)
    found_non_storm = False
    for _ in range(50):
        current = WeatherState(condition=WeatherCondition.storm, intensity=0.8)
        new_state = advance_weather(current, season="summer", rng=rng)
        if new_state.condition in (WeatherCondition.rain, WeatherCondition.cloudy):
            found_non_storm = True
            break
    assert found_non_storm, "storm should transition to rain or cloudy"


def test_snow_only_in_winter_autumn():
    # Snow in winter
    rng = random.Random(45)
    found_snow = False
    for _ in range(200):
        current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
        new_state = advance_weather(current, season="winter", rng=rng)
        if new_state.condition == WeatherCondition.snow:
            found_snow = True
            break
    assert found_snow, "snow should be possible in winter"

    # Snow should be rare or impossible in summer
    rng = random.Random(46)
    for _ in range(100):
        current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
        new_state = advance_weather(current, season="summer", rng=rng)
        # Should not produce snow in summer
        assert new_state.condition != WeatherCondition.snow


def test_climate_temperate_default():
    current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
    rng = random.Random(47)

    # Should work without climate parameter (defaults to temperate)
    new_state = advance_weather(current, season="summer", rng=rng)
    assert isinstance(new_state, WeatherState)


def test_climate_arid_favors_clear():
    # Arid should heavily favor clear weather
    rng = random.Random(48)
    clear_count = 0

    for _ in range(100):
        current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
        new_state = advance_weather(current, season="summer", rng=rng, climate="arid")
        if new_state.condition == WeatherCondition.clear:
            clear_count += 1

    # Arid should stay clear most of the time
    assert clear_count > 70, f"arid should favor clear, got {clear_count}/100"


def test_climate_arid_no_snow():
    # Arid should never produce snow
    rng = random.Random(49)
    for _ in range(100):
        current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
        new_state = advance_weather(current, season="winter", rng=rng, climate="arid")
        assert new_state.condition != WeatherCondition.snow


def test_climate_arctic_favors_snow_fog_cloudy():
    # Arctic should produce snow, fog, or cloudy frequently
    rng = random.Random(50)
    arctic_conditions = 0

    for _ in range(100):
        current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
        new_state = advance_weather(current, season="winter", rng=rng, climate="arctic")
        if new_state.condition in (
            WeatherCondition.snow,
            WeatherCondition.fog,
            WeatherCondition.cloudy,
        ):
            arctic_conditions += 1

    # Arctic should heavily favor snow/fog/cloudy
    assert arctic_conditions > 70, (
        f"arctic should favor snow/fog/cloudy, got {arctic_conditions}/100"
    )


def test_advance_weather_accepts_all_seasons():
    current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
    rng = random.Random(51)

    for season in ["spring", "summer", "autumn", "winter"]:
        new_state = advance_weather(current, season=season, rng=rng)
        assert isinstance(new_state, WeatherState)


def test_get_weather_ambience_rain():
    from mudlib.weather import get_weather_ambience

    messages = get_weather_ambience(WeatherCondition.rain)
    assert isinstance(messages, list)
    assert len(messages) > 0
    assert all(isinstance(msg, str) for msg in messages)


def test_get_weather_ambience_storm():
    from mudlib.weather import get_weather_ambience

    messages = get_weather_ambience(WeatherCondition.storm)
    assert isinstance(messages, list)
    assert len(messages) > 0
    assert all(isinstance(msg, str) for msg in messages)


def test_get_weather_ambience_snow():
    from mudlib.weather import get_weather_ambience

    messages = get_weather_ambience(WeatherCondition.snow)
    assert isinstance(messages, list)
    assert len(messages) > 0
    assert all(isinstance(msg, str) for msg in messages)


def test_get_weather_ambience_fog():
    from mudlib.weather import get_weather_ambience

    messages = get_weather_ambience(WeatherCondition.fog)
    assert isinstance(messages, list)
    assert len(messages) > 0
    assert all(isinstance(msg, str) for msg in messages)


def test_get_weather_ambience_clear():
    from mudlib.weather import get_weather_ambience

    messages = get_weather_ambience(WeatherCondition.clear)
    assert isinstance(messages, list)
    assert len(messages) == 0


def test_get_weather_ambience_cloudy():
    from mudlib.weather import get_weather_ambience

    messages = get_weather_ambience(WeatherCondition.cloudy)
    assert isinstance(messages, list)
    assert len(messages) == 0
