/**
 * Admin Forecast Push Endpoints
 *
 * POST /api/admin/forecast/push-todays-best    — layout/functionality refresh (no content regen)
 * POST /api/admin/forecast/full-push-todays-best — full rebuild including content
 */

import { Router, Request, Response } from 'express';
import { authMiddleware } from '../middleware/auth';
import { auditAdminAccess, requireAdminAccess } from '../middleware/admin';
import { getCachedForecast, cacheForecast, updateCachedOdds } from '../models/forecast';
import { generateForecast } from '../services/grok';
import { fetchEvents, parseOdds, LEAGUE_MAP } from '../services/sgo';
import { calculateComposite } from '../services/composite-score';
import { getPiffPropsForGame } from '../services/piff';
import { getDigimonForGame } from '../services/digimon';
import { getDvpForMatchup } from '../services/dvp';
import pool from '../db';
import { getLegacyCompositeView } from '../services/rie';

const router = Router();
router.use(authMiddleware, requireAdminAccess, auditAdminAccess('admin-forecast'));

/**
 * Resolve the Today's Best event — the free pick of the day.
 * Returns { eventId, league } or null.
 */
async function getTodaysBestEventId(): Promise<{ eventId: string; league: string } | null> {
  const dateET = new Date().toLocaleDateString('en-CA', { timeZone: 'America/New_York' });

  // First check rm_free_pick for today's persisted pick, but only if it has not started
  const { rows: pickRows } = await pool.query(
    `SELECT fp.event_id, fp.league
     FROM rm_free_pick fp
     LEFT JOIN rm_forecast_cache fc ON fp.event_id = fc.event_id
     WHERE fp.pick_date = $1
       AND fc.starts_at > NOW()`,
    [dateET]
  );
  if (pickRows.length > 0) {
    return { eventId: pickRows[0].event_id, league: pickRows[0].league };
  }

  // Fallback: compute it from highest-confidence forecast today
  const { rows } = await pool.query(
    `SELECT fc.event_id, fc.league
     FROM rm_forecast_cache fc
     WHERE (fc.starts_at AT TIME ZONE 'America/New_York')::date = $1::date
       AND fc.starts_at > NOW()
       AND fc.confidence_score IS NOT NULL
     ORDER BY fc.confidence_score DESC, fc.starts_at ASC
     LIMIT 1`,
    [dateET]
  );
  return rows[0] ? { eventId: rows[0].event_id, league: rows[0].league } : null;
}

/** Fetch curated event from rm_events */
async function getCuratedEvent(eventId: string): Promise<any | null> {
  const { rows } = await pool.query(
    'SELECT * FROM rm_events WHERE event_id = $1',
    [eventId]
  ).catch(() => ({ rows: [] }));
  return rows[0] || null;
}

/** Refresh odds from SGO for a curated event */
async function refreshOddsFromSgo(eventId: string, league: string): Promise<any | null> {
  try {
    if (!league || !LEAGUE_MAP[league]) return null;
    const events = await fetchEvents(league);
    const sgoEvent = events.find((e: any) => e.eventID === eventId);
    if (sgoEvent) {
      const odds = parseOdds(sgoEvent);
      await updateCachedOdds(eventId, odds);
      return odds;
    }
  } catch (err) {
    console.error(`[admin-forecast] SGO odds refresh failed for ${eventId}:`, err);
  }
  return null;
}

// ── POST /push-todays-best — layout/functionality refresh, no content regen ──
router.post('/push-todays-best', async (req: Request, res: Response) => {
  try {
    const best = await getTodaysBestEventId();
    if (!best) {
      res.status(404).json({ error: 'No Today\'s Best forecast found for today' });
      return;
    }

    const { eventId, league } = best;
    const cached = await getCachedForecast(eventId);
    if (!cached) {
      res.status(404).json({ error: `No cached forecast for event ${eventId}` });
      return;
    }

    // 1. Refresh odds from SGO
    const freshOdds = await refreshOddsFromSgo(eventId, league);

    // 2. Recalculate composite score (no Grok call — uses existing forecast data)
    const forecastData = cached.forecast_data;
    const legacyComposite = getLegacyCompositeView(
      cached.model_signals,
      cached.confidence_score ?? forecastData?.confidence ?? 0.5,
      forecastData?.value_rating ?? 5,
    );
    const grokConfidence = legacyComposite?.modelSignals?.grok?.confidence ?? cached.confidence_score ?? forecastData?.confidence ?? 0.5;
    const grokValueRating = forecastData?.value_rating ?? 5;

    const piffProps = getPiffPropsForGame(cached.home_team, cached.away_team, undefined, league);
    const digimonPicks = getDigimonForGame(cached.home_team, cached.away_team);

    let dvp = { home: null as any, away: null as any };
    try {
      dvp = await getDvpForMatchup(cached.home_team, cached.away_team, league);
    } catch { /* DVP optional */ }

    const composite = calculateComposite({
      grokConfidence,
      grokValueRating,
      piffProps,
      digimonPicks,
      dvp,
      league,
    });

    // 3. Update cache row: stamp refreshed composite + odds, preserve forecast_data
    await pool.query(
      `UPDATE rm_forecast_cache SET
         composite_confidence = $1,
         model_signals = $2,
         composite_version = $3,
         odds_updated_at = NOW()
       WHERE event_id = $4`,
      [composite.compositeConfidence, JSON.stringify(composite.modelSignals), composite.compositeVersion, eventId]
    );

    // 4. Re-persist free pick with updated confidence
    const dateET = new Date().toLocaleDateString('en-CA', { timeZone: 'America/New_York' });
    await pool.query(
      `UPDATE rm_free_pick SET confidence_score = $1, updated_at = NOW() WHERE pick_date = $2 AND event_id = $3`,
      [composite.compositeConfidence, dateET, eventId]
    );

    console.log(`[admin-forecast] Push to Today's Best: ${eventId} (${cached.home_team} vs ${cached.away_team}) — composite=${composite.compositeConfidence.toFixed(3)}, storm=${composite.stormCategory}`);

    res.json({
      status: 'ok',
      action: 'push-todays-best',
      eventId,
      league,
      homeTeam: cached.home_team,
      awayTeam: cached.away_team,
      compositeConfidence: composite.compositeConfidence,
      stormCategory: composite.stormCategory,
      oddsRefreshed: !!freshOdds,
      contentRegenerated: false,
    });
  } catch (err) {
    console.error('Push to Today\'s Best error:', err);
    res.status(500).json({ error: 'Failed to push Today\'s Best' });
  }
});

