
// app-state.jsx — Global state, context, and data fetching
const { createContext, useContext, useState, useEffect, useCallback } = React;

const AppContext = createContext(null);

const POSITIONS = ['TOP', 'JUNGLE', 'MID', 'ADC', 'SUPPORT'];
const POSITION_EMOJIS = {
  TOP:     <window.RoleIcon pos="TOP" size={18} />,
  JUNGLE:  <window.RoleIcon pos="JUNGLE" size={18} />,
  MID:     <window.RoleIcon pos="MID" size={18} />,
  ADC:     <window.RoleIcon pos="ADC" size={18} />,
  SUPPORT: <window.RoleIcon pos="SUPPORT" size={18} />,
};
const POSITION_LABELS = { TOP: 'Top', JUNGLE: 'Jungle', MID: 'Mid', ADC: 'ADC', SUPPORT: 'Support' };
const POSITION_COLORS = {
  TOP: '#F7DFA0', JUNGLE: '#B8E0C0', MID: '#C5B4E3',
  ADC: '#F2A7C3', SUPPORT: '#AED6F1'
};
const FLOWER_COLORS = {
  TOP: '🌻', JUNGLE: '🍀', MID: '💜', ADC: '🌸', SUPPORT: '💙'
};

// Identity colours — same palette family as POSITION_COLORS but available as personal identity
const IDENTITY_COLORS = [
  '#F2A7C3', // rose
  '#F7DFA0', // honey
  '#B8E0C0', // sage
  '#AED6F1', // sky
  '#C5B4E3', // lilac
  '#F2C795', // peach
  '#E8A0A0', // coral
  '#A8D8C9', // mint
];

const resetSessionUserId = () => {
  try {
    const newUid = 'user_' + Math.random().toString(36).substr(2, 9) + '_' + Date.now();
    localStorage.setItem('cozydraft-userid', newUid);
    window.sessionUserId = newUid;
    return newUid;
  } catch (e) {
    return 'user_' + Math.random().toString(36).substr(2, 9);
  }
};

const getOrGenerateSessionUserId = () => {
  try {
    let uId = localStorage.getItem('cozydraft-userid');
    if (!uId) {
      uId = 'user_' + Math.random().toString(36).substr(2, 9) + '_' + Date.now();
      localStorage.setItem('cozydraft-userid', uId);
    }
    window.sessionUserId = uId;
    return uId;
  } catch (e) {
    return 'user_' + Math.random().toString(36).substr(2, 9);
  }
};
const sessionUserId = getOrGenerateSessionUserId();

const DEFAULT_PLAYERS = [
  { id: 0, name: 'TopLaner', position: 'TOP', pool: [], ownerId: null, claimed: false, online: false, isSub: false, identityColor: '#F7DFA0' },
  { id: 1, name: 'Jungler', position: 'JUNGLE', pool: [], ownerId: null, claimed: false, online: false, isSub: false, identityColor: '#B8E0C0' },
  { id: 2, name: 'Midlaner', position: 'MID', pool: [], ownerId: null, claimed: false, online: false, isSub: false, identityColor: '#C5B4E3' },
  { id: 3, name: 'Carry', position: 'ADC', pool: [], ownerId: null, claimed: false, online: false, isSub: false, identityColor: '#F2A7C3' },
  { id: 4, name: 'Support', position: 'SUPPORT', pool: [], ownerId: null, claimed: false, online: false, isSub: false, identityColor: '#AED6F1' },
];

const INITIAL_STATE = {
  players: DEFAULT_PLAYERS,
  activePlayerId: null,
  // "Me" — the local player in the multi-user lobby fiction. May differ from activePlayerId,
  // which is the slot the user is currently *viewing/editing*.
  myUserId: null,
  lobbyCode: null,
  // Drives whether the onboarding flow shows on launch.
  onboarded: true,
  teamName: 'Cozy Crew',
  activeComp: { TOP: null, JUNGLE: null, MID: null, ADC: null, SUPPORT: null },
  activeCompNotes: [],  // crew comments on the WIP comp (carried into savedComps on save)
  savedComps: [],
  clashReadiness: {},
  scoutingReports: [],
  activeScoutingReportId: null,
  fearlessSeries: null,
  fearlessSeriesHistory: null,
};

// Tag-based analysis helpers
function analyzeComp(champData, comp) {
  if (!comp) return null;
  const getSlotChampionId = window.getSlotChampionId;
  const positions = window.POSITIONS || ['TOP', 'JUNGLE', 'MID', 'ADC', 'SUPPORT'];
  const hasPositions = positions.some(p => p in comp);
  const rawSlots = hasPositions ? positions.map(p => comp[p]) : Object.values(comp);
  const slots = rawSlots
    .map(c => (getSlotChampionId ? getSlotChampionId(c) : (c && typeof c === 'object' ? c.championId : c)))
    .filter(Boolean);
  if (slots.length === 0) return null;

  const tags = slots.flatMap(c => {
    const d = champData[c];
    return d ? d.tags : [];
  });

  const hasTank = tags.includes('Tank');
  const hasFighter = tags.includes('Fighter');
  const hasMage = tags.includes('Mage');
  const hasAssassin = tags.includes('Assassin');
  const hasMarksman = tags.includes('Marksman');
  const hasSupport = tags.includes('Support');

  const apCount = tags.filter(t => t === 'Mage').length;
  const adCount = tags.filter(t => t === 'Marksman').length;
  const tankCount = tags.filter(t => t === 'Tank' || t === 'Fighter').length;
  const ccCount = ['Tank', 'Support'].filter(t => tags.includes(t)).length;

  // Stat bars (0-100)
  const stats = {
    cc: Math.min(100, ccCount * 30 + (hasTank ? 20 : 0) + (hasSupport ? 15 : 0)),
    frontline: Math.min(100, tankCount * 30 + (hasFighter ? 15 : 0)),
    engage: Math.min(100, (hasTank ? 35 : 0) + (hasFighter ? 20 : 0) + (hasSupport ? 15 : 0)),
    lateGame: Math.min(100, (hasMarksman ? 35 : 0) + (hasMage ? 25 : 0) + (hasFighter ? 20 : 0)),
    botSafety: Math.min(100, (hasSupport ? 40 : 0) + (hasTank ? 20 : 0) + (hasMarksman ? 15 : 0)),
  };

  // Vibe
  let vibe = 'Mixed Bag 🌿';
  if (tankCount >= 2 && hasMarksman) vibe = 'Teamfight! 💪';
  else if (apCount >= 2 && !hasTank) vibe = 'Poke & Chill ✨';
  else if (hasSupport && hasMarksman && hasTank) vibe = 'Protect the Carry 💖';
  else if (hasAssassin && hasMage) vibe = 'Pick Comp 🎯';
  else if (hasFighter && !hasMage) vibe = 'Split Push 🌿';
  else if (hasTank && hasSupport) vibe = 'Engage Crew ⚡';

  // Pros
  const pros = [];
  if (hasTank || hasFighter) pros.push('Solid frontline 🛡️');
  if (ccCount >= 2) pros.push('Lots of crowd control 🌀');
  if (hasSupport && hasMarksman && hasTank) pros.push('Cozy protect comp 💛');
  if (apCount >= 1 && adCount >= 1) pros.push('Balanced damage — hard to itemize ⚖️');
  if (apCount >= 1) pros.push('Safe zone control ✨');

  // Cons
  const cons = [];
  if (!hasTank && !hasFighter) cons.push('No frontline — ouch! 😬');
  if (apCount >= 3) cons.push('Too much AP — buy some boots ⚡');
  if (apCount === 0 && adCount >= 2) cons.push('No AP threat ⚔️');
  if (ccCount === 0) cons.push('No lockdown — hope for outplays 🙏');
  if (adCount >= 2) cons.push('Gold competition 💰');

  // Win condition
  let winCond = 'working together as a team';
  let timing = 'mid';
  if (vibe.includes('Teamfight')) { winCond = 'winning big teamfights'; timing = 'mid'; }
  if (vibe.includes('Poke')) { winCond = 'poking enemies down before fights'; timing = 'early'; }
  if (vibe.includes('Protect')) { winCond = 'getting the carry fed'; timing = 'late'; }
  if (vibe.includes('Pick')) { winCond = 'catching isolated enemies'; timing = 'mid'; }
  if (vibe.includes('Split')) { winCond = 'applying side lane pressure'; timing = 'late'; }

  return { vibe, stats, pros, cons, winCond, timing };
}

