// Live league bridge — any signed-in owner's selected league.
// Draft Live Yes writes that league's picks/teams; dashboard pages read the same league record.

// Live week is frozen until it is over. Mon/Tue/Fri actual uploads
// apply only after LIVE_CURRENT_WEEK advances to the next slate.
// Fallback 4: 2026 weeks 1-3 are in history; hydrate infers max(week)+1.
const LIVE_CURRENT_WEEK = 4;
const LIVE_SEASON_WEEKS = 17;

function liveWeek() {
  if (typeof window.inferLiveCurrentWeekFromLogs === "function") {
    const inferred = Number(window.inferLiveCurrentWeekFromLogs()) || 0;
    if (inferred > 0) {
      window.LIVE_CURRENT_WEEK = inferred;
      return inferred;
    }
  }
  return Number(window.LIVE_CURRENT_WEEK || LIVE_CURRENT_WEEK) || LIVE_CURRENT_WEEK;
}

function playedWeekPoints(player) {
  const logs = typeof window.currentSeasonGameLogs === "function"
    ? window.currentSeasonGameLogs(player)
    : [];
  return logs
    .map((row) => Number(row.ppr ?? row.fantasyPoints))
    .filter((value) => Number.isFinite(value));
}

function playerWeekActual(player, week) {
  const logs = typeof window.currentSeasonGameLogs === "function"
    ? window.currentSeasonGameLogs(player)
    : [];
  const row = logs.find((log) => Number(log.week) === Number(week));
  if (!row) return null;
  const pts = Number(row.ppr ?? row.fantasyPoints);
  return Number.isFinite(pts) ? pts : null;
}

function playerPool() {
  const draft = window.DRAFT_PLAYERS || [];
  if (draft.length) return draft;
  return window.PLAYERS || [];
}

function resolveRosterConfig(league) {
  const fallback = { QB: 1, RB: 2, WR: 2, TE: 1, FLEX: 1, SUPERFLEX: 0, DST: 1, K: 1, BENCH: 3 };
  const stored = league?.rosterConfig && typeof league.rosterConfig === "object" ? league.rosterConfig : {};
  const hasStoredFlex = Object.prototype.hasOwnProperty.call(stored, "FLEX");
  const base = { ...fallback, ...(window.DEFAULT_ROSTER_CONFIG || {}), ...stored };
  if (!hasStoredFlex) base.FLEX = 1;
  const starters = ["QB", "RB", "WR", "TE", "FLEX", "SUPERFLEX", "DST", "K"]
    .reduce((sum, slot) => sum + (Number(base[slot]) || 0), 0);
  const rosterSize = Number(league?.rosterSize);
  if (!Object.prototype.hasOwnProperty.call(stored, "BENCH") && Number.isFinite(rosterSize) && rosterSize > starters) {
    base.BENCH = rosterSize - starters;
  }
  return base;
}

function resolveDraftedPlayer(pick, league) {
  const pool = playerPool();
  const matched = typeof window.findDraftPoolPlayer === "function"
    ? window.findDraftPoolPlayer(pool, pick)
    : null;
  const byId = pick.playerId && pool.find((p) => p.id === pick.playerId);
  const byName = pool.find((p) => (
    p.name === pick.playerName
    && (!pick.playerPosition || p.pos === pick.playerPosition)
  ));
  let player = matched || byId || byName || {
    id: pick.playerId || `pick-${pick.pick}`,
    name: pick.playerName || "Unknown",
    pos: pick.playerPosition || "FLEX",
    team: pick.playerTeam || "FA",
    proj: 0,
    ros: 0,
    adjusted: 0,
    adp: pick.pick || 999,
    risk: "med",
    boom: 0,
    bust: 0,
    traj: [],
    matchup: "TBD",
    matchupRating: 3,
    synthetic: true,
  };
  player = { ...player };
  const week = liveWeek();
  const scoring = league?.scoring || window.LIVE_LEAGUE?.scoring || window.ACTIVE_DRAFT_SCORING || "ppr";
  if (typeof window.applyWeeklyProjectionModel === "function") {
    player = window.applyWeeklyProjectionModel(player, week, scoring);
  }
  return player;
}

