/**
 * QM News Actions - Fetch symbol-specific and general market news
 */

import type { ExecutionContext } from '@stock-bot/handlers';
import type { QMHandler } from '../qm.handler';
import { QM_CONFIG, QM_SESSION_IDS } from '../shared/config';
import { QMSessionManager } from '../shared/session-manager';

interface NewsArticle {
  id: string;
  publishedDate: Date;
  title: string;
  summary: string;
  source: string;
  url: string;
  symbols?: string[];
  categories?: string[];
  sentiment?: {
    score: number;
    label: string; // positive, negative, neutral
  };
  imageUrl?: string;
}

/**
 * Update news for a single symbol
 */
export async function updateSymbolNews(
  this: QMHandler,
  input: {
    symbol: string;
    symbolId: number;
    qmSearchCode: string;
    lookbackDays?: number;
  },
  _context?: ExecutionContext
): Promise<{
  success: boolean;
  symbol: string;
  message: string;
  data?: any;
}> {
  const { symbol, symbolId, qmSearchCode, lookbackDays = 30 } = input;
  
  this.logger.info('Fetching symbol news', { symbol, symbolId, lookbackDays });

  const sessionManager = QMSessionManager.getInstance();
  await sessionManager.initialize(this.cache, this.logger);

  const sessionId = QM_SESSION_IDS.LOOKUP;
  const session = await sessionManager.getSession(sessionId);
  
  if (!session || !session.uuid) {
    throw new Error(`No active session found for QM news`);
  }

  try {
    // Calculate date range
    const endDate = new Date();
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - lookbackDays);

    // Build API request for symbol news
    const searchParams = new URLSearchParams({
      symbol: symbol,
      symbolId: symbolId.toString(),
      qmodTool: 'News',
      webmasterId: '500',
      startDate: startDate.toISOString().split('T')[0],
      endDate: endDate.toISOString().split('T')[0],
      includeContent: 'true',
      pageSize: '50'
    } as Record<string, string>);
    
    const apiUrl = `${QM_CONFIG.BASE_URL}/datatool/news.json?${searchParams.toString()}`;

    const response = await fetch(apiUrl, {
      method: 'GET',
      headers: session.headers,
      proxy: session.proxy,
    });

    if (!response.ok) {
      throw new Error(`QM API request failed: ${response.status} ${response.statusText}`);
    }

    const newsData = await response.json();
    
    // Update session success stats
    await sessionManager.incrementSuccessfulCalls(sessionId, session.uuid);

    // Process and store news data
    if (newsData && newsData.articles && newsData.articles.length > 0) {
      const processedArticles = newsData.articles.map((article: any) => ({
        articleId: article.id || `${symbol}_${article.publishedDate}_${article.title.substring(0, 20)}`,
        symbol,
        symbolId,
        publishedDate: new Date(article.publishedDate),
        title: article.title,
        summary: article.summary || article.content?.substring(0, 500),
        source: article.source || 'Unknown',
        url: article.url,
        symbols: article.symbols || [symbol],
        categories: article.categories || [],
        sentiment: article.sentiment ? {
          score: parseFloat(article.sentiment.score) || 0,
          label: article.sentiment.label || 'neutral'
        } : null,
        imageUrl: article.imageUrl,
        isSymbolSpecific: true,
        updated_at: new Date()
      }));

      // Store in MongoDB
      await this.mongodb.batchUpsert(
        'qmNews',
        processedArticles,
        ['articleId'] // Unique key
      );

      // Calculate sentiment summary
      const sentimentCounts = processedArticles.reduce((acc: any, article: any) => {
        if (article.sentiment) {
          acc[article.sentiment.label] = (acc[article.sentiment.label] || 0) + 1;
        }
        return acc;
      }, {});

      // Update operation tracking
      await this.operationRegistry.updateOperation('qm', qmSearchCode, 'news_update', {
        status: 'success',
        lastRecordDate: endDate,
        recordCount: processedArticles.length,
        metadata: {
          sentimentCounts,
          uniqueSources: new Set(processedArticles.map((a: any) => a.source)).size,
          avgSentimentScore: processedArticles
            .filter((a: any) => a.sentiment?.score)
            .reduce((sum: number, a: any, i: number, arr: any[]) => 
              i === arr.length - 1 ? (sum + a.sentiment.score) / arr.length : sum + a.sentiment.score, 0
            )
        }
      });

      this.logger.info('Symbol news updated successfully', { 
        symbol,
        articleCount: processedArticles.length,
        sentimentCounts
      });

      return {
        success: true,
        symbol,
        message: `Updated ${processedArticles.length} news articles`,
        data: { 
          count: processedArticles.length,
          sentimentCounts,
          sources: new Set(processedArticles.map((a: any) => a.source)).size
        }
      };
    } else {
      // No news found
      this.logger.info('No news articles found', { symbol });
      
      await this.operationRegistry.updateOperation('qm', qmSearchCode, 'news_update', {
        status: 'success',
        lastRecordDate: endDate,
        recordCount: 0
      });
      
      return {
        success: true,
        symbol,
        message: 'No news articles found',
        data: { count: 0 }
      };
    }

  } catch (error) {
    // Update session failure stats
    if (session.uuid) {
      await sessionManager.incrementFailedCalls(sessionId, session.uuid);
    }

    this.logger.error('Error fetching symbol news', { 
      symbol,
      error: error instanceof Error ? error.message : 'Unknown error'
    });
    
    // Update operation tracking for failure
    await this.operationRegistry.updateOperation('qm', qmSearchCode, 'news_update', {
      status: 'failure',
      error: error instanceof Error ? error.message : 'Unknown error'
    });
    
    return {
      success: false,
      symbol,
      message: `Failed to fetch news: ${error instanceof Error ? error.message : 'Unknown error'}`
    };
  }
}