const firebaseToArray = (val) => {
  if (!val) return [];
  if (Array.isArray(val)) return val.filter(Boolean);
  if (typeof val === 'object') return Object.values(val).filter(Boolean);
  return [];
};

const sanitizeScoutingReport = (r) => {
  if (!r) return null;
  const rawEnemies = firebaseToArray(r.enemies || r.lanes);
  const enemies = rawEnemies.map(enemy => {
    if (!enemy) return null;
    return {
      ...enemy,
      champPool: firebaseToArray(enemy.champPool),
      notes: enemy.notes ?? '',
      riotId: enemy.riotId ?? '',
      region: enemy.region ?? 'NA'
    };
  }).filter(Boolean);

  return {
    ...r,
    name: r.name || 'New report',
    subtitle: r.subtitle || 'Saturday 7pm',
    enemies,
    targetBans: firebaseToArray(r.targetBans || r.bans),
    possibleBans: firebaseToArray(r.possibleBans),
    gamePlan: r.gamePlan ?? '',
    priorityFocus: firebaseToArray(r.priorityFocus || r.priorities)
  };
};

const sanitizeScoutingReports = (reports) => {
  return firebaseToArray(reports)
    .map(sanitizeScoutingReport)
    .filter(Boolean);
};

const sanitizeSavedComp = (c) => {
  if (!c) return null;
  return {
    ...c,
    notes: firebaseToArray(c.notes)
  };
};

const sanitizeSavedComps = (comps) => {
  return firebaseToArray(comps)
    .map(sanitizeSavedComp)
    .filter(Boolean);
};

const sanitizeFearlessSeries = (series) => {
  if (!series) return null;
  const games = firebaseToArray(series.games).map(game => {
    if (!game) return null;
    return {
      ...game,
      ourPicks: firebaseToArray(game.ourPicks),
      enemyPicks: firebaseToArray(game.enemyPicks),
      bans: firebaseToArray(game.bans)
    };
  }).filter(Boolean);

  return {
    ...series,
    games
  };
};