function slotAccepts(slot, pos) {
  if (slot === "FLEX") return ["RB", "WR", "TE"].includes(pos);
  if (slot === "SUPERFLEX") return ["QB", "RB", "WR", "TE"].includes(pos);
  if (slot === "BENCH") return true;
  return slot === pos;
}

function assignLiveLineup(players, rosterConfig) {
  const config = rosterConfig && !Array.isArray(rosterConfig)
    ? rosterConfig
    : (window.DEFAULT_ROSTER_CONFIG || { QB: 1, RB: 2, WR: 2, TE: 1, FLEX: 1, DST: 1, K: 1, BENCH: 6 });
  const slots = Array.isArray(rosterConfig)
    ? rosterConfig
    : (typeof window.buildRosterSlots === "function"
      ? window.buildRosterSlots(config)
      : ["QB", "RB", "RB", "WR", "WR", "TE", "FLEX", "DST", "K", "BENCH", "BENCH", "BENCH"]);
  const remaining = (Array.isArray(players) ? players : [])
    .slice()
    .sort((a, b) => (b.adjusted || b.proj || 0) - (a.adjusted || a.proj || 0) || (a.adp || 999) - (b.adp || 999));
  const used = new Set();
  const lineup = [];

  (Array.isArray(slots) ? slots : []).forEach((slot) => {
    const idx = remaining.findIndex((p) => !used.has(p.id) && slotAccepts(slot, p.pos));
    if (idx < 0) {
      lineup.push({ slot, player: null });
      return;
    }
    const player = remaining[idx];
    used.add(player.id);
    lineup.push({ slot, player });
  });

  remaining.forEach((player) => {
    if (used.has(player.id)) return;
    lineup.push({ slot: "BENCH", player });
  });

  return lineup.filter((row) => row.player);
}

function weeklyProj(player, week, scoring) {
  const wk = Number(week) || liveWeek();
  if (typeof window.modeledWeekProjection === "function") {
    return window.modeledWeekProjection(player, wk, scoring);
  }
  return Number(player.adjusted || player.proj || 0) || 0;
}

function buildProjectedWeeks(starters, scoring) {
  const current = liveWeek();
  return Array.from({ length: LIVE_SEASON_WEEKS }, (_, index) => {
    const wk = index + 1;
    const proj = (starters || []).reduce((sum, player) => sum + weeklyProj(player, wk, scoring), 0);
    const played = wk < current;
    let actual = null;
    if (played) {
      actual = (starters || []).reduce((sum, player) => {
        const pts = playerWeekActual(player, wk);
        return sum + (pts == null ? 0 : pts);
      }, 0);
    }
    return {
      wk,
      proj: +proj.toFixed(1),
      oppProj: +(proj * 0.92).toFixed(1),
      played,
      total: played ? +Number(actual || 0).toFixed(1) : +proj.toFixed(1),
    };
  });
}

function buildPositionPerformance(starters) {
  const current = liveWeek();
  const groups = { QB: [], RB: [], WR: [], TE: [], FLEX: [], DST: [], K: [] };
  starters.forEach((row) => {
    const pos = row.slot === "FLEX" || row.slot === "SUPERFLEX" ? row.slot : row.player.pos;
    if (!groups[pos]) groups[pos] = [];
    const weeks = playedWeekNumbers(row.player);
    const played = playedWeekPoints(row.player);
    const actual = played.length
      ? played.reduce((a, b) => a + b, 0) / played.length
      : weeklyProj(row.player, current);
    const expectedWeeks = weeks.length
      ? weeks.map((wk) => weeklyProj(row.player, wk))
      : [weeklyProj(row.player, current)];
    groups[pos].push({ actual, expected: expectedWeeks.reduce((a, b) => a + b, 0) / Math.max(1, expectedWeeks.length) });
  });
  const colors = {
    QB: "#F2C94C", RB: "#95F9AE", WR: "#7BD9F4", TE: "#D9A6F4",
    FLEX: "#D9FFE4", DST: "#FF9E5E", K: "#A6B7AC",
  };
  const fallbackExpected = { QB: 18, RB: 12, WR: 12, TE: 8, FLEX: 11, DST: 6, K: 7 };
  return Object.fromEntries(Object.keys(groups).map((pos) => {
    const vals = groups[pos];
    const actual = vals.length ? vals.reduce((a, b) => a + b.actual, 0) / vals.length : 0;
    const expected = vals.length
      ? vals.reduce((a, b) => a + b.expected, 0) / vals.length
      : (fallbackExpected[pos] || 10);
    return [pos, {
      expected: +Number(expected).toFixed(1),
      actual: +Number(actual).toFixed(1),
      starters: vals.length || (["QB", "TE", "FLEX", "DST", "K"].includes(pos) ? 1 : 2),
      slotLabel: pos,
      color: colors[pos] || "#A6B7AC",
    }];
  }));
}

