import { NextResponse } from 'next/server';

// Session 17 — Next.js App Router refuses to compile a route that
// exports BOTH `dynamic = 'force-dynamic'` AND `revalidate`. The two
// modes are mutually exclusive: force-dynamic skips static
// generation; revalidate gates ISR. Session 16 shipped both, which
// silently broke the route at build time (production audit found a
// hard 404 on /api/hero-prop).
//
// We keep `force-dynamic` (we want the random-prop pick to vary per
// request) and emit the 15-minute cache via the response's
// Cache-Control header — Coolify's reverse proxy honors it.
export const dynamic = 'force-dynamic';

const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:3000';
const HERO_FETCH_TIMEOUT_MS = 6000;

interface OddsResponseProp {
  player: string;
  stat_type: string;
  line: number;
  direction?: 'over' | 'under';
  book?: string;
  home_team?: string;
  away_team?: string;
}

interface OddsResponse {
  sport?: string;
  source?: string;
  props?: OddsResponseProp[];
  error?: string;
}

interface GradeResponse {
  grade?: string;
  confidence?: number;
  edge_pct?: number;
  projection?: number;
  reasoning?: { summary?: string; steps?: unknown };
  kill_conditions_triggered?: Array<{ code: string; reason: string }>;
}

/**
 * Live hero prop endpoint (Session 16).
 *
 * Picks one fresh, real prop from the day's odds and grades it. The
 * landing page hero renders this in place of the static Jokic mockup
 * — cold visitors see proof of live intelligence on first paint
 * instead of a hypothetical example.
 *
 * Sport cascade: NBA → WNBA → MLB. Whichever sport produces a non-
 * empty `props` list first wins. When every sport is empty (off-
 * hours, holiday slate, upstream odds quota burned), responds with
 * `{ isStatic: true }` and the client falls back to the existing
 * static card. Never throws — odds outages must not blank the
 * landing page.
 *
 * Two-stage flow:
 *   1. GET ${BACKEND}/api/odds/{sport}     → pick random prop
 *   2. POST ${BACKEND}/api/analyze/prop    → grade it
 *
 * Both calls share a 6s timeout (AbortController). The overall
 * route is wrapped in try/catch and always 200s (with `isStatic:true`
 * on failure) so the client renders gracefully.
 */
async function fetchWithTimeout(url: string, init?: RequestInit, ms = HERO_FETCH_TIMEOUT_MS): Promise<Response | null> {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), ms);
  try {
    return await fetch(url, { ...init, signal: controller.signal });
  } catch {
    return null;
  } finally {
    clearTimeout(timer);
  }
}

async function pickPropFromSport(sport: string): Promise<{ prop: OddsResponseProp; sport: string } | null> {
  const res = await fetchWithTimeout(`${BACKEND_URL}/api/odds/${sport}`, {
    method: 'GET',
    headers: { Accept: 'application/json' },
    cache: 'no-store',
  });
  if (!res || !res.ok) return null;
  const body = (await res.json().catch(() => null)) as OddsResponse | null;
  if (!body || !Array.isArray(body.props) || body.props.length === 0) return null;

  // Bias toward A-list player names — props with longer player names
  // tend to be top-of-rotation stars (better hero material). Cheap
  // heuristic, not a hard filter; we still random-pick among the top
  // half of the sorted list so multiple page loads vary.
  const sorted = body.props
    .filter((p) => p.player && p.stat_type && Number.isFinite(p.line))
    .sort((a, b) => (b.player.length - a.player.length));
  if (sorted.length === 0) return null;
  const topHalf = sorted.slice(0, Math.max(3, Math.ceil(sorted.length / 2)));
  const pick = topHalf[Math.floor(Math.random() * topHalf.length)];
  return { prop: pick, sport };
}

async function gradeProp(sport: string, prop: OddsResponseProp): Promise<GradeResponse | null> {
  const res = await fetchWithTimeout(`${BACKEND_URL}/api/analyze/prop`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
    body: JSON.stringify({
      sport,
      player: prop.player,
      stat_type: prop.stat_type,
      line: prop.line,
      direction: prop.direction || 'over',
      book: prop.book || 'draftkings',
    }),
    cache: 'no-store',
  });
  if (!res || !res.ok) return null;
  return (await res.json().catch(() => null)) as GradeResponse | null;
}

export async function GET() {
  // The order matters: NBA props lead because mid-summer the cascade
  // would otherwise constantly land on the same sport. After NBA
  // off-season concludes, swap to a season-aware ordering (winter:
  // NBA, summer: MLB + WNBA, fall: NFL — when supported).
  const sportsToTry = ['nba', 'wnba', 'mlb'];

  try {
    for (const sport of sportsToTry) {
      const picked = await pickPropFromSport(sport);
      if (!picked) continue;
      const grade = await gradeProp(picked.sport, picked.prop);
      if (!grade) continue;
      return NextResponse.json(
        {
          isStatic: false,
          sport: picked.sport,
          prop: picked.prop,
          grade,
        },
        { headers: { 'Cache-Control': 'public, s-maxage=900, stale-while-revalidate=60' } },
      );
    }
  } catch {
    // Falls through to static fallback below.
  }

  // Static fallback — keeps the hero alive when every sport is empty.
  return NextResponse.json(
    {
      isStatic: true,
      sport: 'nba',
      prop: {
        player: 'Nikola Jokic',
        stat_type: 'points',
        line: 26.5,
        direction: 'over',
        book: 'draftkings',
        home_team: 'DEN',
        away_team: 'LAL',
      },
      grade: {
        grade: 'A-',
        confidence: 73,
        edge_pct: 6.2,
        projection: 29.4,
        reasoning: {
          summary: 'L5 form is 28.6 over 5 games, +2.1 above the line. Lakers are bottom-five vs Cs.',
        },
        kill_conditions_triggered: [],
      },
    },
    { headers: { 'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=60' } },
  );
}