/**
 * Update general market news
 */
export async function updateGeneralNews(
  this: QMHandler,
  input: {
    categories?: string[];
    lookbackMinutes?: number;
  } = {},
  _context?: ExecutionContext
): Promise<{
  success: boolean;
  message: string;
  data?: any;
}> {
  const { categories = ['market', 'economy', 'politics'], lookbackMinutes = 60 } = input;
  
  this.logger.info('Fetching general news', { categories, lookbackMinutes });

  const sessionManager = QMSessionManager.getInstance();
  await sessionManager.initialize(this.cache, this.logger);

  const sessionId = QM_SESSION_IDS.LOOKUP;
  const session = await sessionManager.getSession(sessionId);
  
  if (!session || !session.uuid) {
    throw new Error(`No active session found for QM general news`);
  }

  try {
    // Calculate time range
    const endDate = new Date();
    const startDate = new Date();
    startDate.setMinutes(startDate.getMinutes() - lookbackMinutes);

    // Build API request for general news
    const searchParams = new URLSearchParams({
      qmodTool: 'MarketNews',
      webmasterId: '500',
      categories: categories.join(','),
      startDateTime: startDate.toISOString(),
      endDateTime: endDate.toISOString(),
      includeContent: 'true',
      pageSize: '100'
    } as Record<string, string>);
    
    const apiUrl = `${QM_CONFIG.BASE_URL}/datatool/marketnews.json?${searchParams.toString()}`;

    const response = await fetch(apiUrl, {
      method: 'GET',
      headers: session.headers,
      proxy: session.proxy,
    });

    if (!response.ok) {
      throw new Error(`QM API request failed: ${response.status} ${response.statusText}`);
    }

    const newsData = await response.json();
    
    // Update session success stats
    await sessionManager.incrementSuccessfulCalls(sessionId, session.uuid);

    // Process and store general news
    if (newsData && newsData.articles && newsData.articles.length > 0) {
      const processedArticles = newsData.articles.map((article: any) => ({
        articleId: article.id || `general_${article.publishedDate}_${article.title.substring(0, 20)}`,
        publishedDate: new Date(article.publishedDate),
        title: article.title,
        summary: article.summary || article.content?.substring(0, 500),
        source: article.source || 'Unknown',
        url: article.url,
        symbols: article.symbols || [],
        categories: article.categories || categories,
        sentiment: article.sentiment ? {
          score: parseFloat(article.sentiment.score) || 0,
          label: article.sentiment.label || 'neutral'
        } : null,
        imageUrl: article.imageUrl,
        isSymbolSpecific: false,
        isMarketMoving: article.isMarketMoving || false,
        importance: article.importance || 'medium',
        updated_at: new Date()
      }));

      // Store in MongoDB
      await this.mongodb.batchUpsert(
        'qmNews',
        processedArticles,
        ['articleId'] // Unique key
      );

      // Find high-importance articles
      const highImportanceCount = processedArticles.filter((a: any) => 
        a.importance === 'high' || a.isMarketMoving
      ).length;

      // Update a general tracking document
      await this.mongodb.updateOne(
        'qmOperationStats',
        { operation: 'general_news_update' },
        {
          $set: {
            lastRunAt: new Date(),
            lastRecordCount: processedArticles.length,
            highImportanceCount,
            categories,
            updated_at: new Date()
          }
        },
        { upsert: true }
      );

      this.logger.info('General news updated successfully', { 
        articleCount: processedArticles.length,
        highImportanceCount,
        categories
      });

      return {
        success: true,
        message: `Updated ${processedArticles.length} general news articles`,
        data: { 
          count: processedArticles.length,
          highImportanceCount,
          categories,
          sources: new Set(processedArticles.map((a: any) => a.source)).size
        }
      };
    } else {
      // No news found
      this.logger.info('No general news articles found');
      
      return {
        success: true,
        message: 'No general news articles found',
        data: { count: 0 }
      };
    }

  } catch (error) {
    // Update session failure stats
    if (session.uuid) {
      await sessionManager.incrementFailedCalls(sessionId, session.uuid);
    }

    this.logger.error('Error fetching general news', { 
      error: error instanceof Error ? error.message : 'Unknown error'
    });
    
    return {
      success: false,
      message: `Failed to fetch general news: ${error instanceof Error ? error.message : 'Unknown error'}`
    };
  }
}

