"""
VYNDR Weather Monitoring
Continuous weather monitoring via Open-Meteo (free, no API key).
Includes dome detection, ball carry factor, and regrade triggers.
"""

import logging
import requests

from utils.data_warehouse import fetch_with_cache
from utils.retry import api_call_with_retry

logger = logging.getLogger('vyndr')

OPEN_METEO_URL = 'https://api.open-meteo.com/v1/forecast'

WEATHER_MONITORING = {
    'initial_pull': 'at_lineup_confirmation',
    'refresh_interval_minutes': 30,
    'stop_at': 'first_pitch',
    'regrade_triggers': {
        'temperature_change_f': 5,
        'wind_speed_change_mph': 5,
        'rain_probability_threshold': 0.50,
        'humidity_change_pct': 15
    }
}

# Loaded from park_factors.json at boot
PARK_COORDINATES = {}


def load_park_coordinates(park_data):
    """
    Load park coordinates from park_factors.json data.

    Args:
        park_data: Dict loaded from park_factors.json.
    """
    global PARK_COORDINATES
    if isinstance(park_data, dict):
        PARK_COORDINATES = park_data
    elif isinstance(park_data, list):
        PARK_COORDINATES = {p['park_id']: p for p in park_data}


def get_game_weather(park_id, game_date, game_time):
    """
    Get weather conditions for a game. Skips API call for dome/retractable-closed parks.

    Args:
        park_id: MLB park identifier.
        game_date: Game date string (YYYY-MM-DD).
        game_time: Game time string (HH:MM).

    Returns:
        Dict with temperature_f, wind_speed_mph, wind_direction, humidity_pct,
        ball_carry_factor, impact_on_hr, impact_on_scoring, dome_game.
    """
    park = PARK_COORDINATES.get(park_id, {})

    # Dome detection — skip weather for closed/dome parks
    roof = park.get('roof_status', 'open')
    if roof in ('dome', 'retractable_closed'):
        return {
            'temperature_f': 72, 'wind_speed_mph': 0, 'wind_direction': 'none',
            'humidity_pct': 50, 'ball_carry_factor': 1.0,
            'impact_on_hr': 'neutral', 'impact_on_scoring': 'neutral',
            'dome_game': True
        }

    def _fetch():
        params = {
            'latitude': park.get('lat', 40.0),
            'longitude': park.get('lng', -74.0),
            'hourly': 'temperature_2m,windspeed_10m,winddirection_10m,relativehumidity_2m',
            'temperature_unit': 'fahrenheit',
            'windspeed_unit': 'mph',
            'timezone': park.get('timezone', 'America/New_York')
        }
        response = requests.get(OPEN_METEO_URL, params=params, timeout=10)
        response.raise_for_status()
        return response.json()

    weather = fetch_with_cache(
        f'weather_{park_id}_{game_date}_{game_time}',
        _fetch,
        data_type='weather',
        has_game_today=True
    )

    if weather is None:
        return {
            'temperature_f': 72, 'wind_speed_mph': 5, 'wind_direction': 'unknown',
            'humidity_pct': 50, 'ball_carry_factor': 1.0,
            'impact_on_hr': 'neutral', 'impact_on_scoring': 'neutral',
            'dome_game': False, '_fallback': True
        }

    game_hour_data = extract_hour_data(weather, game_time)
    carry = calculate_ball_carry(game_hour_data)

    return {
        'temperature_f': game_hour_data.get('temp', 72),
        'wind_speed_mph': game_hour_data.get('wind_speed', 5),
        'wind_direction': game_hour_data.get('wind_dir', 'unknown'),
        'humidity_pct': game_hour_data.get('humidity', 50),
        'ball_carry_factor': carry,
        'impact_on_hr': classify_hr_impact(game_hour_data, park_id),
        'impact_on_scoring': classify_scoring_impact(game_hour_data),
        'dome_game': False
    }