// ── POST /full-push-todays-best — full rebuild including content regen ──
router.post('/full-push-todays-best', async (req: Request, res: Response) => {
  try {
    const best = await getTodaysBestEventId();
    if (!best) {
      res.status(404).json({ error: 'No Today\'s Best forecast found for today' });
      return;
    }

    const { eventId, league } = best;
    const curated = await getCuratedEvent(eventId);
    if (!curated) {
      res.status(404).json({ error: `Event ${eventId} not found in rm_events` });
      return;
    }

    // 1. Full forecast regeneration via LLM
    const forecast = await generateForecast({
      homeTeam: curated.home_team,
      awayTeam: curated.away_team,
      league: curated.league,
      startsAt: curated.starts_at,
      moneyline: curated.moneyline || { home: null, away: null },
      spread: curated.spread || { home: null, away: null },
      total: curated.total || { over: null, under: null },
      dbAnalytics: null,
      eventId,
    });

    // 2. Cache the regenerated forecast
    const saved = await cacheForecast({
      eventId,
      league: curated.league,
      homeTeam: curated.home_team,
      awayTeam: curated.away_team,
      forecastData: forecast,
      confidenceScore: forecast.confidence,
      startsAt: curated.starts_at,
      expiresAt: curated.starts_at,
      oddsData: {
        moneyline: curated.moneyline || { home: null, away: null },
        spread: curated.spread || { home: null, away: null },
        total: curated.total || { over: null, under: null },
      },
    });

    // 3. Recalculate composite with fresh forecast signals
    const piffProps = getPiffPropsForGame(curated.home_team, curated.away_team, undefined, curated.league);
    const digimonPicks = getDigimonForGame(curated.home_team, curated.away_team);

    let dvp = { home: null as any, away: null as any };
    try {
      dvp = await getDvpForMatchup(curated.home_team, curated.away_team, league);
    } catch { /* DVP optional */ }

    const composite = calculateComposite({
      grokConfidence: forecast.confidence,
      grokValueRating: forecast.value_rating || 5,
      piffProps,
      digimonPicks,
      dvp,
      league,
    });

    // 4. Stamp composite on the cache row
    await pool.query(
      `UPDATE rm_forecast_cache SET
         composite_confidence = $1,
         model_signals = $2,
         composite_version = $3
       WHERE event_id = $4`,
      [composite.compositeConfidence, JSON.stringify(composite.modelSignals), composite.compositeVersion, eventId]
    );

    // 5. Refresh SGO odds
    const freshOdds = await refreshOddsFromSgo(eventId, league);

    // 6. Update free pick row
    const dateET = new Date().toLocaleDateString('en-CA', { timeZone: 'America/New_York' });
    await pool.query(
      `UPDATE rm_free_pick SET confidence_score = $1, updated_at = NOW() WHERE pick_date = $2 AND event_id = $3`,
      [composite.compositeConfidence, dateET, eventId]
    );

    console.log(`[admin-forecast] Full Push Today's Best: ${eventId} (${curated.home_team} vs ${curated.away_team}) — composite=${composite.compositeConfidence.toFixed(3)}, storm=${composite.stormCategory}`);

    res.json({
      status: 'ok',
      action: 'full-push-todays-best',
      eventId,
      league,
      homeTeam: curated.home_team,
      awayTeam: curated.away_team,
      forecastId: saved.id,
      compositeConfidence: composite.compositeConfidence,
      stormCategory: composite.stormCategory,
      oddsRefreshed: !!freshOdds,
      contentRegenerated: true,
      generatedAt: saved.created_at,
    });
  } catch (err) {
    console.error('Full Push Today\'s Best error:', err);
    res.status(500).json({ error: 'Failed to full-push Today\'s Best' });
  }
});

export default router;
