// ARCH-1 Step 4 (Session 7f): /api/scan/parlay legs now grade via
// engine1 (computeFeatures → engine1 → gradeAdapter) instead of the
// legacy `analyzeProp`. Response shape is byte-compatible. Parallel
// resolution from PERF-2 is preserved (still inside Promise.allSettled).
const { analyzeViaEngine1 } = require('./intelligence/analyzeViaEngine1');
const { getOdds } = require('./oddsService');
const { detectCorrelations } = require('./correlationEngine');
const { gradeParlayFromLegs } = require('./parlayGrader');
const { generateUpgradePitch } = require('./upgradePitch');
const { getSupabaseServiceClient } = require('../utils/supabase');

async function scanParlay(user, legs) {
  const supabase = getSupabaseServiceClient();
  const isFree = user.tier === 'free';

  // Scan count check (atomic for free tier)
  if (isFree) {
    if (user.scan_count >= 5) {
      // Already exhausted — return 403 with pitch
      const pitch = await generateUpgradePitch(supabase, user.id, null);
      return {
        blocked: true,
        scan_count: user.scan_count,
        scans_remaining: 0,
        upgrade_pitch: pitch,
      };
    }
  }

  // PERF-2 (Session 7d): analyze legs in parallel. Each call is an
  // independent upstream lookup, so a 6-leg parlay is ~6x faster here
  // than the old sequential loop. allSettled preserves leg order and
  // lets a single failed leg surface as an error stub instead of
  // crashing the whole parlay.
  const settled = await Promise.allSettled(legs.map((leg) => analyzeViaEngine1(leg)));
  const legResults = settled.map((s, i) => {
    if (s.status === 'fulfilled') {
      // Session 58 (work-order 1.5) — a refused read carries grade null;
      // the parlay flow needs a letter per leg, so it takes the existing
      // failed-leg convention (F / 0 confidence) with an honest summary.
      if (s.value && s.value.insufficient_data) {
        return {
          ...s.value,
          grade: 'F',
          insufficient_data: true,
          reasoning: { summary: 'INSUFFICIENT DATA — no read for this leg.' },
        };
      }
      return s.value;
    }
    return {
      ...legs[i],
      error: s.reason?.message || 'analysis_failed',
      grade: 'F',
      confidence: 0,
      reasoning: { summary: 'Analysis failed for this leg.' },
    };
  });

  // Fetch odds data for correlation detection (spreads, game context)
  let spreads = [];
  try {
    const oddsData = await getOdds('nba');
    spreads = oddsData.spreads || [];

    // Attach game context to leg results for correlation detection
    for (const leg of legResults) {
      const matchingProps = (oddsData.props || []).filter(
        (p) => p.player.toLowerCase().includes(leg.player.toLowerCase())
      );
      if (matchingProps.length > 0) {
        const prop = matchingProps[0];
        leg._gameTime = prop.game_time;
        // Resolve team from season avg
        const seasonStep = leg.reasoning?.steps?.season_avg;
        const team = leg._resolvedTeam || null;
        // Use the team from the analysis context
        if (leg.reasoning?.steps?.situational?.home_away?.context === 'home') {
          leg._team = prop.home_team;
        } else if (leg.reasoning?.steps?.situational?.home_away?.context === 'away') {
          leg._team = prop.away_team;
        }
      }
    }
  } catch (_) {
    // Correlation detection is best-effort
  }

  // Detect correlations
  const correlationFlags = detectCorrelations(legResults, spreads);

  // Grade the parlay
  // Attach composite scores from individual analyses for parlay grading
  for (const leg of legResults) {
    // Reconstruct composite from the reasoning steps
    const steps = leg.reasoning?.steps;
    if (steps) {
      const seasonDelta = steps.season_avg?.vs_line || 0;
      const recentDelta = steps.recent_form?.vs_line || 0;
      leg._composite = (Math.abs(seasonDelta) + Math.abs(recentDelta)) / 2;
    } else {
      leg._composite = 0;
    }
  }

  const { grade: parlayGrade, confidence: parlayConfidence } = gradeParlayFromLegs(
    legResults,
    correlationFlags
  );

  // PERF-2 (Session 7d): one batched insert for every leg's pick row
  // instead of N sequential inserts. Supabase preserves insert order in
  // the returned data array so pickIds line up with legResults.
  const pickRows = legResults.map((leg) => ({
    user_id: user.id,
    player: leg.player,
    stat_type: leg.stat_type,
    line: leg.line,
    book: leg.book || 'unknown',
    direction: leg.direction,
    grade: leg.grade,
    edge_pct: leg.edge_pct,
    reasoning: leg.reasoning?.summary || '',
    kill_conditions: (leg.kill_conditions_triggered || []).map((k) => k.code),
    confidence: leg.confidence,
  }));
  let pickIds = [];
  if (pickRows.length > 0) {
    const { data: picksData, error: picksErr } = await supabase
      .from('picks')
      .insert(pickRows)
      .select('id');
    if (picksErr) console.warn('[VYNDR] picks batch insert failed:', picksErr.message);
    pickIds = (picksData || []).map((p) => p.id);
  }

  // Write scan session
  const { data: session } = await supabase
    .from('scan_sessions')
    .insert({
      user_id: user.id,
      legs: pickIds,
      final_grade: parlayGrade,
      kill_conditions: correlationFlags
        .filter((f) => f.impact !== 'positive')
        .map((f) => f.type),
      correlation_notes: JSON.stringify(correlationFlags),
    })
    .select('id')
    .single();

  // Atomic scan count increment for free tier
  let newScanCount = user.scan_count;
  if (isFree) {
    const { data: updated } = await supabase
      .from('users')
      .update({ scan_count: user.scan_count + 1 })
      .eq('id', user.id)
      .eq('scan_count', user.scan_count)
      .select('scan_count')
      .single();

    newScanCount = updated?.scan_count ?? user.scan_count + 1;
  }

  // Build response legs (stripped of internal fields)
  const responseLegs = legResults.map((leg, i) => ({
    index: i,
    player: leg.player,
    stat_type: leg.stat_type,
    line: leg.line,
    direction: leg.direction,
    grade: leg.grade,
    confidence: leg.confidence,
    edge_pct: leg.edge_pct,
    kill_conditions: leg.kill_conditions_triggered || [],
    reasoning_summary: leg.reasoning?.summary || '',
  }));

  // Generate upgrade pitch at scan 5
  let upgradePitch = null;
  if (isFree && newScanCount >= 5) {
    upgradePitch = await generateUpgradePitch(supabase, user.id, {
      grade: parlayGrade,
      legs: responseLegs,
    });
  }

  return {
    blocked: false,
    scan_id: session?.id || null,
    parlay_grade: parlayGrade,
    parlay_confidence: parlayConfidence,
    correlation_flags: correlationFlags,
    legs: responseLegs,
    scan_count: newScanCount,
    scans_remaining: isFree ? Math.max(0, 5 - newScanCount) : null,
    upgrade_pitch: upgradePitch,
  };
}

module.exports = { scanParlay };
