import { parseSource, standardizeSourceData } from './sourceParser';
import { getCachedData, cacheSourceData } from './storageService';
import { fetchWithCors } from '../utils/corsProxy';
import { fetchOpenMeteoWeather } from './openMeteoService';
import { summarizeWebpage } from './claudeService';
import {
  DEFAULT_DATA_SOURCES,
  WEATHER_API_BASE,
  DEFAULT_REFRESH_INTERVAL,
  DEFAULT_TEMPERATURE_UNIT,
  debugLog,
  debugWarn
} from '../utils/constants';

/**
 * Parse environment variable - supports both array and object formats
 * @param {string} envValue - Comma-separated values or JSON object
 * @returns {Array|Object} Parsed data
 */
function parseEnvSources(envValue) {
  if (!envValue) return [];
  
  // Try to parse as JSON object first (new format)
  try {
    const parsed = JSON.parse(envValue);
    if (typeof parsed === 'object' && !Array.isArray(parsed)) {
      // It's an object with rows
      return parsed;
    }
  } catch (e) {
    // Not valid JSON, try comma-separated format (legacy)
  }
  
  // Fall back to comma-separated format
  return envValue.split(',').map(s => s.trim()).filter(Boolean);
}

/**
 * Generate a cache key from URL
 * @param {string} url - The URL
 * @returns {string} Cache key
 */
function getCacheKey(url) {
  return url.replace(/[^a-zA-Z0-9]/g, '_').substring(0, 50);
}

/**
 * Fetch single source with caching
 * @param {string} url - Source URL
 * @returns {Promise<Object>} Source data
 */
export async function fetchSingleSource(url) {
  const cacheKey = getCacheKey(url);

  // Check cache first
  const cached = getCachedData(cacheKey);
  if (cached) {
    debugLog(`Using cached data for ${url}`);
    return cached;
  }

  try {
    debugLog(`Fetching fresh data from ${url}`);
    const data = await parseSource(url);
    let standardized = standardizeSourceData(data, url);
    let tokenUsage = null;

    // If this is a webpage that needs Claude summarization, do it now
    if (standardized.type === 'webpage' && standardized.needsSummarization && standardized.cleanedText) {
      debugLog(`Summarizing webpage content from ${url} with Claude...`);
      try {
        const summary = await summarizeWebpage(standardized.cleanedText, url);
        standardized = {
          ...standardized,
          title: summary.title,
          contentType: summary.contentType,
          summary: summary.summary,
          items: summary.items || [],
          needsSummarization: false,
          // Remove the large cleaned text from the cached result
          cleanedText: undefined
        };
        // Track token usage from webpage summarization
        tokenUsage = summary.usage;
        debugLog(`Webpage summarized: "${summary.title}" (${summary.items?.length || 0} items)`);
        if (tokenUsage) {
          debugLog(`Webpage summarization tokens: ${tokenUsage.input_tokens} input + ${tokenUsage.output_tokens} output`);
        }
      } catch (summaryError) {
        console.error(`Failed to summarize webpage ${url}:`, summaryError);
        // Keep the standardized data but mark summarization as failed
        standardized.needsSummarization = false;
        standardized.title = new URL(url).hostname;
        standardized.summary = `Failed to summarize: ${summaryError.message}`;
        standardized.cleanedText = undefined;
      }
    }

    // Cache the result
    const ttl = parseInt(import.meta.env.REACT_APP_REFRESH_INTERVAL) || DEFAULT_REFRESH_INTERVAL;
    cacheSourceData(cacheKey, standardized, ttl);

    return { data: standardized, tokenUsage };
  } catch (error) {
    console.error(`Error fetching ${url}:`, error);
    // Try to return cached data even if expired
    const expiredCache = getCachedData(cacheKey);
    if (expiredCache) {
      debugLog(`Returning expired cache for ${url} due to fetch error`);
      return expiredCache;
    }
    throw error;
  }
}

/**
 * Fetch weather data using Open-Meteo API
 * @param {string|number|Object} location - Location as string, zip code, or {latitude, longitude}
 * @returns {Promise<Object>} Weather data
 */
export async function fetchWeather(location) {
  if (!location) {
    debugWarn('No location configured for weather');
    return null;
  }

  // Parse location - check if it's a numeric string (zip code)
  let parsedLocation = location;
  if (typeof location === 'string' && /^\d+$/.test(location)) {
    parsedLocation = parseInt(location, 10);
  }

  // Get temperature unit from environment or use default
  const tempUnit = import.meta.env.REACT_APP_TEMPERATURE_UNIT || DEFAULT_TEMPERATURE_UNIT;

  try {
    // Use the new Open-Meteo service (now supports numbers and objects)
    const weatherData = await fetchOpenMeteoWeather(parsedLocation, tempUnit);
    return weatherData;
  } catch (error) {
    console.error(`Error fetching weather for ${parsedLocation}:`, error);
    return {
      type: 'weather',
      error: true,
      message: error.message || 'Failed to fetch weather data',
      location: location
    };
  }
}