/**
 * Schedule symbol news updates
 */
export async function scheduleSymbolNewsUpdates(
  this: QMHandler,
  input: {
    limit?: number;
    minHoursSinceRun?: number;
    forceUpdate?: boolean;
  } = {},
  _context?: ExecutionContext
): Promise<{
  message: string;
  symbolsQueued: number;
  errors: number;
}> {
  const { limit = 200, minHoursSinceRun = 24 * 7, forceUpdate = false } = input;
  
  this.logger.info('Scheduling symbol news updates', { limit, minHoursSinceRun, forceUpdate });

  try {
    // Get symbols that need news updates
    const staleSymbols = await this.operationRegistry.getStaleSymbols('qm', 'news_update', {
      minHoursSinceRun: forceUpdate ? 0 : minHoursSinceRun,
      limit
    });

    if (staleSymbols.length === 0) {
      this.logger.info('No symbols need news updates');
      return {
        message: 'No symbols need news updates',
        symbolsQueued: 0,
        errors: 0
      };
    }

    this.logger.info(`Found ${staleSymbols.length} symbols for news updates`);

    let symbolsQueued = 0;
    let errors = 0;

    // Schedule update jobs
    for (const item of staleSymbols) {
      try {
        if (!item.symbol.symbolId) {
          this.logger.warn(`Symbol ${item.symbol.symbol} missing symbolId, skipping`);
          continue;
        }

        await this.scheduleOperation('update-symbol-news', {
          symbol: item.symbol.symbol,
          symbolId: item.symbol.symbolId,
          qmSearchCode: item.symbol.qmSearchCode
        }, {
          priority: 4, // Lower priority than price data
          delay: symbolsQueued * 500 // 0.5 seconds between jobs
        });
        
        symbolsQueued++;
      } catch (error) {
        this.logger.error(`Failed to schedule news update for ${item.symbol.symbol}`, { error });
        errors++;
      }
    }

    this.logger.info('Symbol news update scheduling completed', {
      symbolsQueued,
      errors
    });

    return {
      message: `Scheduled news updates for ${symbolsQueued} symbols`,
      symbolsQueued,
      errors
    };
  } catch (error) {
    this.logger.error('Symbol news scheduling failed', { error });
    throw error;
  }
}