import { getCachedData, cacheSourceData } from './storageService';
import { fetchWithCors } from '../utils/corsProxy';
import { debugLog } from '../utils/constants';

// Weather code descriptions mapping (WMO Weather interpretation codes)
const WEATHER_CODE_MAP = {
  0: { description: 'Clear sky', icon: 'sunny' },
  1: { description: 'Mainly clear', icon: 'partly_cloudy' },
  2: { description: 'Partly cloudy', icon: 'partly_cloudy' },
  3: { description: 'Overcast', icon: 'cloudy' },
  45: { description: 'Foggy', icon: 'foggy' },
  48: { description: 'Depositing rime fog', icon: 'foggy' },
  51: { description: 'Light drizzle', icon: 'rainy' },
  53: { description: 'Moderate drizzle', icon: 'rainy' },
  55: { description: 'Dense drizzle', icon: 'rainy' },
  56: { description: 'Light freezing drizzle', icon: 'rainy' },
  57: { description: 'Dense freezing drizzle', icon: 'rainy' },
  61: { description: 'Slight rain', icon: 'rainy' },
  63: { description: 'Moderate rain', icon: 'rainy' },
  65: { description: 'Heavy rain', icon: 'rainy' },
  66: { description: 'Light freezing rain', icon: 'rainy' },
  67: { description: 'Heavy freezing rain', icon: 'rainy' },
  71: { description: 'Slight snow fall', icon: 'snowy' },
  73: { description: 'Moderate snow fall', icon: 'snowy' },
  75: { description: 'Heavy snow fall', icon: 'snowy' },
  77: { description: 'Snow grains', icon: 'snowy' },
  80: { description: 'Slight rain showers', icon: 'rainy' },
  81: { description: 'Moderate rain showers', icon: 'rainy' },
  82: { description: 'Violent rain showers', icon: 'stormy' },
  85: { description: 'Slight snow showers', icon: 'snowy' },
  86: { description: 'Heavy snow showers', icon: 'snowy' },
  95: { description: 'Thunderstorm', icon: 'stormy' },
  96: { description: 'Thunderstorm with slight hail', icon: 'stormy' },
  99: { description: 'Thunderstorm with heavy hail', icon: 'stormy' }
};

/**
 * Get weather description and icon from weather code
 * @param {number} code - WMO weather interpretation code
 * @returns {Object} Description and icon type
 */
function getWeatherInfo(code) {
  return WEATHER_CODE_MAP[code] || { description: 'Unknown', icon: 'cloudy' };
}

/**
 * Format date for API calls
 * @param {Date} date - Date object
 * @returns {string} Formatted date string YYYY-MM-DD
 */
function formatDate(date) {
  return date.toISOString().split('T')[0];
}

/**
 * Geocode location to coordinates using Open-Meteo
 * @param {string|number|Object} location - Location as string, zip code (number), or {latitude, longitude} object
 * @returns {Promise<Object>} Geocoded coordinates
 */
async function geocodeLocation(location) {
  // If location is already a coordinates object, return it
  if (typeof location === 'object' && location.latitude && location.longitude) {
    return {
      latitude: location.latitude,
      longitude: location.longitude,
      displayName: location.displayName || `${location.latitude}, ${location.longitude}`,
      name: location.name || 'Custom Location',
      country: location.country || '',
      admin1: location.admin1 || ''
    };
  }
  
  // Convert zip code (number) to string for geocoding
  const locationStr = typeof location === 'number' ? location.toString() : location;
  const cacheKey = `geocode_${locationStr.replace(/[^a-zA-Z0-9]/g, '_')}`;
  
  // Check cache first (geocoding results are stable)
  const cached = getCachedData(cacheKey);
  if (cached) {
    debugLog(`Using cached geocoding for ${locationStr}`);
    return cached;
  }

  try {
    const url = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(locationStr)}&count=1&language=en&format=json`;
    debugLog(`Geocoding location: ${locationStr}`);
    
    const data = await fetchWithCors(url);
    
    if (data.results && data.results.length > 0) {
      const result = data.results[0];
      const geocodeData = {
        latitude: result.latitude,
        longitude: result.longitude,
        name: result.name,
        country: result.country,
        admin1: result.admin1, // State/Province
        displayName: `${result.name}${result.admin1 ? ', ' + result.admin1 : ''}${result.country ? ', ' + result.country : ''}`
      };
      
      // Cache for 7 days (geocoding doesn't change often)
      cacheSourceData(cacheKey, geocodeData, 60 * 24 * 7);
      return geocodeData;
    } else {
      throw new Error(`Location not found: ${location}`);
    }
  } catch (error) {
    console.error(`Geocoding error for ${location}:`, error);
    throw error;
  }
}

/**
 * Fetch current weather and forecast from Open-Meteo
 * @param {number} latitude - Latitude
 * @param {number} longitude - Longitude
 * @param {string} tempUnit - Temperature unit ('F' or 'C')
 * @returns {Promise<Object>} Current weather and forecast data
 */
async function fetchCurrentAndForecast(latitude, longitude, tempUnit = 'F') {
  const tempUnitParam = tempUnit === 'F' ? 'fahrenheit' : 'celsius';
  const windUnitParam = tempUnit === 'F' ? 'mph' : 'kmh';
  
  const url = `https://api.open-meteo.com/v1/forecast?` +
    `latitude=${latitude}&longitude=${longitude}` +
    `&current=temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m,wind_direction_10m` +
    `&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max` +
    `&temperature_unit=${tempUnitParam}` +
    `&wind_speed_unit=${windUnitParam}` +
    `&timezone=auto` +
    `&forecast_days=5`;
  
  debugLog(`Fetching current weather and forecast for ${latitude}, ${longitude}`);
  
  const data = await fetchWithCors(url);
  return data;
}