/**
 * Fetch from all configured sources
 * @returns {Promise<Object>} Object with all source data organized by rows
 */
export async function fetchAllSources() {
  // Get configured sources from environment or use defaults
  const sourcesEnv = import.meta.env.REACT_APP_DATA_SOURCES;
  const sources = sourcesEnv ? parseEnvSources(sourcesEnv) : DEFAULT_DATA_SOURCES;

  // Get location for weather
  const location = import.meta.env.REACT_APP_LOCATION;

  debugLog('Fetching from sources:', sources);
  debugLog('Weather location:', location);

  let sourcesByRow = {};
  let allUrls = [];

  // Check if sources is an object (new format) or array (legacy format)
  if (typeof sources === 'object' && !Array.isArray(sources)) {
    // New format: { "row1": [...urls], "row2": [...urls] }
    sourcesByRow = sources;
    // Flatten all URLs for fetching
    allUrls = Object.values(sources).flat();
  } else {
    // Legacy format: flat array of URLs
    // Put all in a single row
    sourcesByRow = { row1: Array.isArray(sources) ? sources : [] };
    allUrls = Array.isArray(sources) ? sources : [];
  }

  // Create fetch promises for all URLs
  const urlPromises = allUrls.map(url =>
    fetchSingleSource(url)
      .then(result => ({ url, result, error: false }))
      .catch(error => ({
        url,
        result: null,
        error: true,
        message: error.message
      }))
  );

  // Add weather fetch if location is configured
  let weatherPromise = null;
  if (location) {
    weatherPromise = fetchWeather(location).catch(error => ({
      error: true,
      type: 'weather',
      message: error.message
    }));
  }

  // Fetch all in parallel
  const urlResults = await Promise.all(urlPromises);
  const weatherResult = weatherPromise ? await weatherPromise : null;

  // Create a map of URL to fetched data and accumulate token usage
  const urlDataMap = {};
  const errors = [];
  const allTokenUsage = [];

  urlResults.forEach(result => {
    if (result.error) {
      errors.push({
        url: result.url,
        error: true,
        message: result.message
      });
    } else if (result.result) {
      // Handle new return format: { data, tokenUsage }
      if (result.result.data) {
        urlDataMap[result.url] = result.result.data;
      }
      if (result.result.tokenUsage) {
        allTokenUsage.push({
          source: result.url,
          usage: result.result.tokenUsage
        });
      }
    }
  });

  // Calculate total webpage summarization token usage
  let totalWebpageTokens = { input_tokens: 0, output_tokens: 0 };
  allTokenUsage.forEach(({ usage }) => {
    totalWebpageTokens.input_tokens += usage.input_tokens || 0;
    totalWebpageTokens.output_tokens += usage.output_tokens || 0;
  });

  if (allTokenUsage.length > 0) {
    debugLog(`Total webpage summarization usage: ${totalWebpageTokens.input_tokens} input + ${totalWebpageTokens.output_tokens} output tokens from ${allTokenUsage.length} source(s)`);
  }

  // Organize data by rows
  const sourceData = {};
  
  // Add weather as a special entry
  if (weatherResult && !weatherResult.error) {
    sourceData.weather = weatherResult;
  } else if (weatherResult && weatherResult.error) {
    errors.push(weatherResult);
  }

  // Process each row
  Object.entries(sourcesByRow).forEach(([rowKey, urls]) => {
    sourceData[rowKey] = [];
    
    urls.forEach(url => {
      if (urlDataMap[url]) {
        sourceData[rowKey].push(urlDataMap[url]);
      }
    });
  });

  // For backward compatibility, also add flat structure
  let flatIndex = 0;
  Object.values(sourceData).forEach(rowData => {
    if (Array.isArray(rowData)) {
      rowData.forEach(data => {
        if (data.type === 'reddit') {
          const key = `reddit_${Object.keys(sourceData).filter(k => k.startsWith('reddit')).length}`;
          sourceData[key] = data;
        } else if (data.type === 'rss') {
          const key = `rss_${Object.keys(sourceData).filter(k => k.startsWith('rss')).length}`;
          sourceData[key] = data;
        } else {
          const key = `source_${flatIndex++}`;
          sourceData[key] = data;
        }
      });
    }
  });

  // Log any errors
  if (errors.length > 0) {
    debugWarn('Some sources failed to fetch:', errors);
  }

  return {
    sources: sourceData,
    errors: errors,
    fetchedAt: Date.now(),
    successful: allUrls.length - errors.length + (weatherResult && !weatherResult.error ? 1 : 0),
    failed: errors.length,
    webpageTokenUsage: allTokenUsage.length > 0 ? totalWebpageTokens : null
  };
}
