/**
 * weather.ts — Weather data provider for HCW (Home Crowd + Weather) insight.
 *
 * Uses Open-Meteo API (free, no API key) for forecast weather data.
 * Provides current + game-time conditions for outdoor venues.
 */

export interface WeatherData {
  temperature_f: number;
  wind_mph: number;
  wind_direction: string;
  humidity: number;
  precipitation_chance: number;
  conditions: string;           // "Clear", "Partly Cloudy", "Rain", etc.
  feels_like_f: number;
  visibility_miles: number;
}

export interface GameWeather {
  current: WeatherData;
  gameTime: WeatherData | null;  // null if game hasn't started yet / no hourly data
  alerts: string[];              // "High winds expected", "Rain likely", etc.
  source: string;
}

// WMO weather code to condition string
const WMO_CODES: Record<number, string> = {
  0: 'Clear',
  1: 'Mostly Clear',
  2: 'Partly Cloudy',
  3: 'Overcast',
  45: 'Fog',
  48: 'Depositing Rime Fog',
  51: 'Light Drizzle',
  53: 'Moderate Drizzle',
  55: 'Dense Drizzle',
  56: 'Light Freezing Drizzle',
  57: 'Dense Freezing Drizzle',
  61: 'Slight Rain',
  63: 'Moderate Rain',
  65: 'Heavy Rain',
  66: 'Light Freezing Rain',
  67: 'Heavy Freezing Rain',
  71: 'Slight Snow',
  73: 'Moderate Snow',
  75: 'Heavy Snow',
  77: 'Snow Grains',
  80: 'Slight Rain Showers',
  81: 'Moderate Rain Showers',
  82: 'Violent Rain Showers',
  85: 'Slight Snow Showers',
  86: 'Heavy Snow Showers',
  95: 'Thunderstorm',
  96: 'Thunderstorm w/ Slight Hail',
  99: 'Thunderstorm w/ Heavy Hail',
};

// Wind direction from degrees
function windDirection(deg: number): string {
  const dirs = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'];
  return dirs[Math.round(deg / 22.5) % 16];
}

// Celsius to Fahrenheit
function cToF(c: number): number {
  return Math.round(c * 9 / 5 + 32);
}

// km/h to mph
function kmhToMph(kmh: number): number {
  return Math.round(kmh * 0.621);
}

/**
 * Fetch weather data for a venue location + game time.
 * Uses Open-Meteo forecast API with hourly data.
 */
export async function fetchGameWeather(lat: number, lon: number, gameTimeIso?: string): Promise<GameWeather> {
  try {
    const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code,wind_speed_10m,wind_direction_10m,visibility&hourly=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation_probability,weather_code,wind_speed_10m,wind_direction_10m&temperature_unit=celsius&wind_speed_unit=kmh&timezone=auto&forecast_days=2`;

    const res = await fetch(url, { signal: AbortSignal.timeout(10000) });
    if (!res.ok) throw new Error(`Open-Meteo ${res.status}`);
    const data = await res.json() as any;

    // Parse current conditions
    const current: WeatherData = {
      temperature_f: cToF(data.current.temperature_2m),
      wind_mph: kmhToMph(data.current.wind_speed_10m),
      wind_direction: windDirection(data.current.wind_direction_10m),
      humidity: data.current.relative_humidity_2m,
      precipitation_chance: 0,
      conditions: WMO_CODES[data.current.weather_code] || 'Unknown',
      feels_like_f: cToF(data.current.apparent_temperature),
      visibility_miles: Math.round((data.current.visibility || 10000) / 1609),
    };

    // Find game-time hourly data
    let gameTime: WeatherData | null = null;
    if (gameTimeIso && data.hourly?.time) {
      const gameHour = new Date(gameTimeIso).toISOString().slice(0, 13);
      const hourIdx = data.hourly.time.findIndex((t: string) => t.startsWith(gameHour));
      if (hourIdx >= 0) {
        gameTime = {
          temperature_f: cToF(data.hourly.temperature_2m[hourIdx]),
          wind_mph: kmhToMph(data.hourly.wind_speed_10m[hourIdx]),
          wind_direction: windDirection(data.hourly.wind_direction_10m[hourIdx]),
          humidity: data.hourly.relative_humidity_2m[hourIdx],
          precipitation_chance: data.hourly.precipitation_probability?.[hourIdx] || 0,
          conditions: WMO_CODES[data.hourly.weather_code[hourIdx]] || 'Unknown',
          feels_like_f: cToF(data.hourly.apparent_temperature[hourIdx]),
          visibility_miles: 10,
        };
      }
    }

    // Generate alerts
    const alerts: string[] = [];
    const wx = gameTime || current;
    if (wx.wind_mph >= 20) alerts.push(`High winds: ${wx.wind_mph} mph ${wx.wind_direction}`);
    if (wx.precipitation_chance >= 60) alerts.push(`Rain likely: ${wx.precipitation_chance}% chance`);
    if (wx.temperature_f <= 32) alerts.push(`Freezing conditions: ${wx.temperature_f}°F`);
    if (wx.temperature_f >= 95) alerts.push(`Extreme heat: ${wx.temperature_f}°F`);
    if (wx.conditions.includes('Snow')) alerts.push(`Snow expected`);
    if (wx.conditions.includes('Thunderstorm')) alerts.push(`Thunderstorms possible`);
    if (wx.visibility_miles < 3) alerts.push(`Low visibility: ${wx.visibility_miles} mi`);

    return { current, gameTime, alerts, source: 'open-meteo' };
  } catch (err) {
    console.error('[weather] Open-Meteo fetch error:', err);
    return getFallbackWeather();
  }
}

/** Fallback weather when API is unavailable */
function getFallbackWeather(): GameWeather {
  return {
    current: {
      temperature_f: 72,
      wind_mph: 5,
      wind_direction: 'W',
      humidity: 50,
      precipitation_chance: 0,
      conditions: 'Clear',
      feels_like_f: 72,
      visibility_miles: 10,
    },
    gameTime: null,
    alerts: [],
    source: 'fallback',
  };
}