/**
 * Fetch historical weather data from Open-Meteo Archive API
 * @param {number} latitude - Latitude
 * @param {number} longitude - Longitude
 * @param {string} tempUnit - Temperature unit ('F' or 'C')
 * @returns {Promise<Object>} Historical weather data
 */
async function fetchHistorical(latitude, longitude, tempUnit = 'F') {
  const tempUnitParam = tempUnit === 'F' ? 'fahrenheit' : 'celsius';
  const windUnitParam = tempUnit === 'F' ? 'mph' : 'kmh';
  
  // Calculate date range (past 7 days)
  const endDate = new Date();
  endDate.setDate(endDate.getDate() - 1); // Yesterday
  const startDate = new Date();
  startDate.setDate(startDate.getDate() - 7); // 7 days ago
  
  const url = `https://archive-api.open-meteo.com/v1/archive?` +
    `latitude=${latitude}&longitude=${longitude}` +
    `&start_date=${formatDate(startDate)}` +
    `&end_date=${formatDate(endDate)}` +
    `&daily=temperature_2m_max,temperature_2m_min,wind_speed_10m_max,wind_speed_10m_mean,weather_code` +
    `&temperature_unit=${tempUnitParam}` +
    `&wind_speed_unit=${windUnitParam}` +
    `&timezone=auto`;
  
  debugLog(`Fetching historical weather for ${latitude}, ${longitude}`);
  
  const data = await fetchWithCors(url);
  return data;
}

/**
 * Fetch complete weather data from Open-Meteo
 * @param {string|number|Object} location - Location as string, zip code, or {latitude, longitude}
 * @param {string} tempUnit - Temperature unit ('F' or 'C')
 * @returns {Promise<Object>} Complete weather data
 */
export async function fetchOpenMeteoWeather(location, tempUnit = 'F') {
  if (!location) {
    throw new Error('No location provided');
  }

  // Generate cache key based on location type
  let cacheKeyLocation;
  if (typeof location === 'object' && location.latitude && location.longitude) {
    cacheKeyLocation = `${location.latitude}_${location.longitude}`;
  } else {
    cacheKeyLocation = String(location).replace(/[^a-zA-Z0-9]/g, '_');
  }
  const cacheKey = `openmeteo_${cacheKeyLocation}_${tempUnit}`;
  
  // Check cache first
  const cached = getCachedData(cacheKey);
  if (cached) {
    debugLog(`Using cached Open-Meteo data for ${location}`);
    return cached;
  }

  try {
    // Step 1: Geocode the location
    const geocoded = await geocodeLocation(location);
    
    // Step 2: Fetch current weather and forecast
    const currentForecast = await fetchCurrentAndForecast(
      geocoded.latitude,
      geocoded.longitude,
      tempUnit
    );
    
    // Step 3: Fetch historical data
    const historical = await fetchHistorical(
      geocoded.latitude,
      geocoded.longitude,
      tempUnit
    );
    
    // Parse current weather
    const currentWeatherInfo = getWeatherInfo(currentForecast.current.weather_code);
    
    // Parse forecast data
    const forecast = currentForecast.daily.time.map((date, index) => {
      const weatherInfo = getWeatherInfo(currentForecast.daily.weather_code[index]);
      return {
        date: date,
        maxTemp: currentForecast.daily.temperature_2m_max[index],
        minTemp: currentForecast.daily.temperature_2m_min[index],
        precipitation: currentForecast.daily.precipitation_sum[index],
        windSpeed: currentForecast.daily.wind_speed_10m_max[index],
        weatherCode: currentForecast.daily.weather_code[index],
        description: weatherInfo.description,
        icon: weatherInfo.icon
      };
    });
    
    // Parse historical data
    const historicalData = {
      dates: historical.daily.time,
      maxTemps: historical.daily.temperature_2m_max,
      minTemps: historical.daily.temperature_2m_min,
      maxWindSpeeds: historical.daily.wind_speed_10m_max,
      avgWindSpeeds: historical.daily.wind_speed_10m_mean,
      weatherCodes: historical.daily.weather_code
    };
    
    // Build complete weather data structure
    const weatherData = {
      type: 'weather',
      location: geocoded.displayName,
      coordinates: {
        latitude: geocoded.latitude,
        longitude: geocoded.longitude
      },
      current: {
        temp: currentForecast.current.temperature_2m,
        apparentTemp: currentForecast.current.apparent_temperature,
        humidity: currentForecast.current.relative_humidity_2m,
        windSpeed: currentForecast.current.wind_speed_10m,
        windDirection: currentForecast.current.wind_direction_10m,
        weatherCode: currentForecast.current.weather_code,
        description: currentWeatherInfo.description,
        icon: currentWeatherInfo.icon,
        time: currentForecast.current.time
      },
      forecast: forecast,
      historical: historicalData,
      units: {
        temperature: tempUnit === 'F' ? '°F' : '°C',
        windSpeed: tempUnit === 'F' ? 'mph' : 'km/h',
        precipitation: 'in'
      },
      fetchedAt: Date.now()
    };
    
    // Cache for 15 minutes
    cacheSourceData(cacheKey, weatherData, 15);
    
    return weatherData;
  } catch (error) {
    console.error(`Error fetching Open-Meteo weather for ${location}:`, error);
    // Return error object that WeatherWidget can handle
    return {
      type: 'weather',
      error: true,
      message: error.message || 'Failed to fetch weather data',
      location: location
    };
  }
}