function playedWeekNumbers(player) {
  const logs = typeof window.currentSeasonGameLogs === "function"
    ? window.currentSeasonGameLogs(player)
    : [];
  return logs.map((row) => Number(row.week)).filter(Boolean);
}

function buildStandings(league) {
  const teams = (league.teams || []).map((team, index) => ({
    id: `slot-${team.slot}`,
    name: team.name || `Team ${team.slot}`,
    owner: team.isUserTeam ? "You" : `Manager ${team.slot}`,
    w: 0,
    l: 0,
    pf: 0,
    pa: 0,
    streak: "—",
    rank: index + 1,
    isUser: !!team.isUserTeam,
  }));
  if (!teams.length) {
    return [{
      id: "you",
      name: league.teamName || league.name || "My Team",
      owner: "You",
      w: 0, l: 0, pf: 0, pa: 0, streak: "—", rank: 1, isUser: true,
    }];
  }
  return teams.sort((a, b) => a.rank - b.rank);
}

function nflTeamsFromPlayers(players) {
  return new Set((players || []).map((p) => p.team).filter((t) => t && t !== "FA"));
}

function applyHomeOverrides(game, homeOverrides) {
  const home = game.homeTeam;
  const away = game.awayTeam;
  if (["NYG", "NYJ"].includes(home)) homeOverrides[home] = home;
  if (["LAC", "LAR"].includes(home)) homeOverrides[home] = home;
  if (["LAC", "LAR"].includes(away)) homeOverrides[away] = home;
  if (["NYG", "NYJ"].includes(away)) homeOverrides[away] = home;
}

function playersForTeamSlot(league, slot) {
  return [...(league?.picks || [])]
    .filter((pick) => Number(pick.teamSlot) === Number(slot))
    .map((pick) => resolveDraftedPlayer(pick, league));
}

function opponentSlotForWeek(league, userSlot, week) {
  if (!league || typeof window.loadLeagueScheduleGrid !== "function") return null;
  const grid = window.loadLeagueScheduleGrid(league.id, league.teamCount || league.teams?.length || 12);
  return grid?.[week]?.[userSlot] || grid?.[week]?.[String(userSlot)] || null;
}

function markStake(status, schedule, teams, stake) {
  teams.forEach((team) => {
    const game = schedule.find((g) => g.homeTeam === team || g.awayTeam === team);
    if (!game) {
      status[team] = "off";
      return;
    }
    const apply = (code) => {
      if (stake === "you") status[code] = "you";
      else if (status[code] !== "you") status[code] = stake;
    };
    apply(team);
    if (game.homeTeam) apply(game.homeTeam);
    if (game.awayTeam) apply(game.awayTeam);
  });
}