def check_weather_for_regrade(park_id, game_date, game_time, previous_weather):
    """
    Check if weather changed enough to trigger re-grade. Called every 30min.

    Args:
        park_id: MLB park identifier.
        game_date: Game date string.
        game_time: Game time string.
        previous_weather: Previous weather data dict.

    Returns:
        Dict with needs_regrade (bool), current_weather, and changes list.
    """
    current = get_game_weather(park_id, game_date, game_time)
    if current.get('dome_game'):
        return {'needs_regrade': False}

    triggers = WEATHER_MONITORING['regrade_triggers']
    needs_regrade = False
    changes = []

    temp_diff = abs(current['temperature_f'] - previous_weather.get('temperature_f', 72))
    if temp_diff >= triggers['temperature_change_f']:
        needs_regrade = True
        changes.append(
            f"Temp: {previous_weather.get('temperature_f', '?')}"
            f"→{current['temperature_f']}°F"
        )

    wind_diff = abs(current['wind_speed_mph'] - previous_weather.get('wind_speed_mph', 5))
    if wind_diff >= triggers['wind_speed_change_mph']:
        needs_regrade = True
        changes.append(
            f"Wind: {previous_weather.get('wind_speed_mph', '?')}"
            f"→{current['wind_speed_mph']}mph"
        )

    return {
        'needs_regrade': needs_regrade,
        'current_weather': current,
        'changes': changes
    }


def extract_hour_data(weather_data, game_time):
    """
    Extract weather data for the specific game hour from Open-Meteo response.

    Args:
        weather_data: Full Open-Meteo API response dict.
        game_time: Game time string (HH:MM).

    Returns:
        Dict with temp, wind_speed, wind_dir, humidity for the game hour.
    """
    hourly = weather_data.get('hourly', {})
    times = hourly.get('time', [])

    # Find closest hour
    target_hour = int(game_time.split(':')[0]) if ':' in str(game_time) else 19
    best_idx = 0
    for i, t in enumerate(times):
        if str(target_hour).zfill(2) in str(t):
            best_idx = i
            break

    temps = hourly.get('temperature_2m', [])
    winds = hourly.get('windspeed_10m', [])
    wind_dirs = hourly.get('winddirection_10m', [])
    humidity = hourly.get('relativehumidity_2m', [])

    return {
        'temp': temps[best_idx] if best_idx < len(temps) else 72,
        'wind_speed': winds[best_idx] if best_idx < len(winds) else 5,
        'wind_dir': wind_dirs[best_idx] if best_idx < len(wind_dirs) else 0,
        'humidity': humidity[best_idx] if best_idx < len(humidity) else 50
    }


def calculate_ball_carry(weather):
    """
    Calculate ball carry factor based on temperature and humidity.

    Args:
        weather: Dict with 'temp' and 'humidity' keys.

    Returns:
        Float ball carry factor (1.0 = neutral).
    """
    temp = weather.get('temp', 72)
    humidity = weather.get('humidity', 50)
    temp_factor = 1 + (temp - 72) * 0.002
    humidity_factor = 1 - (humidity - 50) * 0.001
    return round(temp_factor * humidity_factor, 3)


def classify_hr_impact(weather, park_id):
    """Classify HR impact based on weather conditions."""
    carry = calculate_ball_carry(weather)
    wind = weather.get('wind_speed', 0)
    if carry > 1.02 and wind < 10:
        return 'favorable'
    elif carry < 0.98 or wind > 15:
        return 'unfavorable'
    return 'neutral'


def classify_scoring_impact(weather):
    """Classify overall scoring impact based on weather conditions."""
    temp = weather.get('temp', 72)
    wind = weather.get('wind_speed', 0)
    if temp > 85 and wind < 10:
        return 'elevated'
    elif temp < 50 or wind > 15:
        return 'depressed'
    return 'neutral'


def check_all_games_weather_regrade():
    """
    Check weather for all today's MLB games and trigger regrade if needed.
    Called by weather monitoring GitHub Actions cron every 30min.
    """
    logger.info('[VYNDR] Checking weather for all games')
    # In production: iterate today's MLB games from schedule,
    # call check_weather_for_regrade for each open-air park