function AppProvider({ children }) {
  const [lobbyCode, setLobbyCodeState] = useState(() => {
    try {
      localStorage.removeItem('cozydraft-lobbycode');
    } catch {}
    return null;
  });

  const [state, setState, isSynced] = window.useFirebaseSync(lobbyCode, {
    ...INITIAL_STATE,
    lobbyCode
  });

  const [currentTab, setCurrentTabState] = useState('home');
  const [champions, setChampions] = useState({});
  const [championsLoading, setChampionsLoading] = useState(true);
  const [ddragonVersion, setDdragonVersion] = useState('15.10.1');
  const [toast, setToast] = useState(null);

  const [tempIdentity, setTempIdentity] = useState({ displayName: '', displayColor: '' });
  const [userProfile, setUserProfile] = useState(null);
  const [localActiveScoutingReportId, setLocalActiveScoutingReportId] = useState(null);

  useEffect(() => {
    setLocalActiveScoutingReportId(null);
  }, [lobbyCode]);

  useEffect(() => {
    if (!window.firebase || !window.firebase.auth || !window.db) return;

    const unsubscribeAuth = window.firebase.auth().onAuthStateChanged((user) => {
      if (user && user.uid) {
        const userRef = window.db.ref(`users/${user.uid}`);
        const callback = userRef.on('value', (snap) => {
          const val = snap.val() || {};
          setUserProfile({
            uid: user.uid,
            displayName: val.displayName || user.displayName || user.email?.split('@')[0] || 'Player',
            icon: val.icon || 'walnut',
            identityColor: val.identityColor || '#F7DFA0',
            preferredLane: val.preferredLane || 'TOP',
            email: val.email || user.email || '',
            username: val.username || '',
          });
        });
        return () => {
          try { userRef.off('value', callback); } catch (e) {}
        };
      } else {
        setUserProfile(null);
      }
    });

    return () => {
      try { unsubscribeAuth(); } catch (e) {}
    };
  }, []);

  useEffect(() => {
    if (state && state.roomNotFound) {
      setToast("This lobby no longer exists 🌸");
      setLobbyCodeState(null);
      setState(s => ({
        ...s,
        roomNotFound: false,
        lobbyCode: null,
        onboarded: true,
      }));
    }
  }, [state?.roomNotFound]);

  const [accountPoolsByUid, setAccountPoolsByUid] = useState({});
  const [myTeams, setMyTeams] = useState([]);

  // Listener for users/{uid}/teams (one-time once('value') read per team)
  useEffect(() => {
    const authUser = window.firebase && window.firebase.auth && window.firebase.auth().currentUser;
    const uid = userProfile?.uid || (authUser ? authUser.uid : null) || localStorage.getItem('cozydraft-userid');
    if (!uid || !window.db) {
      setMyTeams([]);
      return;
    }

    const teamsRef = window.db.ref(`users/${uid}/teams`);

    const handleTeams = async (snap) => {
      const val = snap.val() || {};
      const codes = Object.keys(val).filter(c => val[c] === true);

      if (codes.length === 0) {
        setMyTeams([]);
        return;
      }

      const authUid = window.firebase && window.firebase.auth && window.firebase.auth().currentUser && window.firebase.auth().currentUser.uid;
      const currentSessionId = (typeof getOrGenerateSessionUserId === 'function') ? getOrGenerateSessionUserId() : window.sessionUserId;
      const myName = (userProfile?.displayName || '').toLowerCase().trim();

      const teamPromises = codes.map(async (code) => {
        try {
          const roomSnap = await window.db.ref(`rooms/${code}/state`).once('value');
          const roomData = roomSnap.val() || {};

          const playersRaw = roomData.players;
          const playersList = Array.isArray(playersRaw) ? playersRaw : (playersRaw && typeof playersRaw === 'object' ? Object.values(playersRaw) : []);

          // Reuse header dropdown's resolver logic from FriendBarV2
          const me = playersList.find(p => p && (p.claimed || !!p.ownerId || !!p.uid) && (
            (authUid && (p.uid === authUid || p.ownerId === authUid)) ||
            (uid && (p.uid === uid || p.ownerId === uid)) ||
            (!authUid && currentSessionId && p.ownerId === currentSessionId) ||
            (!authUid && window.sessionUserId && p.ownerId === window.sessionUserId) ||
            (myName && p.name && p.name.toLowerCase().trim() === myName)
          ));

          const iOwnALane = !!(me && (me.claimed || me.ownerId));
          const pos = iOwnALane ? me.position : null;
          const roleLabel = pos ? (POSITION_LABELS[pos] || pos) : null;
          const roleEmoji = pos ? ({ TOP: '⚔️', JUNGLE: '🌾', MID: '🔮', ADC: '🏹', SUPPORT: '💛' }[pos] || '🌾') : '🌾';

          return {
            code,
            teamName: roomData.teamName || `Cozy Crew (${code})`,
            myRole: pos,
            myRoleLabel: roleLabel,
            myRoleEmoji: roleEmoji,
            players: playersList,
          };
        } catch (e) {
          return {
            code,
            teamName: `Cozy Crew (${code})`,
            myRole: null,
            myRoleLabel: null,
            myRoleEmoji: '🌾',
            players: [],
          };
        }
      });

      const teamResults = await Promise.all(teamPromises);
      setMyTeams(teamResults.filter(Boolean));
    };

    teamsRef.on('value', handleTeams);

    return () => {
      try { teamsRef.off('value', handleTeams); } catch (e) {}
    };
  }, [userProfile, sessionUserId]);

  const [userProfilesByUid, setUserProfilesByUid] = useState({});

  useEffect(() => {
    if (!window.db || !state.players) return;

    const authUser = window.firebase && window.firebase.auth && window.firebase.auth().currentUser;
    const currentUid = authUser ? authUser.uid : null;
    const targetUids = Array.from(new Set(
      state.players
        .filter(p => p && (p.uid || p.ownerId))
        .map(p => p.uid || p.ownerId)
        .filter(Boolean)
    ));

    if (targetUids.length === 0) return;

    const listeners = [];

    targetUids.forEach(uid => {
      const userPoolsRef = window.db.ref(`users/${uid}/pools`);
      const callback = userPoolsRef.on('value', (snap) => {
        const val = snap.val() || {};
        const formattedRolePools = {};
        Object.keys(val).forEach(roleKey => {
          const roleUpper = roleKey.toUpperCase();
          const champsObj = (val[roleKey] && val[roleKey].champions) || {};
          formattedRolePools[roleUpper] = Object.entries(champsObj).map(([champId, cData]) => ({
            championId: champId,
            position: roleUpper,
            comfort: (cData && cData.comfort) || 2,
            notes: (cData && cData.notes) || '',
            playstyle: null,
            isFocusPick: Boolean(cData && (cData.isFocusPick || cData.focusPick)),
            focusPick: Boolean(cData && (cData.isFocusPick || cData.focusPick)),
            focusSetAt: (cData && cData.focusSetAt) || null,
          }));
        });

        setAccountPoolsByUid(prev => ({
          ...prev,
          [uid]: formattedRolePools,
        }));
      });

      listeners.push({ ref: userPoolsRef, callback });

      const nameRef = window.db.ref(`users/${uid}/displayName`);
      const nameCallback = nameRef.on('value', (snap) => {
        const val = snap.val();
        if (val) {
          setUserProfilesByUid(prev => ({ ...prev, [uid]: val }));
        }
      });
      listeners.push({ ref: nameRef, callback: nameCallback });
    });

    return () => {
      listeners.forEach(({ ref, callback }) => {
        try { ref.off('value', callback); } catch (e) {}
      });
    };
  }, [state.players, state.sessionUserId, state.myUserId]);

  const getEffectivePlayerPool = useCallback((player) => {
    if (!player) return [];
    if (Array.isArray(player.pool) && player.pool.length > 0) {
      return player.pool;
    }
    const authUser = window.firebase && window.firebase.auth && window.firebase.auth().currentUser;
    const currentUid = authUser ? authUser.uid : null;
    const isMe = Boolean((player.claimed || player.ownerId || player.uid) && (
      (currentUid && (player.uid === currentUid || player.ownerId === currentUid)) ||
      (!currentUid && player.ownerId === state.sessionUserId)
    ));
    const targetUid = player.uid || (isMe ? currentUid : player.ownerId);
    if (targetUid && accountPoolsByUid && accountPoolsByUid[targetUid]) {
      const role = (player.position || 'TOP').toUpperCase();
      const userRolePool = accountPoolsByUid[targetUid][role];
      if (Array.isArray(userRolePool) && userRolePool.length > 0) {
        return userRolePool;
      }
    }
    return [];
  }, [accountPoolsByUid, state.sessionUserId, state.myUserId]);

  useEffect(() => {
    if (lobbyCode && window.db) {
      window.db.ref(`rooms/${lobbyCode}/state`).once('value').then(snap => {
        if (!snap.exists() || !snap.val()) {
          try {
            localStorage.removeItem('cozydraft-lobbycode');
            localStorage.removeItem(`cozydraft-room-${lobbyCode}`);
          } catch (e) {}
          setLobbyCodeState(null);
          setState(s => ({ ...s, lobbyCode: null, onboarded: true }));
        }
      }).catch(() => {
        try {
          localStorage.removeItem('cozydraft-lobbycode');
        } catch (e) {}
        setLobbyCodeState(null);
        setState(s => ({ ...s, lobbyCode: null, onboarded: true }));
      });
    }
  }, []);

  useEffect(() => {
    const fetchChamps = async () => {
      let latestVersion = '15.10.1';
      try {
        const versionRes = await fetch('https://ddragon.leagueoflegends.com/api/versions.json');
        const versions = await versionRes.json();
        if (versions && versions.length > 0) {
          latestVersion = versions[0];
        }
      } catch (e) {
        console.warn('Failed to fetch latest version, using fallback 15.10.1:', e);
      }
      setDdragonVersion(latestVersion);

      try {
        const champRes = await fetch(`https://ddragon.leagueoflegends.com/cdn/${latestVersion}/data/en_US/champion.json`);
        const d = await champRes.json();
        const rawData = d?.data || {};
        const beforeCount = Object.keys(rawData).length;

        // Data-driven filter: Exclude classic-mode champions (distinguished by 'Jade_' ID prefix or key >= 60000)
        // Fallback deduplication: group by base numeric key (key % 60000) to keep the current live-game entry
        const filteredEntries = Object.entries(rawData).filter(([id, champ]) => {
          if (!champ) return false;
          const isClassic = (champ.id && champ.id.startsWith('Jade_')) || (champ.key && Number(champ.key) >= 60000);
          return !isClassic;
        });

        const seenKeys = new Map();
        const cleanEntries = [];
        for (const [id, champ] of filteredEntries) {
          const baseKey = Number(champ.key) % 60000;
          if (!seenKeys.has(baseKey)) {
            seenKeys.set(baseKey, [id, champ]);
            cleanEntries.push([id, champ]);
          }
        }

        const filteredData = Object.fromEntries(cleanEntries);
        const afterCount = Object.keys(filteredData).length;

        console.log('Champions before filter:', beforeCount, '| After filter:', afterCount);
        setChampions(filteredData);
      } catch (e) {
        console.error('Failed to fetch champions:', e);
      } finally {
        setChampionsLoading(false);
      }
    };
    fetchChamps();
  }, []);

  const showToast = useCallback((msg) => {
    setToast(msg);
    setTimeout(() => setToast(null), 3000);
  }, []);

  // Keep activePlayerId locked to the current user's slot and keep online status active
  useEffect(() => {
    if (!state.players) return;
    const authUser = window.firebase && window.firebase.auth && window.firebase.auth().currentUser;
    const currentUid = authUser ? authUser.uid : null;
    const mySlot = state.players.find(p => p && (p.claimed || !!p.ownerId || !!p.uid) && (
      (currentUid && (p.uid === currentUid || p.ownerId === currentUid)) ||
      (!currentUid && sessionUserId && p.ownerId === sessionUserId)
    ));
    if (mySlot) {
      if (state.activePlayerId !== mySlot.id || state.myUserId !== mySlot.id) {
        setState(s => ({ ...s, activePlayerId: mySlot.id, myUserId: mySlot.id }));
      }
      if (mySlot.ownerId === sessionUserId && !mySlot.online) {
        setState(s => ({
          ...s,
          players: s.players.map(p => p.id === mySlot.id ? { ...p, online: true } : p)
        }));
      }
    } else {
      if (state.activePlayerId !== null || state.myUserId !== null) {
        setState(s => ({ ...s, activePlayerId: null, myUserId: null }));
      }
    }
  }, [state.players, state.myUserId, state.activePlayerId, sessionUserId, setState]);

  const setTab = useCallback((tab, extra = {}) => {
    setCurrentTabState(tab);
    if (Object.keys(extra).length) setState(s => ({ ...s, ...extra }));
  }, [setState]);

  const setActivePlayer = useCallback((id) => {
    setState(s => {
      const authUser = window.firebase && window.firebase.auth && window.firebase.auth().currentUser;
      const currentUid = authUser ? authUser.uid : null;
      const mySlot = s.players.find(p => p && (p.claimed || !!p.ownerId || !!p.uid) && (
        (currentUid && (p.uid === currentUid || p.ownerId === currentUid)) ||
        (!currentUid && sessionUserId && p.ownerId === sessionUserId)
      ));
      if (mySlot && id === mySlot.id) {
        return { ...s, activePlayerId: id, myUserId: id };
      }
      return s;
    });
  }, [sessionUserId, setState]);
  const updateTeamName = useCallback((name) => setState(s => ({ ...s, teamName: name })), []);

  const updatePlayerName = useCallback((playerId, name) => {
    setState(s => ({ ...s, players: s.players.map(p => p.id === playerId ? { ...p, name } : p) }));
  }, []);

  const updatePlayerIdentity = useCallback((playerId, updates) => {
    if (updates.name || updates.displayName || updates.identityColor || updates.displayColor) {
      setTempIdentity(prev => ({
        displayName: updates.name || updates.displayName || prev?.displayName || '',
        displayColor: updates.identityColor || updates.displayColor || prev?.displayColor || '#F7DFA0',
      }));
    }
    setState(s => ({ ...s, players: s.players.map(p => p.id === playerId ? { ...p, ...updates } : p) }));
  }, []);

  const claimLane = useCallback((playerId, userId, force = false) => {
    const authUser = window.firebase && window.firebase.auth && window.firebase.auth().currentUser;
    const authUid = authUser ? authUser.uid : null;
    const currentSessionUserId = getOrGenerateSessionUserId();
    const effectiveUserId = authUid || userId || currentSessionUserId;
    setState(s => {
      const idx = s.players.findIndex(p => p.id === playerId);
      if (idx === -1) return s;
      const slot = s.players[idx];
      // REJECT if slot is owned by someone else AND force is false
      if (!force && slot.ownerId && slot.ownerId !== effectiveUserId) {
        if (typeof showToast === 'function') showToast('That lane is already taken! 🔒');
        return s;
      }

      let newPlayers = [...s.players];

      // Structural enforcer: release any OTHER lane previously owned by effectiveUserId
      const existingIdx = newPlayers.findIndex(p => p.ownerId === effectiveUserId && p.id !== playerId);
      if (existingIdx !== -1) {
        const defaultSlot = DEFAULT_PLAYERS[existingIdx];
        const defaultName = defaultSlot ? defaultSlot.name : newPlayers[existingIdx].name;
        const defaultColor = defaultSlot ? defaultSlot.identityColor : newPlayers[existingIdx].identityColor;
        
        newPlayers[existingIdx] = {
          ...newPlayers[existingIdx],
          name: defaultName,
          identityColor: defaultColor,
          claimed: false,
          online: false,
          ownerId: null,
          pool: []
        };

        if (window.db && lobbyCode) {
          window.db.ref(`rooms/${lobbyCode}/state/players/${existingIdx}`).update({
            name: defaultName,
            identityColor: defaultColor,
            claimed: false,
            online: false,
            ownerId: null,
            pool: []
          });
        }
      }

      const updatedSlot = {
        ...slot,
        ownerId: effectiveUserId,
        claimed: true,
        online: true,
        name: tempIdentity?.displayName || slot.name,
        identityColor: tempIdentity?.displayColor || slot.identityColor,
        pool: null, // Default to null so claimed lane inherits from users/{uid}/pools/{role}
      };
      if (window.db && lobbyCode) {
        const authUser = window.firebase && window.firebase.auth && window.firebase.auth().currentUser;
        const authUid = authUser ? authUser.uid : null;
        const displayNameToWrite = userProfile?.displayName || tempIdentity?.displayName || updatedSlot.name;

        const playerRef = window.db.ref(`rooms/${lobbyCode}/state/players/${idx}`);
        playerRef.child('ownerId').set(effectiveUserId);
        playerRef.child('claimed').set(true);
        playerRef.child('online').set(true);
        if (displayNameToWrite) {
          playerRef.child('name').set(displayNameToWrite);
        }
        if (updatedSlot.identityColor) {
          playerRef.child('identityColor').set(updatedSlot.identityColor);
        }
        playerRef.child('pool').set(null);

        if (authUid) {
          playerRef.child('uid').set(authUid);
          window.db.ref(`users/${authUid}/teams/${lobbyCode}`).set(true);
        }
      }
      newPlayers = newPlayers.map((p, i) => i === idx ? updatedSlot : p);
      return { ...s, players: newPlayers, myUserId: playerId };
    });
  }, [lobbyCode, tempIdentity, showToast]);

  const releaseLane = useCallback((playerId) => {
    const currentSessionUserId = getOrGenerateSessionUserId();
    setState(s => {
      // Strictly locate target player slot owned by currentSessionUserId
      const idx = s.players.findIndex(p => p.ownerId === currentSessionUserId && (playerId === undefined || playerId === null || p.id === playerId));
      if (idx === -1) return s;

      const defaultSlot = DEFAULT_PLAYERS[idx];
      const releasedSlot = {
        ...s.players[idx],
        name: defaultSlot ? defaultSlot.name : s.players[idx].name,
        identityColor: defaultSlot ? defaultSlot.identityColor : s.players[idx].identityColor,
        claimed: false,
        online: false,
        ownerId: null,
        pool: []
      };

      if (window.db && lobbyCode) {
        window.db.ref(`rooms/${lobbyCode}/state/players/${idx}`).update({
          name: releasedSlot.name,
          identityColor: releasedSlot.identityColor,
          claimed: false,
          online: false,
          ownerId: null,
          pool: []
        });
      }

      return {
        ...s,
        players: s.players.map((p, i) => i === idx ? releasedSlot : p)
      };
    });
  }, [lobbyCode]);

  const leaveLobby = useCallback((targetCodeParam) => {
    setState(s => {
      const activeCode = targetCodeParam || s.lobbyCode;
      const authUser = window.firebase && window.firebase.auth && window.firebase.auth().currentUser;
      const authUid = authUser ? authUser.uid : null;
      const currentSessionUserId = authUid || getOrGenerateSessionUserId();
      const myName = (userProfile?.displayName || '').toLowerCase().trim();

      const myIdx = s.players.findIndex(p => p && (p.claimed || !!p.ownerId || !!p.uid) && (
        (authUid && (p.uid === authUid || p.ownerId === authUid)) ||
        (currentSessionUserId && p.ownerId === currentSessionUserId) ||
        (s?.myUserId && (p.id === s.myUserId || p.ownerId === s.myUserId)) ||
        (myName && p.name && p.name.toLowerCase().trim() === myName)
      ));

      if (window.db && activeCode) {
        if (myIdx !== -1) {
          const defaultSlot = DEFAULT_PLAYERS[myIdx];
          const defaultName = defaultSlot ? defaultSlot.name : s.players[myIdx].name;
          const defaultColor = defaultSlot ? defaultSlot.identityColor : s.players[myIdx].identityColor;
          
          const playerRef = window.db.ref(`rooms/${activeCode}/state/players/${myIdx}`);
          playerRef.child('ownerId').set(null);
          playerRef.child('claimed').set(false);
          playerRef.child('online').set(false);
          playerRef.child('uid').set(null);
          playerRef.child('name').set(defaultName);
          playerRef.child('identityColor').set(defaultColor);
          playerRef.child('pool').set([]);
          playerRef.child('subRoles').set(null);
          playerRef.child('notes').set(null);
          playerRef.child('comfortRatings').set(null);
          playerRef.child('focusPicks').set(null);
        }

        if (authUid) {
          window.db.ref(`users/${authUid}/teams/${activeCode}`).remove();
        }
      }

      try {
        localStorage.removeItem('cozydraft-lobbycode');
        if (activeCode) {
          localStorage.removeItem(`cozydraft-room-${activeCode}`);
        }
      } catch (e) {}

      if (!targetCodeParam || targetCodeParam === s.lobbyCode) {
        setLobbyCodeState(null);
        return {
          ...s,
          lobbyCode: null,
          players: DEFAULT_PLAYERS.map(p => ({ ...p })),
          myUserId: null,
          activePlayerId: null,
          onboarded: true,
        };
      }

      return s;
    });
  }, [userProfile]);

  const setMyUser = useCallback((userId) => setState(s => ({ ...s, myUserId: userId })), [setState]);
  const setLobbyCode = useCallback(async (code) => {
    setLobbyCodeState(code);
    try { localStorage.setItem('cozydraft-lobbycode', code); } catch {}
    setState(s => ({ ...s, lobbyCode: code }));

    if (code) {
      const authUser = window.firebase && window.firebase.auth && window.firebase.auth().currentUser;
      const uid = userProfile?.uid || (authUser ? authUser.uid : null) || localStorage.getItem('cozydraft-userid');
      if (window.db && uid) {
        try {
          await window.db.ref(`users/${uid}/teams/${code}`).set(true);
        } catch (err) {
          console.error(`Failed to register team membership for user ${uid} in room ${code}:`, err);
        }
      }
    }
  }, [userProfile]);
  const setOnboarded = useCallback((v) => setState(s => ({ ...s, onboarded: v })), [setState]);

  const createScoutingReport = useCallback(async (name) => {
    const newReport = {
      id: Date.now(),
      name: name || 'New report',
      createdAt: new Date().toISOString(),
      createdBy: sessionUserId,
      enemies: POSITIONS.map(pos => ({
        position: pos,
        riotId: '',
        region: 'NA',
        champPool: [],
        notes: ''
      })),
      targetBans: [],
      gamePlan: '',
      priorityFocus: []
    };
    const ref = window.db.ref(`rooms/${lobbyCode}/state/scoutingReports`);
    const snapshot = await ref.once('value');
    const existing = firebaseToArray(snapshot.val()) || [];
    await ref.set([...existing, newReport]);
    setLocalActiveScoutingReportId(newReport.id);
    return newReport;
  }, [lobbyCode, sessionUserId]);

  const updateScoutingReport = useCallback(async (reportId, updates) => {
    const ref = window.db.ref(`rooms/${lobbyCode}/state/scoutingReports`);
    const snapshot = await ref.once('value');
    const reports = firebaseToArray(snapshot.val()) || [];
    const index = reports.findIndex(r => r.id === reportId);
    if (index === -1) return;
    Object.keys(updates).forEach(key => {
      window.db.ref(`rooms/${lobbyCode}/state/scoutingReports/${index}/${key}`).set(updates[key]);
    });
  }, [lobbyCode]);

  const updateScoutingEnemy = useCallback(async (reportId, enemyIndex, updates) => {
    const ref = window.db.ref(`rooms/${lobbyCode}/state/scoutingReports`);
    const snapshot = await ref.once('value');
    const reports = firebaseToArray(snapshot.val()) || [];
    const reportIndex = reports.findIndex(r => r.id === reportId);
    if (reportIndex === -1) return;
    Object.keys(updates).forEach(key => {
      window.db.ref(`rooms/${lobbyCode}/state/scoutingReports/${reportIndex}/enemies/${enemyIndex}/${key}`).set(updates[key]);
    });
  }, [lobbyCode]);

  const deleteScoutingReport = useCallback(async (reportId) => {
    const ref = window.db.ref(`rooms/${lobbyCode}/state/scoutingReports`);
    const snapshot = await ref.once('value');
    const reports = firebaseToArray(snapshot.val()) || [];
    const updated = reports.filter(r => r.id !== reportId);
    await ref.set(updated);
  }, [lobbyCode]);

  const setActiveScoutingReport = useCallback((reportId) => {
    setLocalActiveScoutingReportId(reportId);
  }, []);

  const updateFearlessSeries = useCallback((fearlessSeries) => {
    setState(s => ({ ...s, fearlessSeries }));
  }, [setState]);

  const updateFearlessSeriesHistory = useCallback((fearlessSeriesHistory) => {
    setState(s => ({ ...s, fearlessSeriesHistory }));
  }, [setState]);

  const isCreatingLobbyRef = React.useRef(false);

  const createNewLobby = useCallback(async (customTeamName) => {
    if (isCreatingLobbyRef.current) return null;
    isCreatingLobbyRef.current = true;

    try {
      const db = window.db;
      let code = String(Math.floor(1000 + Math.random() * 9000));

      if (db) {
        let attempts = 0;
        while (attempts < 10) {
          try {
            const snap = await db.ref(`rooms/${code}/state`).once('value');
            if (!snap.exists()) break;
          } catch (e) { break; }
          code = String(Math.floor(1000 + Math.random() * 9000));
          attempts++;
        }
      }

      if (!window.recentlyCreatedLobbies) {
        window.recentlyCreatedLobbies = new Set();
      }
      window.recentlyCreatedLobbies.add(code);
      setTimeout(() => {
        if (window.recentlyCreatedLobbies) {
          window.recentlyCreatedLobbies.delete(code);
        }
      }, 5000);

      const nameToUse = (customTeamName && String(customTeamName).trim()) || 'Cozy Crew';
      console.log('CREATING LOBBY:', code, 'with teamName:', nameToUse, 'at', Date.now());

      try {
        localStorage.setItem('cozydraft-lobbycode', code);
      } catch (e) {}

      const freshPlayers = DEFAULT_PLAYERS.map(p => ({ ...p }));
      const freshState = {
        players: freshPlayers,
        teamName: nameToUse,
        activeComp: { TOP: null, JUNGLE: null, MID: null, ADC: null, SUPPORT: null },
        activeCompNotes: [],
        savedComps: [],
      };

      try {
        localStorage.setItem(`cozydraft-room-${code}`, JSON.stringify(freshState));
      } catch (e) {}

      // Await RTDB write confirmation completely before setting active room code
      if (db) {
        await db.ref(`rooms/${code}/state`).set(freshState);
      }

      const authUser = window.firebase && window.firebase.auth && window.firebase.auth().currentUser;
      const uid = userProfile?.uid || (authUser ? authUser.uid : null) || localStorage.getItem('cozydraft-userid');
      if (db && uid) {
        try {
          await db.ref(`users/${uid}/teams/${code}`).set(true);
        } catch (err) {
          console.error(`Failed to write team membership for ${code} under users/${uid}/teams:`, err);
          if (typeof showToast === 'function') {
            showToast('Warning: Failed to save team membership ⚠️');
          }
        }
      }

      setLobbyCodeState(code);
      setState(s => ({
        ...s,
        ...freshState,
        lobbyCode: code,
        roomNotFound: false,
        myUserId: null,
        activePlayerId: null,
      }));

      return code;
    } catch (err) {
      console.error("Error creating new lobby:", err);
      return null;
    } finally {
      isCreatingLobbyRef.current = false;
    }
  }, []);

  // Wipe all claims (used when starting a fresh lobby).
  const resetLobby = useCallback((targetCode) => {
    const code = targetCode || lobbyCode;
    const freshPlayers = DEFAULT_PLAYERS.map(p => ({ ...p }));
    setState(s => ({
      ...s,
      players: freshPlayers,
      activeComp: { TOP: null, JUNGLE: null, MID: null, ADC: null, SUPPORT: null },
      activeCompNotes: [],
    }));
    if (window.db && code) {
      window.db.ref(`rooms/${code}/state`).update({
        players: freshPlayers,
        activeComp: { TOP: null, JUNGLE: null, MID: null, ADC: null, SUPPORT: null },
        activeCompNotes: [],
      });
    }
  }, [lobbyCode]);

  const kickPlayer = useCallback((playerId) => {
    setState(s => ({
      ...s,
      players: s.players.map((p, idx) => p.id === playerId ? {
        ...p,
        name: DEFAULT_PLAYERS[idx].name,
        identityColor: DEFAULT_PLAYERS[idx].identityColor,
        claimed: false,
        online: false,
        ownerId: null,
        pool: []
      } : p)
    }));
  }, []);

  const addToPool = useCallback((playerId, position, champion) => {
    const nowIso = new Date().toISOString();
    setState(s => {
      const player = s.players.find(p => p.id === playerId);
      if (!player) return s;
      if (player.pool.find(c => c.championId === champion.id && c.position === position)) return s;
      return {
        ...s,
        players: s.players.map(p => p.id === playerId ? {
          ...p,
          pool: [...p.pool, { championId: champion.id, comfort: 1, isFlex: false, isFocusPick: false, position, updatedAt: nowIso }]
        } : p)
      };
    });
  }, []);

  const removeFromPool = useCallback((playerId, championId, position) => {
    setState(s => ({
      ...s,
      players: s.players.map(p => p.id === playerId ? {
        ...p, pool: p.pool.filter(c => !(c.championId === championId && c.position === position)),
        _explicitPoolAction: true
      } : p)
    }));
  }, []);

  const updatePoolEntry = useCallback((playerId, championId, position, updates) => {
    const nowIso = new Date().toISOString();
    setState(s => ({
      ...s,
      players: s.players.map(p => p.id === playerId ? {
        ...p,
        pool: p.pool.map(c =>
          c.championId === championId && c.position === position ? { ...c, ...updates, updatedAt: nowIso } : c
        )
      } : p)
    }));
  }, []);

  const toggleFocusPick = useCallback((playerId, championId, position) => {
    const nowTs = Date.now();
    const authUser = window.firebase && window.firebase.auth && window.firebase.auth().currentUser;
    const authUid = authUser ? authUser.uid : null;
    setState(s => {
      const player = s.players.find(p => p.id === playerId);
      if (!player) return s;

      const isOwner = (player.claimed || player.ownerId || player.uid) && (
        (authUid && (player.uid === authUid || player.ownerId === authUid)) ||
        (!authUid && sessionUserId && player.ownerId === sessionUserId)
      );
      if (!isOwner) return s;

      const pool = player.pool || [];
      const entry = pool.find(c => c.championId === championId && c.position === position);
      if (!entry) return s;

      const currentlyFocus = Boolean(entry.isFocusPick || entry.focusPick);

      if (!currentlyFocus) {
        const focusCount = pool.filter(e => e.isFocusPick || e.focusPick).length;
        if (focusCount >= 3) {
          if (showToast) showToast('Only 3 at a time — unselect one first 🌸');
          return s;
        }
      }

      const updatedPool = pool.map(c =>
        c.championId === championId && c.position === position
          ? { ...c, isFocusPick: !currentlyFocus, focusPick: !currentlyFocus }
          : c
      );

      return {
        ...s,
        players: s.players.map(p => p.id === playerId ? {
          ...p,
          pool: updatedPool,
          focusPicksSetAt: nowTs,
        } : p)
      };
    });
  }, [showToast]);

  const setActiveComp = useCallback((comp) => {
    if (window.db && lobbyCode) {
      window.db.ref(`rooms/${lobbyCode}/state/activeComp`).set(comp);
    }
    setState(s => ({ ...s, activeComp: comp }));
  }, [lobbyCode, setState]);

  // ── Comp notes (active WIP comp) ───────────────────────────────────────
  const addActiveCompNote = useCallback(async (note) => {
    let current = [];
    if (window.db && lobbyCode) {
      try {
        const ref = window.db.ref(`rooms/${lobbyCode}/state/activeCompNotes`);
        const snapshot = await ref.once('value');
        current = firebaseToArray(snapshot.val()) || [];
        const updated = [...current, note];
        await ref.set(updated);
      } catch (e) {
        console.error("addActiveCompNote error:", e);
      }
    } else {
      current = firebaseToArray(state.activeCompNotes) || [];
      const updated = [...current, note];
      setState(s => ({ ...s, activeCompNotes: updated }));
    }
  }, [lobbyCode, state.activeCompNotes, setState]);

  const editActiveCompNote = useCallback(async (noteId, text) => {
    let current = [];
    if (window.db && lobbyCode) {
      try {
        const ref = window.db.ref(`rooms/${lobbyCode}/state/activeCompNotes`);
        const snapshot = await ref.once('value');
        current = firebaseToArray(snapshot.val()) || [];
        const updated = current.map(n =>
          n.id === noteId ? { ...n, text, editedAt: new Date().toISOString() } : n
        );
        await ref.set(updated);
      } catch (e) {
        console.error("editActiveCompNote error:", e);
      }
    } else {
      current = firebaseToArray(state.activeCompNotes) || [];
      const updated = current.map(n =>
        n.id === noteId ? { ...n, text, editedAt: new Date().toISOString() } : n
      );
      setState(s => ({ ...s, activeCompNotes: updated }));
    }
  }, [lobbyCode, state.activeCompNotes, setState]);

  const deleteActiveCompNote = useCallback(async (noteId) => {
    let current = [];
    if (window.db && lobbyCode) {
      try {
        const ref = window.db.ref(`rooms/${lobbyCode}/state/activeCompNotes`);
        const snapshot = await ref.once('value');
        current = firebaseToArray(snapshot.val()) || [];
        const updated = current.filter(n => n.id !== noteId);
        await ref.set(updated);
      } catch (e) {
        console.error("deleteActiveCompNote error:", e);
      }
    } else {
      current = firebaseToArray(state.activeCompNotes) || [];
      const updated = current.filter(n => n.id !== noteId);
      setState(s => ({ ...s, activeCompNotes: updated }));
    }
  }, [lobbyCode, state.activeCompNotes, setState]);

  const setActiveCompNotes = useCallback((notes) => {
    const cleanNotes = firebaseToArray(notes);
    if (window.db && lobbyCode) {
      window.db.ref(`rooms/${lobbyCode}/state/activeCompNotes`).set(cleanNotes);
    }
    setState(s => ({ ...s, activeCompNotes: cleanNotes }));
  }, [lobbyCode, setState]);

  const cleanActiveCompNotes = useCallback(() => {
    if (window.db && lobbyCode) {
      window.db.ref(`rooms/${lobbyCode}/state/activeCompNotes`).set([]);
    }
    setState(s => ({ ...s, activeCompNotes: [] }));
  }, [lobbyCode, setState]);

  // ── Comp notes (saved comps — editable after the comp is saved) ────────
  const addNoteToComp = useCallback(async (compId, noteTextOrObj) => {
    const me = state.players.find(p => p.id === state.myUserId) || state.players[0];
    const displayName = me ? me.name : 'Teammate';

    let noteText = typeof noteTextOrObj === 'string' ? noteTextOrObj : noteTextOrObj?.text;
    if (!noteText || !noteText.trim()) return;

    const newNote = {
      id: (typeof noteTextOrObj === 'object' && noteTextOrObj?.id) || (Date.now() + Math.floor(Math.random() * 1000)),
      author: (typeof noteTextOrObj === 'object' && noteTextOrObj?.authorName) || displayName,
      authorId: (typeof noteTextOrObj === 'object' && noteTextOrObj?.authorId) || state.myUserId,
      authorName: (typeof noteTextOrObj === 'object' && noteTextOrObj?.authorName) || displayName,
      authorColor: (typeof noteTextOrObj === 'object' && noteTextOrObj?.authorColor) || (me ? me.identityColor : '#F7DFA0'),
      text: noteText.trim(),
      timestamp: Date.now(),
      createdAt: (typeof noteTextOrObj === 'object' && noteTextOrObj?.createdAt) || new Date().toISOString(),
    };

    if (window.db && lobbyCode) {
      const ref = window.db.ref(`rooms/${lobbyCode}/state/savedComps`);
      const snapshot = await ref.once('value');
      const comps = firebaseToArray(snapshot.val()) || [];
      const index = comps.findIndex(c => String(c.id) === String(compId));
      if (index !== -1) {
        const existingNotes = firebaseToArray(comps[index].notes) || [];
        if (!existingNotes.some(n => String(n.id) === String(newNote.id))) {
          const updatedNotes = [...existingNotes, newNote];
          await window.db.ref(`rooms/${lobbyCode}/state/savedComps/${index}/notes`).set(updatedNotes);
        }
      }
    } else {
      setState(s => {
        const comps = firebaseToArray(s.savedComps) || [];
        const updated = comps.map(c => {
          if (String(c.id) !== String(compId)) return c;
          const existingNotes = firebaseToArray(c.notes);
          if (existingNotes.some(n => String(n.id) === String(newNote.id))) return c;
          return { ...c, notes: [...existingNotes, newNote] };
        });
        return { ...s, savedComps: updated };
      });
    }
  }, [lobbyCode, state.players, state.myUserId, setState]);

  const addSavedCompNote = addNoteToComp;

  const editSavedCompNote = useCallback(async (compId, noteId, text) => {
    if (window.db && lobbyCode) {
      const ref = window.db.ref(`rooms/${lobbyCode}/state/savedComps`);
      const snapshot = await ref.once('value');
      const comps = firebaseToArray(snapshot.val()) || [];
      const index = comps.findIndex(c => String(c.id) === String(compId));
      if (index !== -1) {
        const existingNotes = firebaseToArray(comps[index].notes) || [];
        const updatedNotes = existingNotes.map(n =>
          String(n.id) === String(noteId) ? { ...n, text, editedAt: new Date().toISOString() } : n
        );
        await window.db.ref(`rooms/${lobbyCode}/state/savedComps/${index}/notes`).set(updatedNotes);
      }
    } else {
      setState(s => {
        const comps = firebaseToArray(s.savedComps) || [];
        const updated = comps.map(c =>
          String(c.id) === String(compId)
            ? {
                ...c,
                notes: firebaseToArray(c.notes).map(n =>
                  String(n.id) === String(noteId) ? { ...n, text, editedAt: new Date().toISOString() } : n
                )
              }
            : c
        );
        return { ...s, savedComps: updated };
      });
    }
  }, [lobbyCode, setState]);

  const deleteSavedCompNote = useCallback(async (compId, noteId) => {
    if (window.db && lobbyCode) {
      const ref = window.db.ref(`rooms/${lobbyCode}/state/savedComps`);
      const snapshot = await ref.once('value');
      const comps = firebaseToArray(snapshot.val()) || [];
      const index = comps.findIndex(c => String(c.id) === String(compId));
      if (index !== -1) {
        const existingNotes = firebaseToArray(comps[index].notes) || [];
        const updatedNotes = existingNotes.filter(n => String(n.id) !== String(noteId));
        await window.db.ref(`rooms/${lobbyCode}/state/savedComps/${index}/notes`).set(updatedNotes);
      }
    } else {
      setState(s => {
        const comps = firebaseToArray(s.savedComps) || [];
        const updated = comps.map(c =>
          String(c.id) === String(compId)
            ? { ...c, notes: firebaseToArray(c.notes).filter(n => String(n.id) !== String(noteId)) }
            : c
        );
        return { ...s, savedComps: updated };
      });
    }
  }, [lobbyCode, setState]);

  const saveComp = useCallback(async (comp) => {
    const me = state.players.find(p => p.id === state.myUserId) || state.players[0];
    const displayName = me ? me.name : 'Teammate';

    let activeNotes = [];
    if (window.db && lobbyCode) {
      try {
        const activeNotesRef = window.db.ref(`rooms/${lobbyCode}/state/activeCompNotes`);
        const notesSnapshot = await activeNotesRef.once('value');
        activeNotes = firebaseToArray(notesSnapshot.val());
      } catch (e) {
        console.error("Error reading activeCompNotes from Firebase during save:", e);
      }
    }
    if (!activeNotes || activeNotes.length === 0) {
      activeNotes = firebaseToArray(comp?.notes || state.activeCompNotes || []);
    }

    const currentNotes = firebaseToArray(activeNotes).map(n => ({ ...n }));
    const { notes: _ignoredNotes, ...compData } = comp || {};

    const newComp = {
      ...compData,
      id: Date.now(),
      savedAt: new Date().toLocaleDateString(),
      savedBy: state.myUserId,
      savedByName: displayName,
      notes: currentNotes,
    };

    let existing = [];
    if (window.db && lobbyCode) {
      const ref = window.db.ref(`rooms/${lobbyCode}/state/savedComps`);
      const snapshot = await ref.once('value');
      existing = sanitizeSavedComps(snapshot.val()) || [];
    } else {
      existing = sanitizeSavedComps(state.savedComps) || [];
    }

    const updated = [newComp, ...existing].slice(0, 30);

    if (window.db && lobbyCode) {
      await window.db.ref(`rooms/${lobbyCode}/state/savedComps`).set(updated);
      await window.db.ref(`rooms/${lobbyCode}/state/activeCompNotes`).set([]);
    }

    setState(s => ({
      ...s,
      savedComps: updated,
      activeCompNotes: [],
    }));

    return newComp;
  }, [lobbyCode, state.players, state.myUserId, state.activeCompNotes, state.savedComps, setState]);

  const deleteComp = useCallback(async (compId) => {
    const ref = window.db.ref(`rooms/${lobbyCode}/state/savedComps`);
    const snapshot = await ref.once('value');
    const existing = firebaseToArray(snapshot.val()) || [];
    const updated = existing.filter(c => c.id !== compId);
    await ref.set(updated);
  }, [lobbyCode]);

  const renameComp = useCallback(async (compId, newName) => {
    const ref = window.db.ref(`rooms/${lobbyCode}/state/savedComps`);
    const snapshot = await ref.once('value');
    const existing = firebaseToArray(snapshot.val()) || [];
    const updated = existing.map(c => c.id === compId ? { ...c, name: newName } : c);
    await ref.set(updated);
  }, [lobbyCode]);

  const updateSavedCompRole = useCallback(async (compId, pos, newRoleData) => {
    setState(s => {
      const comps = firebaseToArray(s.savedComps) || [];
      const updated = comps.map(c => c.id === compId ? { ...c, [pos]: newRoleData } : c);
      return { ...s, savedComps: updated };
    });

    if (window.db && lobbyCode) {
      const ref = window.db.ref(`rooms/${lobbyCode}/state/savedComps`);
      const snapshot = await ref.once('value');
      const existing = firebaseToArray(snapshot.val()) || [];
      const updated = existing.map(c => c.id === compId ? { ...c, [pos]: newRoleData } : c);
      await ref.set(updated);
    }
  }, [lobbyCode, setState]);

  const updateClashReadiness = useCallback((playerId, field, value) => {
    setState(s => ({
      ...s,
      clashReadiness: {
        ...s.clashReadiness,
        [playerId]: { ...(s.clashReadiness[playerId] || {}), [field]: value }
      }
    }));
  }, []);

  const db = window.db;

  const addSubstitute = useCallback(() => {
    setState(s => {
      const subCount = s.players.filter(p => p.isSub).length;
      const sub = {
        id: Date.now(),
        name: `Sub ${subCount + 1}`,
        position: 'TOP',
        pool: [],
        isSub: true,
        subRoles: ['TOP'],
        available: true,
        ownerId: null,
        identityColor: '#F7DFA0',
      };
      const newIndex = s.players.length;
      const newPlayers = [...s.players, sub];
      if (window.db && lobbyCode) {
        window.db.ref(`rooms/${lobbyCode}/state/players/${newIndex}`).set(sub);
      }
      return { ...s, players: newPlayers };
    });
  }, [lobbyCode]);

  const removePlayer = useCallback((playerId) => {
    setState(s => {
      const idx = s.players.findIndex(p => p.id === playerId);
      const newPlayers = s.players.filter(p => p.id !== playerId);
      if (window.db && lobbyCode && idx !== -1) {
        window.db.ref(`rooms/${lobbyCode}/state/players/${idx}`).remove();
      }
      return { ...s, players: newPlayers };
    });
  }, [lobbyCode]);

  const setPlayerAvailability = useCallback((playerId, available) => {
    setState(s => {
      const idx = s.players.findIndex(p => p.id === playerId);
      if (idx === -1) return s;
      db.ref(`rooms/${lobbyCode}/state/players/${idx}/available`).set(available);
      return { ...s, players: s.players.map(p => p.id === playerId ? { ...p, available } : p) };
    });
  }, [lobbyCode]);

  const toggleSubRole = useCallback((playerId, role) => {
    setState(s => {
      const idx = s.players.findIndex(p => p.id === playerId);
      if (idx === -1) return s;
      const player = s.players[idx];
      const cur = player.subRoles || [];
      const next = cur.includes(role) ? cur.filter(r => r !== role) : [...cur, role];
      const subRoles = next.length ? next : cur;
      db.ref(`rooms/${lobbyCode}/state/players/${idx}/subRoles`).set(subRoles);
      return { ...s, players: s.players.map(p => p.id === playerId ? { ...p, subRoles } : p) };
    });
  }, [lobbyCode]);

  const claimSubstitute = useCallback((fromUserId, subRoles = ['TOP']) => {
    setState(s => {
      const me = s.players.find(p => p.id === fromUserId);
      const fromIdx = s.players.findIndex(p => p.id === fromUserId);
      const newId = Date.now();
      const sub = {
        id: newId,
        name: me?.name || 'Sub',
        position: subRoles[0] || 'TOP',
        pool: [],
        isSub: true,
        subRoles,
        available: true,
        ownerId: sessionUserId,
        identityColor: me?.identityColor || '#F7DFA0',
        claimed: true,
      };
      const newIndex = s.players.length;
      const newPlayers = [
        ...s.players.map(p => p.id === fromUserId && !p.isSub ? { ...p, claimed: false, online: false, ownerId: null } : p),
        sub
      ];
      if (window.db && lobbyCode) {
        if (fromIdx !== -1 && !s.players[fromIdx].isSub) {
          window.db.ref(`rooms/${lobbyCode}/state/players/${fromIdx}`).update({ claimed: false, online: false, ownerId: null });
        }
        window.db.ref(`rooms/${lobbyCode}/state/players/${newIndex}`).set(sub);
      }
      return { ...s, players: newPlayers, myUserId: newId };
    });
  }, [lobbyCode, sessionUserId]);

  const signOutUser = useCallback(async () => {
    try {
      if (window.firebase && window.firebase.auth) {
        await window.firebase.auth().signOut();
      }
    } catch (e) {
      console.warn("Sign out error:", e);
    }

    setLobbyCodeState(null);
    setTempIdentity({ displayName: '', displayColor: '' });
    setUserProfile(null);

    // Destroy stale module constant by regenerating session ID
    resetSessionUserId();

    // Clear session & per-feature local storage
    try {
      localStorage.removeItem('cozydraft-lobbycode');
      localStorage.removeItem('cozydraft-userid');
      localStorage.removeItem('cozydraft-scouting-v1');
      localStorage.removeItem('cozydraft-fearless-v1');
      localStorage.removeItem('cozydraft-fearless-v2');
      localStorage.removeItem('cozydraft-postgame');
      localStorage.removeItem('cozydraft-learn');

      const keysToRemove = [];
      for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        if (key && key.startsWith('cozydraft-room-')) {
          keysToRemove.push(key);
        }
      }
      keysToRemove.forEach(k => localStorage.removeItem(k));
    } catch (e) {}

    setState(s => ({
      ...INITIAL_STATE,
      lobbyCode: null,
      myUserId: null,
      activePlayerId: null,
      players: DEFAULT_PLAYERS.map(p => ({ ...p })),
      onboarded: false,
    }));
  }, [setState]);

  const championIconUrl = (championId) =>
    `https://ddragon.leagueoflegends.com/cdn/${ddragonVersion}/img/champion/${championId}.png`;

  const ctx = {
    ...state,
    savedComps: sanitizeSavedComps(state.savedComps),
    scoutingReports: sanitizeScoutingReports(state.scoutingReports),
    activeScoutingReportId: localActiveScoutingReportId,
    createScoutingReport,
    updateScoutingReport,
    updateScoutingEnemy,
    deleteScoutingReport,
    setActiveScoutingReport,
    renameComp,
    updateSavedCompRole,
    fearlessState: sanitizeFearlessSeries(state.fearlessSeries) || null,
    updateFearlessState: updateFearlessSeries,
    fearlessSeries: sanitizeFearlessSeries(state.fearlessSeries) || null,
    updateFearlessSeries,
    fearlessSeriesHistory: state.fearlessSeriesHistory || null,
    updateFearlessSeriesHistory,
    getEffectivePlayerPool,
    accountPoolsByUid,
    get sessionUserId() {
      return getOrGenerateSessionUserId();
    },
    resetSessionUserId,
    signOutUser,
    currentTab,
    champions,
    championsLoading,
    ddragonVersion,
    toast,
    showToast,
    setTab,
    setActivePlayer,
    updateTeamName,
    updatePlayerName,
    updatePlayerIdentity,
    claimLane,
    releaseLane,
    leaveLobby,
    setMyUser,
    setLobbyCode,
    setOnboarded,
    resetLobby,
    kickPlayer,
    addToPool,
    removeFromPool,
    updatePoolEntry,
    toggleFocusPick,
    setActiveComp,
    setActiveCompNotes,
    cleanActiveCompNotes,
    addActiveCompNote, editActiveCompNote, deleteActiveCompNote,
    addSavedCompNote, addNoteToComp, editSavedCompNote, deleteSavedCompNote,
    saveComp,
    deleteComp,
    updateClashReadiness,
    addSubstitute,
    removePlayer,
    setPlayerAvailability,
    toggleSubRole,
    claimSubstitute,
    championIconUrl,
    analyzeComp,
    createNewLobby,
    tempIdentity,
    setTempIdentity,
    userProfile,
    userProfilesByUid,
    myTeams,
    isSynced,
    DEFAULT_PLAYERS,
    POSITIONS,
    POSITION_EMOJIS,
    POSITION_LABELS,
    POSITION_COLORS,
    FLOWER_COLORS,
    IDENTITY_COLORS,
  };

  return <AppContext.Provider value={ctx}>{children}</AppContext.Provider>;
}

const useApp = () => useContext(AppContext);

Object.assign(window, {
  AppProvider, useApp, DEFAULT_PLAYERS,
  POSITIONS, POSITION_EMOJIS, POSITION_LABELS, POSITION_COLORS, FLOWER_COLORS,
  IDENTITY_COLORS, analyzeComp, sessionUserId, firebaseToArray,
});