function buildWeekGameStatus(roster, week = LIVE_CURRENT_WEEK, league = null) {
  const schedule = (window.NFL_SCHEDULE || []).filter((g) => g.week === week);
  const status = {};
  const homeOverrides = {};
  const hasSchedule = !!(league && typeof window.hasLeagueSchedule === "function" && window.hasLeagueSchedule(league.id));

  (window.NFL_CITIES || []).forEach((city) => { status[city.team] = "off"; });

  if (hasSchedule) {
    schedule.forEach((game) => {
      if (game.homeTeam) status[game.homeTeam] = "neutral";
      if (game.awayTeam) status[game.awayTeam] = "neutral";
      applyHomeOverrides(game, homeOverrides);
    });
  } else {
    schedule.forEach((game) => applyHomeOverrides(game, homeOverrides));
  }

  markStake(status, schedule, nflTeamsFromPlayers(roster), "you");

  let opponentRoster = [];
  if (hasSchedule && league) {
    const userSlot = inferUserSlot(league);
    const oppSlot = opponentSlotForWeek(league, userSlot, week);
    if (oppSlot) {
      opponentRoster = playersForTeamSlot(league, oppSlot);
      markStake(status, schedule, nflTeamsFromPlayers(opponentRoster), "opp");
    }
  }

  return { status, homeOverrides, week, hasSchedule, opponentRoster };
}

function emptyHeatmapWeeks() {
  return Array.from({ length: LIVE_SEASON_WEEKS }, (_, index) => ({
    wk: index + 1,
    QB: null, RB1: null, RB2: null, WR1: null, WR2: null, FLEX: null, TE: null, DST: null, K: null,
    total: null, opp: null, w: null, proj: 0, oppProj: 0,
  }));
}

function buildProjectedHeatmapWeeks(lineup, scoring) {
  const current = liveWeek();
  const starters = (lineup || []).filter((row) => row.slot !== "BENCH" && row.player);
  return Array.from({ length: LIVE_SEASON_WEEKS }, (_, index) => {
    const wk = index + 1;
    const played = wk < current;
    const bySlot = {};
    const rbSeen = [];
    const wrSeen = [];
    starters.forEach((row) => {
      const pts = played
        ? (playerWeekActual(row.player, wk) ?? 0)
        : weeklyProj(row.player, wk, scoring);
      if (row.slot === "RB") {
        rbSeen.push(row.player);
        bySlot[rbSeen.length === 1 ? "RB1" : "RB2"] = pts;
        return;
      }
      if (row.slot === "WR") {
        wrSeen.push(row.player);
        bySlot[wrSeen.length === 1 ? "WR1" : "WR2"] = pts;
        return;
      }
      bySlot[row.slot] = pts;
    });
    const proj = starters.reduce((sum, row) => sum + weeklyProj(row.player, wk, scoring), 0);
    const actual = starters.reduce((sum, row) => sum + (playerWeekActual(row.player, wk) ?? 0), 0);
    return {
      wk,
      QB: bySlot.QB ?? null,
      RB1: bySlot.RB1 ?? null,
      RB2: bySlot.RB2 ?? null,
      WR1: bySlot.WR1 ?? null,
      WR2: bySlot.WR2 ?? null,
      FLEX: bySlot.FLEX ?? null,
      TE: bySlot.TE ?? null,
      DST: bySlot.DST ?? null,
      K: bySlot.K ?? null,
      total: played ? +actual.toFixed(1) : +proj.toFixed(1),
      opp: +(proj * 0.92).toFixed(1),
      w: null,
      proj: +proj.toFixed(1),
      oppProj: +(proj * 0.92).toFixed(1),
      played,
    };
  });
}

