// src/PlayerContext.jsx
import React, { createContext, useState, useEffect, useRef, useCallback, useMemo } from 'react';

export const PlayerContext = createContext();
export const FixturesContext = createContext({});

export const PlayerProvider = ({ children }) => {
  const globalFixturesRef = useRef({});
  const [originalPlayers, setOriginalPlayers] = useState([]);
  const [globalPlayers, setGlobalPlayers] = useState([]);
  const [globalFixtures, setGlobalFixtures] = useState({});
  const [isLoadingDB, setIsLoadingDB] = useState(true);

  const [teamId, setTeamId] = useState('');
  const [availableGWs, setAvailableGWs] = useState([]);
  const [itb, setItb] = useState(0);
  const [availableFts, setAvailableFts] = useState(1);
  const [initialSquadIds, setInitialSquadIds] = useState([]);
  const [solverResult, setSolverResult] = useState(null);
  const [activeChip, setActiveChip] = useState(null);
  const [solveElapsedSec, setSolveElapsedSec] = useState(0);
  const [numSims, setNumSims] = useState(100);
  const HIT_COST = 4;

  const [baselineItb, setBaselineItb] = useState(0);
  const [baselineFt, setBaselineFt] = useState(1);
  const [comprehensiveSettings, setComprehensiveSettings] = useState({});
  const [globalXmins, setGlobalXmins] = useState({});


  const [quickSettings, setQuickSettings] = useState({
    decay: 0.85,
    ft_value: 1.5,
    iterations: 1,
    banned: [],
    locked: [],
  });

  const [advancedSettings, setAdvancedSettings] = useState({
    hit_cost: 4,
    itb_value: 0.08,
    max_per_team: 3,
    vice_weight: 0.05,
    time_limit_sec: 30,
    no_transfer_last_gws: 0
  });

  // FIXED: FPL 24/25 FT Rollover Logic
  const ftAtStartOfGw = (targetGw, gwsArray, baseFt, trByGw, chByGw) => {
    if (!gwsArray || !gwsArray.length) return baseFt;
    let ft = baseFt;
    for (let gw = gwsArray[0]; gw < targetGw; gw++) {
      const chip = chByGw[gw];
      if (chip === 'wc' || chip === 'fh') {
        ft = Math.min(5, ft);
      } else {
        const used = trByGw[gw]?.count || 0;
        ft = Math.min(5, Math.max(0, ft - used) + 1);
      }
    }
    return Math.max(1, Math.min(5, ft));
  };

  const itbAtStartOfGw = (targetGw, gwsArray, baseItb, trByGw) => {
    let currentItb = baseItb;
    if (!gwsArray || !gwsArray.length) return currentItb;
    for (let gw = gwsArray[0]; gw < targetGw; gw++) {
      currentItb += (trByGw[gw]?.netDelta || 0);
    }
    return currentItb;
  };

  // =========================================================
  // --- MULTIVERSE DRAFTS ENGINE (Phase 1) ---
  // =========================================================
  const [drafts, setDrafts] = useState([{
    id: "main_1",
    name: "Main Timeline",
    teamData: [],
    horizon: 5,
    activeGW: null,
    captainId: null,
    viceId: null,
    solverTransferPairs: {},
    solverApplySnapshot: null,
    appliedPlanSummary: null,
    hitsThisGw: 0,
    highlightTransferIds: {},
    transfersByGw: {},
    chipsByGw: {},
    manualOverrides: {},
    fixtureOverrides: {}, // <-- ADDED: Isolated Fixtures
    sessionEdits: {}      // <-- ADDED: Isolated Minutes
  }]);

  const [activeDraftId, setActiveDraftId] = useState("main_1");

  // 1. EXTRACT CURRENT REALITY
  const activeDraft = drafts.find(d => d.id === activeDraftId) || drafts[0];

  const teamData = activeDraft.teamData;
  const horizon = activeDraft.horizon;
  const activeGW = activeDraft.activeGW;
  const captainId = activeDraft.captainId;
  const viceId = activeDraft.viceId;
  const solverTransferPairs = activeDraft.solverTransferPairs || {};
  const solverApplySnapshot = activeDraft.solverApplySnapshot;
  const appliedPlanSummary = activeDraft.appliedPlanSummary;
  const hitsThisGw = activeDraft.hitsThisGw;
  const highlightTransferIds = activeDraft.highlightTransferIds || {};
  const transfersByGw = activeDraft.transfersByGw || {};
  const chipsByGw = activeDraft.chipsByGw || {};

  // Safe extraction fallbacks for older local cache hits
  const manualOverrides = activeDraft.manualOverrides || {};
  const fixtureOverrides = activeDraft.fixtureOverrides || {};
  const sessionEdits = activeDraft.sessionEdits || {};

  const effectiveFixtures = useMemo(() => {
    return { ...globalFixtures, ...(fixtureOverrides || {}) };
  }, [globalFixtures, fixtureOverrides]);

  // 2. PROXY SETTERS (Intercepts state calls and routes them to the active draft)
  const updateDraftState = useCallback((key, newValue) => {
    setDrafts(prevDrafts => {
      const activeIndex = prevDrafts.findIndex(d => d.id === activeDraftId);
      if (activeIndex === -1) return prevDrafts;

      const draft = prevDrafts[activeIndex];
      // Provide an empty object fallback for expected object states to prevent functional crashes
      const currentValue = draft[key] !== undefined ? draft[key] : (key === 'teamData' || key === 'availableGWs' ? [] : {});
      const evaluatedValue = typeof newValue === 'function' ? newValue(currentValue) : newValue;

      const newDrafts = [...prevDrafts];
      newDrafts[activeIndex] = { ...draft, [key]: evaluatedValue };
      return newDrafts;
    });
  }, [activeDraftId]);

  const setTeamData = useCallback((val) => updateDraftState("teamData", val), [updateDraftState]);
  const setHorizon = useCallback((val) => updateDraftState("horizon", val), [updateDraftState]);
  const setActiveGW = useCallback((val) => updateDraftState("activeGW", val), [updateDraftState]);
  const setCaptainId = useCallback((val) => updateDraftState("captainId", val), [updateDraftState]);
  const setViceId = useCallback((val) => updateDraftState("viceId", val), [updateDraftState]);
  const setSolverTransferPairs = useCallback((val) => updateDraftState("solverTransferPairs", val), [updateDraftState]);
  const setSolverApplySnapshot = useCallback((val) => updateDraftState("solverApplySnapshot", val), [updateDraftState]);
  const setAppliedPlanSummary = useCallback((val) => updateDraftState("appliedPlanSummary", val), [updateDraftState]);
  const setHitsThisGw = useCallback((val) => updateDraftState("hitsThisGw", val), [updateDraftState]);
  const setHighlightTransferIds = useCallback((val) => updateDraftState("highlightTransferIds", val), [updateDraftState]);
  const setTransfersByGw = useCallback((val) => updateDraftState("transfersByGw", val), [updateDraftState]);
  const setChipsByGw = useCallback((val) => updateDraftState("chipsByGw", val), [updateDraftState]);
  const setFixtureOverrides = useCallback((val) => updateDraftState("fixtureOverrides", val), [updateDraftState]); // <-- GHOST PATCH
  const setSessionEdits = useCallback((val) => updateDraftState("sessionEdits", val), [updateDraftState]);         // <-- GHOST PATCH
  // =========================================================

  const manualOverridesRef = useRef(manualOverrides);
  useEffect(() => { manualOverridesRef.current = manualOverrides; }, [manualOverrides]);

  const [projSearchTerm, setProjSearchTerm] = useState('');
  const sessionEditsRef = useRef(sessionEdits);
  useEffect(() => { sessionEditsRef.current = sessionEdits; }, [sessionEdits]);

  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [isCheckingAuth, setIsCheckingAuth] = useState(true);
  const [userProfile, setUserProfile] = useState({ username: "Guest", defaultTeamId: null, isAdmin: false });
  const [hasGuestMadeEdits, setHasGuestMadeEdits] = useState(false);
  const [pendingWorkspaceLoad, setPendingWorkspaceLoad] = useState(null);

  // Custom proxy setter for manualOverrides to retain your exact Auth saving logic
  const setManualOverrides = useCallback((updater) => {
    updateDraftState("manualOverrides", (prev) => {
      const next = typeof updater === 'function' ? updater(prev) : updater;
      const token = localStorage.getItem('fpl_token');
      if (token) {
        fetch('https://anayshukla-fpl-solver.hf.space/api/auth/save_session', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
          body: JSON.stringify({ saved_edits: { ...sessionEditsRef.current, _solver_overrides: next } })
        });
      }
      return next;
    });
  }, [updateDraftState]);

  const saveSession = (overrides) => {
    const token = localStorage.getItem('fpl_token');
    if (token) {
      fetch('https://anayshukla-fpl-solver.hf.space/api/auth/save_session', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
        body: JSON.stringify({
          saved_edits: { ...sessionEditsRef.current, _solver_overrides: overrides },
          drafts: drafts
        })
      });
    }
  };

  useEffect(() => {
    // YOUR EXACT PROJECTIONS API ENDPOINT RESTORED
    const cachedProjections = localStorage.getItem('fpl_projections_cache');
    if (cachedProjections) {
      try {
        const parsed = JSON.parse(cachedProjections);
        setOriginalPlayers(JSON.parse(JSON.stringify(parsed)));
        setGlobalPlayers(parsed);
        setIsLoadingDB(false); // Instantly drop the skeleton loader!
      } catch (e) {
        console.error("Cache corrupted, waiting for server...");
      }
    }

    // 2. Silently fetch the fresh data in the background
    fetch('https://anayshukla-fpl-solver.hf.space/api/projections')
      .then(res => { if (!res.ok) throw new Error("DB Error"); return res.json(); })
      .then(data => {
        localStorage.setItem('fpl_projections_cache', JSON.stringify(data)); // Save for next time
        setOriginalPlayers(JSON.parse(JSON.stringify(data)));
        setGlobalPlayers(data);
        setIsLoadingDB(false); // Drop loader if cache was empty
      })
      .catch(err => { if (!cachedProjections) setIsLoadingDB(false); });

    // THE FIX: Store global fixtures in the Ref so the Auth wipe doesn't delete them!
    fetch('https://anayshukla-fpl-solver.hf.space/api/fixtures/overrides')
      .then(res => res.ok ? res.json() : {})
      .then(data => {
        if (Object.keys(data).length > 0) setGlobalFixtures(data);
      })
      .catch(err => console.error("Failed to load global fixtures:", err));

    fetch('https://anayshukla-fpl-solver.hf.space/api/xmins/overrides')
      .then(res => res.ok ? res.json() : {})
      .then(data => { if (Object.keys(data).length > 0) setGlobalXmins(data); })
      .catch(err => console.error("Failed to load global xMins:", err));
  }, []);

  useEffect(() => {
    const token = localStorage.getItem('fpl_token');
    if (token) {
      fetch('https://anayshukla-fpl-solver.hf.space/api/auth/me', { headers: { 'Authorization': `Bearer ${token}` } })
        .then(res => res.json())
        .then(data => {
          if (data.email) {
            setUserProfile({ username: data.email.split('@')[0], defaultTeamId: data.default_team_id, isAdmin: data.is_admin });

            // THE FIX: Inject the Multiverse realities from the database!
            if (data.drafts && data.drafts.length > 0) {
              setDrafts(data.drafts);
              const saved = data.saved_edits || {};
              if (saved._active_draft_id && data.drafts.some(d => d.id === saved._active_draft_id)) {
                setActiveDraftId(saved._active_draft_id);
              } else {
                setActiveDraftId(data.drafts[0].id);
              }
            } else {
              // ONLY run these legacy injectors if the Multiverse doesn't exist yet!
              // Otherwise, they use a stale React closure and overwrite Draft A with Draft B's moves!
              const saved = data.saved_edits || {};
              if (saved._solver_overrides) {
                setManualOverrides(saved._solver_overrides);
                delete saved._solver_overrides;
              }
              if (saved._workspace) {
                setPendingWorkspaceLoad(saved._workspace);
                delete saved._workspace;
              }
              setSessionEdits(saved);
            }

            setIsLoggedIn(true);
            if (data.default_team_id) setTeamId(String(data.default_team_id));
          } else {
            localStorage.removeItem('fpl_token');
            setSessionEdits({});
            setManualOverrides({});
            setTeamId('');
            setTeamData([]);
            setDrafts([{
              id: "main_1",
              name: "Main Timeline",
              teamData: [], horizon: 5, activeGW: null, captainId: null, viceId: null,
              solverTransferPairs: {}, solverApplySnapshot: null, appliedPlanSummary: null,
              hitsThisGw: 0, highlightTransferIds: {}, transfersByGw: {}, chipsByGw: {},
              manualOverrides: {}, fixtureOverrides: {}, sessionEdits: {}
            }]);
            setActiveDraftId("main_1");
          }
        }).catch(() => {
          localStorage.removeItem('fpl_token');
          setSessionEdits({});
          setManualOverrides({});
          setTeamId('');
          setTeamData([]);
          setDrafts([{
            id: "main_1",
            name: "Main Timeline",
            teamData: [], horizon: 5, activeGW: null, captainId: null, viceId: null,
            solverTransferPairs: {}, solverApplySnapshot: null, appliedPlanSummary: null,
            hitsThisGw: 0, highlightTransferIds: {}, transfersByGw: {}, chipsByGw: {},
            manualOverrides: {}, fixtureOverrides: {}, sessionEdits: {}
          }]);
          setActiveDraftId("main_1");
        }).finally(() => {
          setIsCheckingAuth(false);
        });
    } else {
      setSessionEdits({});
      setManualOverrides({});
      setTeamId('');
      setTeamData([]);
      setIsCheckingAuth(false);
    }
  }, [isLoggedIn]);

  useEffect(() => {
    if (originalPlayers.length > 0) {
      setGlobalPlayers(prev => {
        const newPlayers = JSON.parse(JSON.stringify(originalPlayers));

        // --- 1. THE STOCHASTIC FIXTURE ENGINE ---
        newPlayers.forEach(p => {
          if (p.match_projections) {
            // A. Zero out old stats + Track probability sum for averaging
            const gwKeys = Object.keys(p).filter(k => k.includes('_Pts')).map(k => k.split('_')[0]);
            gwKeys.forEach(gw => {
              p[`${gw}_Pts`] = 0; p[`${gw}_xMins`] = 0; p[`${gw}_xG`] = 0; p[`${gw}_xA`] = 0; p[`${gw}_CS`] = 0;
              p[`${gw}_probSum`] = 0; // NEW: Tracks total matches in this GW
            });

            // B. Loop matches and apply Match-Level Minute Edits
            const manualBaseline = sessionEdits[p.ID]?.baseline_xMins;
            const origBaseline = p.baseline_xMins || 90;
            const baselineScale = (manualBaseline != null && origBaseline > 0) ? (Number(manualBaseline) / origBaseline) : 1.0;

            if (!p.match_projections || typeof p.match_projections !== 'object' || Array.isArray(p.match_projections)) return;

            Object.entries(p.match_projections).forEach(([matchId, mData]) => {
              const override = effectiveFixtures[matchId];

              let manualMins = sessionEdits[p.ID]?.[`${matchId}_xMins`];
              let globalMatchMins = globalXmins[p.ID]?.[matchId];
              let isGwOverride = false;

              if (manualMins === undefined) {
                if (globalMatchMins !== undefined) {
                  manualMins = globalMatchMins;
                } else {
                  let activeGw = override ? Object.keys(override).find(g => override[g] > 0) : mData.default_gw;
                  if (activeGw) {
                    manualMins = sessionEdits[p.ID]?.[`${activeGw}_xMins`] ?? globalXmins?.[p.ID]?.[activeGw];
                    if (manualMins !== undefined) isGwOverride = true;
                  }
                }
              }

              // THE FIX: Safely extract data so NaN never infects the app!
              const origMins = mData.xMins !== undefined ? mData.xMins : (mData.mins || 0);
              const origPts = mData.Pts !== undefined ? mData.Pts : (mData.points || 0);

              let activeMins = origMins;
              if (manualMins != null) {
                if (isGwOverride) {
                  // DISTRIBUTE LEAK FIX: If an admin edited a whole GW, distribute it proportionally across the DGW splits!
                  let totalGwMins = 0;
                  Object.values(p.match_projections).forEach(m => {
                    if (m.default_gw === mData.default_gw) totalGwMins += (m.xMins !== undefined ? m.xMins : (m.mins || 0));
                  });
                  const ratio = totalGwMins > 0 ? (origMins / totalGwMins) : 1;
                  activeMins = Math.min(Number(manualMins) * ratio, 90);
                } else {
                  activeMins = Number(manualMins);
                }
              } else {
                activeMins = Math.min((origMins * baselineScale), 90);
              }

              // Scale the match EV safely
              const scaling = (activeMins > 0 && origMins > 0) ? (activeMins / origMins) : 0;
              const aPts = origPts * scaling;
              const axG = (mData.xG || 0) * scaling;
              const axA = (mData.xA || 0) * scaling;
              const aCS = (mData.CS || 0) * scaling;

              // C. Distribute the scaled EV
              if (override) {
                Object.entries(override).forEach(([gwStr, prob]) => {
                  const gw = gwStr;
                  p[`${gw}_Pts`] += (aPts * prob);
                  p[`${gw}_xMins`] += (activeMins * prob);
                  p[`${gw}_probSum`] += prob; // Add probability to the GW sum
                  p[`${gw}_xG`] += (axG * prob); p[`${gw}_xA`] += (axA * prob); p[`${gw}_CS`] += (aCS * prob);
                });
              } else {
                const gw = mData.default_gw;
                p[`${gw}_Pts`] += aPts;
                p[`${gw}_xMins`] += activeMins;
                p[`${gw}_probSum`] += 1.0;
                p[`${gw}_xG`] += axG; p[`${gw}_xA`] += axA; p[`${gw}_CS`] += aCS;
              }
            });

            // D. Calculate FPL Average xMins
            gwKeys.forEach(gw => {
              if (p[`${gw}_probSum`] > 0) {
                p[`${gw}_xMins`] = Math.round(p[`${gw}_xMins`] / p[`${gw}_probSum`]);
              }
            });
          }
        });

        // --- 2. APPLY MANUAL SESSION EDITS (Overwrites everything) ---
        if (Object.keys(sessionEdits).length > 0) {
          Object.keys(sessionEdits).forEach(playerId => {
            if (playerId === '_solver_overrides') return;

            const pid = parseInt(playerId);
            const pIdx = newPlayers.findIndex(p => p.ID === pid);
            if (pIdx > -1) {
              const edits = sessionEdits[playerId];
              Object.keys(edits).forEach(editKey => {
                newPlayers[pIdx][editKey] = edits[editKey];
                if (editKey.includes('_xMins') && !editKey.includes('_vs_')) {
                  const gw = editKey.split('_')[0];
                  if (edits[`${gw}_Pts`] === undefined) {
                    const baseMins = originalPlayers[pIdx][`${gw}_xMins`] || 90;
                    const basePts = originalPlayers[pIdx][`${gw}_Pts`] || 0;
                    newPlayers[pIdx][`${gw}_Pts`] = baseMins > 0 ? (basePts / baseMins) * edits[editKey] : 0;
                  }
                }
              });
            }
          });
        }

        return newPlayers;
      });
    }
  }, [originalPlayers, isLoggedIn, sessionEdits, effectiveFixtures]);

  useEffect(() => {
    if (!isLoggedIn || isLoadingDB || globalPlayers.length === 0) return;

    const timeout = setTimeout(() => {
      const workspace = {
        teamData: teamData.map(p => ({ ID: p.ID, Price: p.Price, isBlank: p.isBlank, replacedPlayer: p.replacedPlayer })),
        horizon,
        activeGW,
        baselineItb,
        baselineFt,
        transfersByGw,
        chipsByGw,
        quickSettings,
        advancedSettings,
        highlightTransferIds: Object.fromEntries(Object.entries(highlightTransferIds).map(([k, v]) => [k, Array.from(v)])),
        solverTransferPairs
      };

      const token = localStorage.getItem('fpl_token');
      if (token) {
        fetch('https://anayshukla-fpl-solver.hf.space/api/auth/save_session', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
          body: JSON.stringify({
            saved_edits: {
              ...sessionEditsRef.current,
              _solver_overrides: manualOverridesRef.current,
              _workspace: workspace,
              _active_draft_id: activeDraftId
            },
            drafts: drafts // <-- THE FIX: Package and send the entire Multiverse!
          })
        });
      }
    }, 500);
    // THE FIX: Added 'drafts' to the end of this dependency array so edits trigger the save
  }, [teamData, horizon, activeGW, baselineItb, baselineFt, transfersByGw, chipsByGw, quickSettings, advancedSettings, highlightTransferIds, solverTransferPairs, isLoggedIn, isLoadingDB, globalPlayers, drafts]);

  useEffect(() => {
    if (globalPlayers.length === 0 || teamData.length === 0) return;

    setTeamData(prevTeam => {
      let needsUpdate = false;

      const syncedTeam = prevTeam.map(tp => {
        const gp = globalPlayers.find(p => p.ID === tp.ID);
        if (!gp) return tp;

        // If the squad's math is out of sync with the global math, flag it for an update!
        // (We check a few sample gameweeks to guarantee we catch the mismatch)
        if (
          tp['1_Pts'] !== gp['1_Pts'] ||
          tp['19_Pts'] !== gp['19_Pts'] ||
          tp['38_Pts'] !== gp['38_Pts'] ||
          tp.baseline_xMins !== gp.baseline_xMins
        ) {
          needsUpdate = true;
          return { ...tp, ...gp, Price: tp.Price }; // Merges the math, but protects your specific Selling Price!
        }
        return tp;
      });

      // Only triggers a re-render if it actually found stale data
      return needsUpdate ? syncedTeam : prevTeam;
    });
  }, [globalPlayers, teamData, setTeamData]);

  const updatePlayerStat = (playerId, gw, statKey, rawValue) => {
    let finalValue = statKey === 'xMins' ? Math.min(Math.max(Number(rawValue), 0), 90) : rawValue;
    if (!isLoggedIn) setHasGuestMadeEdits(true);

    const pristinePlayer = originalPlayers.find(p => p.ID === playerId);
    let calculatedPts = 0;

    // 1. Determine the exact original value to see if we are reverting
    let isRevertingToOriginal = false;
    const isMatchId = String(gw).includes('_vs_');

    // FIX: Look specifically inside match_projections for DGW match edits!
    if (pristinePlayer && pristinePlayer.match_projections && isMatchId) {
      const mData = pristinePlayer.match_projections[gw];
      if (mData) {
        const mOrig = statKey === 'xMins' ? mData.xMins : mData[statKey];
        isRevertingToOriginal = (finalValue === mOrig);
      }
    } else {
      const originalValue = pristinePlayer ? pristinePlayer[`${gw}_${statKey}`] : undefined;
      if (originalValue !== undefined) {
        isRevertingToOriginal = (finalValue === originalValue);
      } else if (statKey === 'xMins' && finalValue === 90) {
        isRevertingToOriginal = true;
      }
    }

    // Calculate instant EV for UI feedback
    if (statKey === 'xMins') {
      if (pristinePlayer && pristinePlayer.match_projections && isMatchId) {
        const mData = pristinePlayer.match_projections[gw];
        const baseMins = mData ? mData.xMins : 90;
        const basePts = mData ? mData.Pts : 0;
        calculatedPts = baseMins > 0 ? (basePts / baseMins) * finalValue : 0;
      } else {
        const baseMins = pristinePlayer ? pristinePlayer[`${gw}_xMins`] || 90 : 90;
        const basePts = pristinePlayer ? pristinePlayer[`${gw}_Pts`] || 0 : 0;
        calculatedPts = baseMins > 0 ? (basePts / baseMins) * finalValue : 0;
      }
    }

    setGlobalPlayers(prev => prev.map(p => {
      if (p.ID === playerId) {
        let updated = { ...p, [`${gw}_${statKey}`]: finalValue };
        if (statKey === 'xMins') updated[`${gw}_Pts`] = calculatedPts;
        return updated;
      }
      return p;
    }));

    setTeamData(prev => prev.map(p => {
      if (p.ID === playerId) {
        let updated = { ...p, [`${gw}_${statKey}`]: finalValue };
        if (statKey === 'xMins') updated[`${gw}_Pts`] = calculatedPts;
        return updated;
      }
      return p;
    }));

    // 2. The Smart Self-Cleaning Session Edits
    setSessionEdits(prev => {
      const newEdits = { ...prev };

      if (isRevertingToOriginal) {
        if (newEdits[playerId]) {
          newEdits[playerId] = { ...newEdits[playerId] };
          delete newEdits[playerId][`${gw}_${statKey}`];
          if (statKey === 'xMins') delete newEdits[playerId][`${gw}_Pts`];

          if (Object.keys(newEdits[playerId]).length === 0) delete newEdits[playerId];
        }
      } else {
        if (!newEdits[playerId]) newEdits[playerId] = {};
        newEdits[playerId] = { ...newEdits[playerId], [`${gw}_${statKey}`]: finalValue };

        if (statKey === 'xMins') {
          // Do not hardcode match points so the stochastic engine can dynamically scale it!
          if (!isMatchId) newEdits[playerId][`${gw}_Pts`] = calculatedPts;
        }
      }

      if (isLoggedIn) {
        fetch('https://anayshukla-fpl-solver.hf.space/api/auth/save_session', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('fpl_token')}` },
          body: JSON.stringify({ saved_edits: { ...newEdits, _solver_overrides: manualOverridesRef.current } })
        });
      }
      return newEdits;
    });
  };

  // --- STOCHASTIC ENGINE INVALIDATION ---
  // If the user changes any fixture odds, the current solver lineup is now mathematically invalid.
  // This instantly clears the stale lineup and forces the UI to reset.
  useEffect(() => {
    if (solverResult) {
      setSolverResult(null);
    }
    if (appliedPlanSummary) {
      setAppliedPlanSummary(null);
    }
  }, [fixtureOverrides]);

  return (
    <PlayerContext.Provider value={{
      globalPlayers, setGlobalPlayers, isLoadingDB, updatePlayerStat, teamId, setTeamId, teamData, setTeamData,
      availableGWs, setAvailableGWs, horizon, setHorizon, activeGW, setActiveGW, captainId, setCaptainId,
      viceId, setViceId, itb, setItb, availableFts, setAvailableFts, initialSquadIds, setInitialSquadIds,
      solverResult, setSolverResult, activeChip, setActiveChip, manualOverrides, setManualOverrides, isLoggedIn,
      setIsLoggedIn, userProfile, setUserProfile, hasGuestMadeEdits, setHasGuestMadeEdits, projSearchTerm, setProjSearchTerm,
      sessionEdits, setSessionEdits, highlightTransferIds, setHighlightTransferIds, transfersByGw, setTransfersByGw,
      chipsByGw, setChipsByGw, baselineItb, setBaselineItb, baselineFt, setBaselineFt, quickSettings, setQuickSettings,
      advancedSettings, setAdvancedSettings, solveElapsedSec, setSolveElapsedSec, solverTransferPairs, setSolverTransferPairs,
      solverApplySnapshot, setSolverApplySnapshot, appliedPlanSummary, setAppliedPlanSummary, hitsThisGw, setHitsThisGw,
      numSims, setNumSims, HIT_COST, comprehensiveSettings, setComprehensiveSettings, saveSession, ftAtStartOfGw, itbAtStartOfGw,
      isCheckingAuth, drafts, setDrafts, activeDraftId, setActiveDraftId, fixtureOverrides, setFixtureOverrides, originalPlayers,
      setOriginalPlayers, globalFixtures, setGlobalFixtures, effectiveFixtures, globalXmins
    }}>
      <FixturesContext.Provider value={effectiveFixtures}>
        {children}
      </FixturesContext.Provider>
    </PlayerContext.Provider>
  );
};