/**
 * Social Engine — Phase 1: Trend Intelligence
 * Scans 5 data sources to detect trending signals worth posting about.
 */
import pool from '../../db';
import { getTopPiffLegs } from '../../twitter/piff-loader';
import { insertTrend, isDuplicateTrend } from './data-queries';
import { isAllowedAmericanTrend, isAmericanSocialLeague, isAmericanSocialSport } from './sport-policy';
import type { TrendSignal } from './types';

// ── Source 1: Breaking/Curated News ──

async function scanNewsLinks(): Promise<Omit<TrendSignal, 'id'>[]> {
  const trends: Omit<TrendSignal, 'id'>[] = [];
  try {
    const result = await pool.query(
      `SELECT title, sport, source_display, custom_headline, custom_summary,
              engagement_score, is_breaking, is_featured, published_at
       FROM rm_news_links
       WHERE is_curated = true AND published_at > NOW() - INTERVAL '6 hours'
       ORDER BY engagement_score DESC LIMIT 10`
    );

    for (const row of result.rows) {
      if (!isAmericanSocialSport(row.sport)) continue;

      const heat = calculateNewsHeat(row);
      if (heat < 20) continue;

      trends.push({
        trend_type: row.is_breaking ? 'breaking_news' : 'curated_news',
        signal_source: 'news_links',
        sport: row.sport || null,
        league: null,
        title: row.custom_headline || row.title,
        data: {
          summary: row.custom_summary || '',
          source: row.source_display,
          engagement_score: row.engagement_score,
          is_breaking: row.is_breaking,
          is_featured: row.is_featured,
        },
        heat_score: heat,
        expires_at: new Date(Date.now() + 8 * 60 * 60 * 1000), // 8h expiry
      });
    }
  } catch (err: any) {
    console.error('[social-engine] News scan failed:', err.message);
  }
  return trends;
}

function calculateNewsHeat(row: any): number {
  let heat = 0;
  // Engagement (0-30)
  heat += Math.min(30, (row.engagement_score || 0) * 0.3);
  // Recency (0-20)
  const ageHours = (Date.now() - new Date(row.published_at).getTime()) / 3600000;
  heat += Math.max(0, 20 - ageHours * 3);
  // Breaking bonus (0-25)
  if (row.is_breaking) heat += 25;
  // Featured bonus (0-15)
  if (row.is_featured) heat += 15;
  return Math.min(100, heat);
}

// ── Source 2: High-Confidence Forecasts ──

async function scanForecasts(): Promise<Omit<TrendSignal, 'id'>[]> {
  const trends: Omit<TrendSignal, 'id'>[] = [];
  try {
    const result = await pool.query(
      `SELECT id, league, home_team, away_team, starts_at, composite_confidence,
              forecast_data, odds_data
       FROM rm_forecast_cache
       WHERE starts_at > NOW() AND starts_at < NOW() + INTERVAL '24 hours'
       AND composite_confidence >= 0.65
       ORDER BY composite_confidence DESC LIMIT 8`
    );

    for (const row of result.rows) {
      if (!isAmericanSocialLeague(row.league)) continue;

      const conf = row.composite_confidence || 0;
      const heat = Math.min(100, conf * 80 + 15);

      const forecastData = typeof row.forecast_data === 'string' ? JSON.parse(row.forecast_data) : (row.forecast_data || {});
      const oddsData = typeof row.odds_data === 'string' ? JSON.parse(row.odds_data) : (row.odds_data || {});

      trends.push({
        trend_type: 'forecast_pick',
        signal_source: 'forecast_cache',
        sport: leagueToSport(row.league),
        league: row.league,
        title: `${row.away_team} @ ${row.home_team} — ${Math.round(conf * 100)}% confidence`,
        data: {
          forecast_id: row.id,
          home_team: row.home_team,
          away_team: row.away_team,
          starts_at: row.starts_at,
          confidence: conf,
          summary: forecastData.summary || '',
          spread: oddsData.spread?.point || null,
          total: oddsData.total?.point || null,
        },
        heat_score: heat,
        expires_at: new Date(row.starts_at),
      });
    }
  } catch (err: any) {
    console.error('[social-engine] Forecast scan failed:', err.message);
  }
  return trends;
}

// ── Source 3: Recently Settled Forecasts (Recaps) ──

async function scanSettledForecasts(): Promise<Omit<TrendSignal, 'id'>[]> {
  const trends: Omit<TrendSignal, 'id'>[] = [];
  try {
    const result = await pool.query(
      `SELECT league, home_team, away_team, starts_at, composite_confidence,
              forecast_data, outcome, winner_pick, actual_winner
       FROM rm_archived_forecasts
       WHERE settled_at > NOW() - INTERVAL '6 hours'
       AND outcome IS NOT NULL AND outcome != 'pending'
       ORDER BY settled_at DESC LIMIT 10`
    );

    if (result.rows.length >= 3) {
      const allowedRows = result.rows.filter((row) => isAmericanSocialLeague(row.league));
      if (allowedRows.length < 3) {
        return trends;
      }

      let wins = 0, losses = 0;
      const games: any[] = [];

      for (const row of allowedRows) {
        const hit = row.outcome === 'win' || (row.winner_pick && row.winner_pick === row.actual_winner);
        if (hit) wins++; else losses++;
        games.push({
          league: row.league,
          home_team: row.home_team,
          away_team: row.away_team,
          hit,
          confidence: row.composite_confidence,
        });
      }

      const winRate = wins / (wins + losses);
      const heat = Math.min(100, winRate * 60 + 20 + (wins + losses) * 2);

      trends.push({
        trend_type: 'recap',
        signal_source: 'archived_forecasts',
        sport: null,
        league: null,
        title: `Forecast Results: ${wins}W-${losses}L`,
        data: { wins, losses, win_rate: winRate, games },
        heat_score: heat,
        expires_at: new Date(Date.now() + 12 * 60 * 60 * 1000),
      });
    }
  } catch (err: any) {
    console.error('[social-engine] Settled forecast scan failed:', err.message);
  }
  return trends;
}