function normalizeTeamName(name) {
  return String(name || "").trim().toLowerCase().replace(/['’]/g, "").replace(/\s+/g, " ");
}

// User roster identity is the league record, not a specific owner or team nickname:
// 1) the single is_user_team row  2) draft_position  3) exact team name on picks.
function resolveUserSlot(league) {
  const teams = league?.teams || [];
  const flagged = teams.filter((team) => team.isUserTeam && Number(team.slot) > 0);
  if (flagged.length === 1) return Number(flagged[0].slot);
  const pos = Number(league?.draftPosition);
  if (pos > 0) return pos;
  if (flagged[0]?.slot) return Number(flagged[0].slot);
  return 1;
}

function inferUserSlot(league) {
  return resolveUserSlot(league);
}

function userSlotForLeague(league) {
  return resolveUserSlot(league);
}

function picksForUserTeam(league) {
  const picks = [...(league?.picks || [])];
  const teams = league?.teams || [];
  const slot = resolveUserSlot(league);
  const bySlot = picks.filter((pick) => Number(pick.teamSlot) === Number(slot));
  if (bySlot.length) return { slot, picks: bySlot };
  const names = [
    league?.teamName,
    teams.find((team) => team.isUserTeam)?.name,
    teams.find((team) => Number(team.slot) === Number(slot))?.name,
  ].map(normalizeTeamName).filter(Boolean);
  const byName = picks.filter((pick) => names.includes(normalizeTeamName(pick.teamName)));
  if (byName.length) {
    return { slot: Number(byName[0].teamSlot) || slot, picks: byName };
  }
  return { slot, picks: bySlot };
}

function buildLeagueRosters(league) {
  const rosterConfig = resolveRosterConfig(league);
  return (league.teams || []).map((team) => {
    const roster = playersForTeamSlot(league, team.slot);
    return {
      slot: team.slot,
      name: team.name || `Team ${team.slot}`,
      isUser: !!team.isUserTeam,
      roster,
      lineup: assignLiveLineup(roster, rosterConfig),
    };
  });
}

function utilizationSplit(starters, bench, pos = "ALL") {
  const filterPos = (player) => pos === "ALL" || player.pos === pos;
  const sPts = Math.round(starters.filter(filterPos).reduce((sum, p) => sum + (p.ros || weeklyProj(p) * 17), 0));
  const bPts = Math.round(bench.filter(filterPos).reduce((sum, p) => sum + (p.ros || weeklyProj(p) * 17), 0));
  const total = Math.max(1, sPts + bPts);
  return {
    starters: Math.round((sPts / total) * 100),
    bench: Math.round((bPts / total) * 100),
    sLabel: "Starters",
    bLabel: "Bench potential",
    sPts,
    bPts,
  };
}

function buildLiveLeagueContext(league) {
  if (!league) return null;
  const { slot: userSlot, picks: userPicksRaw } = picksForUserTeam(league);
  const userPicks = [...userPicksRaw].sort((a, b) => (a.pick || 0) - (b.pick || 0));

  const currentWeek = liveWeek();
  const roster = userPicks.map((pick) => {
    const player = resolveDraftedPlayer(pick, league);
    const schedule = (window.NFL_SCHEDULE || []).filter((g) => g.week === currentWeek);
    const game = schedule.find((g) => g.homeTeam === player.team || g.awayTeam === player.team);
    let matchup = player.matchup || "TBD";
    if (game) {
      matchup = game.homeTeam === player.team
        ? `vs ${game.awayTeam}`
        : `@ ${game.homeTeam}`;
    }
    return {
      ...player,
      matchup,
      draft: {
        pick: pick.pick,
        roundPick: typeof pick.roundPick === "string"
          ? pick.roundPick
          : (window.formatRoundPick ? window.formatRoundPick(Math.max(0, (pick.pick || 1) - 1), league.teamCount || 12) : String(pick.pick)),
        teamSlot: pick.teamSlot,
      },
    };
  });

  const rosterConfig = resolveRosterConfig(league);
  const scoring = league.scoring || "ppr";
  const lineup = assignLiveLineup(roster, rosterConfig);
  const slotById = Object.fromEntries(lineup.filter((row) => row.player).map((row) => [row.player.id, row.slot]));
  roster.forEach((player) => { player.lineupSlot = slotById[player.id] || "BENCH"; });
  const starters = lineup.filter((row) => row.slot !== "BENCH").map((row) => row.player);
  const bench = lineup.filter((row) => row.slot === "BENCH").map((row) => row.player);
  const starterIds = starters.map((p) => p.id);
  const benchIds = bench.map((p) => p.id);
  const rosterIds = roster.map((p) => p.id);
  const projectedWeeks = buildProjectedWeeks(starters, scoring);
  const weekGames = buildWeekGameStatus(roster, currentWeek, league);
  const weekProj = projectedWeeks[currentWeek - 1]?.proj || projectedWeeks[0]?.proj || 0;
  const leagueRosters = buildLeagueRosters(league);
  const hasSchedule = !!weekGames.hasSchedule;
  const pf = projectedWeeks.filter((week) => week.played).reduce((sum, week) => sum + (Number(week.total) || 0), 0);

  return {
    live: true,
    leagueId: league.id,
    leagueName: league.name,
    teamName: league.teamName || (league.teams || []).find((t) => t.isUserTeam)?.name || "My Team",
    teamCount: league.teamCount || (league.teams || []).length || 12,
    scoring: league.scoring || "ppr",
    rosterConfig,
    userSlot,
    currentWeek,
    picks: userPicks,
    roster,
    starters,
    bench,
    lineup,
    rosterIds,
    starterIds,
    benchIds,
    teamPositionWeeks: buildProjectedHeatmapWeeks(lineup, scoring),
    teamFutureWeeks: projectedWeeks,
    leagueRosters,
    opponentRoster: weekGames.opponentRoster || [],
    hasLeagueSchedule: hasSchedule,
    positionPerformance: buildPositionPerformance(lineup.filter((row) => row.slot !== "BENCH")),
    standings: buildStandings(league),
    utilization: {
      ALL: utilizationSplit(starters, bench, "ALL"),
      QB: utilizationSplit(starters, bench, "QB"),
      RB: utilizationSplit(starters, bench, "RB"),
      WR: utilizationSplit(starters, bench, "WR"),
      TE: utilizationSplit(starters, bench, "TE"),
    },
    weekGameStatus: weekGames.status,
    weekHomeOverrides: weekGames.homeOverrides,
    headline: {
      record: "0–0",
      rank: standingsRank(league, userSlot),
      pf: +pf.toFixed(1),
      edge: 0,
      weekLabel: `Week ${currentWeek}`,
      projectedTotal: weekProj,
    },
  };
}

function standingsRank(league, userSlot) {
  const teams = league.teams || [];
  const idx = teams.findIndex((team) => team.slot === userSlot);
  return idx >= 0 ? idx + 1 : 1;
}

function restoreDemoGlobals() {
  const seed = window.SEEDED_PROTOTYPE_DATA || {};
  window.LIVE_LEAGUE = null;
  if (seed.ROSTER_IDS) window.ROSTER_IDS = seed.ROSTER_IDS.slice();
  if (seed.STARTER_IDS) window.STARTER_IDS = seed.STARTER_IDS.slice();
  if (seed.BENCH_IDS) window.BENCH_IDS = seed.BENCH_IDS.slice();
  if (seed.TEAM_POSITION_WEEKS) window.TEAM_POSITION_WEEKS = seed.TEAM_POSITION_WEEKS.slice();
  if (seed.TEAM_FUTURE_WEEKS) window.TEAM_FUTURE_WEEKS = seed.TEAM_FUTURE_WEEKS.slice();
  if (seed.POSITION_PERFORMANCE) window.POSITION_PERFORMANCE = { ...seed.POSITION_PERFORMANCE };
  if (seed.LEAGUE_STANDINGS) window.LEAGUE_STANDINGS = seed.LEAGUE_STANDINGS.slice();
  if (seed.WEEK11_GAMES) {
    window.WEEK11_GAMES = { ...seed.WEEK11_GAMES };
    window.WEEK_GAMES = { ...seed.WEEK11_GAMES };
  }
  window.CURRENT_WEEK = 11;
  window.WEEK_HOME_OVERRIDES = { ...(window.WEEK11_HOME_AT_SHARED || {}) };
  if (typeof window.buildMapMarkers === "function" && window.WEEK11_GAMES) {
    window.NFL_MAP_MARKERS = window.buildMapMarkers(window.WEEK11_GAMES, window.WEEK_HOME_OVERRIDES);
  }
}

function publishLiveLeagueContext(ctx) {
  if (!ctx) {
    restoreDemoGlobals();
    window.dispatchEvent(new CustomEvent("live-league", { detail: null }));
    return null;
  }
  window.LIVE_LEAGUE = ctx;
  window.ROSTER_IDS = (ctx.rosterIds || []).slice();
  window.STARTER_IDS = (ctx.starterIds || []).slice();
  window.BENCH_IDS = (ctx.benchIds || []).slice();
  window.TEAM_POSITION_WEEKS = (ctx.teamPositionWeeks || []).slice();
  window.TEAM_FUTURE_WEEKS = (ctx.teamFutureWeeks || []).slice();
  window.POSITION_PERFORMANCE = { ...(ctx.positionPerformance || {}) };
  window.LEAGUE_STANDINGS = (ctx.standings || []).slice();
  window.CURRENT_WEEK = ctx.currentWeek;
  window.WEEK_GAMES = { ...ctx.weekGameStatus };
  window.WEEK11_GAMES = { ...ctx.weekGameStatus };
  window.WEEK_HOME_OVERRIDES = { ...ctx.weekHomeOverrides };
  window.NFL_MAP_MARKERS = typeof window.buildMapMarkers === "function"
    ? window.buildMapMarkers(ctx.weekGameStatus, ctx.weekHomeOverrides)
    : window.NFL_MAP_MARKERS;
  window.dispatchEvent(new CustomEvent("live-league", { detail: ctx }));
  return ctx;
}

function useLiveLeague(league) {
  const nflStatus = window.useNflDataStatus ? window.useNflDataStatus() : {};
  const [scheduleTick, setScheduleTick] = React.useState(0);
  const [ctx, setCtx] = React.useState(() => {
    if (!league) return null;
    return window.LIVE_LEAGUE?.leagueId === league.id ? window.LIVE_LEAGUE : null;
  });

  React.useEffect(() => {
    const onSchedule = () => setScheduleTick((n) => n + 1);
    window.addEventListener("nfl-schedule", onSchedule);
    window.addEventListener("league-schedule", onSchedule);
    return () => {
      window.removeEventListener("nfl-schedule", onSchedule);
      window.removeEventListener("league-schedule", onSchedule);
    };
  }, []);

  React.useEffect(() => {
    // Logged out / no league → keep demo Vector Reapers surfaces.
    if (!league) {
      if (window.LIVE_LEAGUE) publishLiveLeagueContext(null);
      setCtx(null);
      return;
    }
    try {
      const next = buildLiveLeagueContext(league);
      publishLiveLeagueContext(next);
      setCtx(next);
    } catch (error) {
      console.warn("Live league context failed", error);
      const fallback = {
        live: true,
        leagueId: league.id,
        leagueName: league.name,
        teamName: league.teamName || "My Team",
        roster: [],
        rosterIds: [],
        starters: [],
        bench: [],
        lineup: [],
        picks: league.picks || [],
        headline: { record: "0–0", rank: 1, pf: 0, projectedTotal: 0 },
      };
      publishLiveLeagueContext(fallback);
      setCtx(fallback);
    }
  }, [
    league?.id,
    league?.teamName,
    league?.draftPosition,
    league?.picks?.length,
    league?.teams?.length,
    JSON.stringify(league?.rosterConfig || {}),
    nflStatus.projections,
    nflStatus.weeklyLogsLoaded,
    nflStatus.liveCurrentWeek,
    scheduleTick,
    (window.NFL_SCHEDULE || []).length,
  ]);

  React.useEffect(() => {
    const sync = (event) => setCtx(event.detail || null);
    window.addEventListener("live-league", sync);
    return () => window.removeEventListener("live-league", sync);
  }, []);

  return ctx;
}

Object.assign(window, {
  LIVE_CURRENT_WEEK: typeof window.LIVE_CURRENT_WEEK === "number" ? window.LIVE_CURRENT_WEEK : LIVE_CURRENT_WEEK,
  liveWeek,
  buildLiveLeagueContext,
  publishLiveLeagueContext,
  restoreDemoGlobals,
  opponentSlotForWeek,
  playersForTeamSlot,
  userSlotForLeague,
  inferUserSlot,
  resolveUserSlot,
  resolveRosterConfig,
  useLiveLeague,
});