// ── Source 4: PIFF 3.0 Top Props ──

async function scanPiffProps(): Promise<Omit<TrendSignal, 'id'>[]> {
  const trends: Omit<TrendSignal, 'id'>[] = [];
  try {
    const topLegs = getTopPiffLegs(8);

    for (const leg of topLegs) {
      if (!isAmericanSocialLeague(leg.league || '')) continue;

      const edge = leg.edge || 0;
      const prob = leg.prob || 0;
      const tierBonus = leg.tier === 1 ? 20 : leg.tier === 2 ? 10 : 0;
      const heat = Math.min(100, edge * 100 + prob * 30 + tierBonus);

      if (heat < 20) continue;

      trends.push({
        trend_type: 'piff_prop',
        signal_source: 'piff_3_0',
        sport: leagueToSport(leg.league || ''),
        league: leg.league || null,
        title: `${leg.name} ${leg.direction?.toUpperCase()} ${leg.line} ${leg.stat} (${leg.tier_label})`,
        data: {
          name: leg.name,
          team: leg.team,
          stat: leg.stat,
          line: leg.line,
          edge: leg.edge,
          prob: leg.prob,
          direction: leg.direction,
          tier: leg.tier,
          tier_label: leg.tier_label,
          dvp_tier: leg.dvp_tier,
          league: leg.league,
        },
        heat_score: heat,
        expires_at: new Date(Date.now() + 10 * 60 * 60 * 1000),
      });
    }
  } catch (err: any) {
    console.error('[social-engine] PIFF scan failed:', err.message);
  }
  return trends;
}

// ── Source 5: Celebrity Tracker ──

async function scanCelebrityTracker(): Promise<Omit<TrendSignal, 'id'>[]> {
  const trends: Omit<TrendSignal, 'id'>[] = [];
  try {
    const result = await pool.query(
      `SELECT celebrity_name, description, sport, game_context, engagement_score, detected_at
       FROM rm_celebrity_tracker
       WHERE detected_at > NOW() - INTERVAL '6 hours'
       ORDER BY engagement_score DESC LIMIT 5`
    );

    for (const row of result.rows) {
      if (!isAmericanSocialSport(row.sport)) continue;

      const heat = Math.min(100, (row.engagement_score || 0) * 0.4 + 15);
      if (heat < 20) continue;

      trends.push({
        trend_type: 'celebrity',
        signal_source: 'celebrity_tracker',
        sport: row.sport || null,
        league: null,
        title: `${row.celebrity_name}: ${row.description?.substring(0, 100) || 'Trending'}`,
        data: {
          celebrity_name: row.celebrity_name,
          description: row.description,
          game_context: row.game_context,
          engagement_score: row.engagement_score,
        },
        heat_score: heat,
        expires_at: new Date(Date.now() + 6 * 60 * 60 * 1000),
      });
    }
  } catch (err: any) {
    // Celebrity tracker table may not exist yet — non-fatal
    if (!err.message.includes('does not exist')) {
      console.error('[social-engine] Celebrity scan failed:', err.message);
    }
  }
  return trends;
}

// ── Helpers ──

function leagueToSport(league: string): string {
  const map: Record<string, string> = {
    nba: 'basketball', ncaab: 'basketball',
    nfl: 'football', ncaaf: 'football',
    nhl: 'hockey',
    mlb: 'baseball',
    epl: 'soccer', la_liga: 'soccer', bundesliga: 'soccer',
    serie_a: 'soccer', ligue_1: 'soccer', champions_league: 'soccer',
    mma: 'mma', ufc: 'mma',
  };
  return map[league?.toLowerCase()] || 'general';
}

// ── Main Entry Point ──

export async function detectTrends(): Promise<number> {
  console.log('[social-engine] Phase 1: Scanning for trends...');

  const allSignals: Omit<TrendSignal, 'id'>[] = [];

  // Run all scans in parallel
  const [news, forecasts, settled, piff, celebrity] = await Promise.all([
    scanNewsLinks(),
    scanForecasts(),
    scanSettledForecasts(),
    scanPiffProps(),
    scanCelebrityTracker(),
  ]);

  allSignals.push(...news, ...forecasts, ...settled, ...piff, ...celebrity);

  // Deduplicate and insert
  let inserted = 0;
  for (const signal of allSignals) {
    if (!isAllowedAmericanTrend(signal)) continue;

    const isDupe = await isDuplicateTrend(signal.title, 12);
    if (isDupe) continue;

    await insertTrend(signal);
    inserted++;
  }

  console.log(`[social-engine] Phase 1 complete: ${inserted} trends inserted (${news.length} news, ${forecasts.length} forecasts, ${settled.length} settled, ${piff.length} PIFF, ${celebrity.length} celebrity)`);
  return inserted;
}
