// Stryke — central store: state, navigation, scoring engine, settlement + recap selectors.
const { createContext, useContext, useState, useCallback, useMemo, useRef } = React;

const StrykeCtx = createContext(null);
window.StrykeCtx = StrykeCtx;
window.useStryke = () => useContext(StrykeCtx);

// ── Course: Pine Hollow, par 72
const PARS = [4,4,5,3,4,4,3,5,4, 4,3,4,5,4,4,3,4,5];
const SI   = [7,3,11,17,1,13,15,5,9, 8,18,4,2,12,16,14,10,6]; // stroke index per hole
window.PARS = PARS; window.SI = SI;

// ── Seed players (the Saturday Crew demo round, live thru 14)
const seedPlayers = () => ([
  { id: 'tr', name: 'Trevor Okafor', hcp: 8,  captain: true,  joined: true,  team: 'A', guest: false },
  { id: 'mi', name: 'Mike Reyes',    hcp: 6,  captain: false, joined: true,  team: 'B', guest: false },
  { id: 'de', name: 'Dev Anand',     hcp: 12, captain: false, joined: true,  team: 'A', guest: false },
  { id: 'ga', name: 'Gabe Munoz',    hcp: 16, captain: false, joined: false, team: 'B', guest: true  },
]);

// Seed gross scores thru hole 14 (index 0..13) — Trevor leads, Dev & Mike steal a few skins
const SEED_GROSS = {
  tr: [4,4,5,3,4,4,3,5,4, 4,3,4,5,4],
  mi: [4,3,5,4,5,4,4,6,5, 5,4,5,4,5],
  de: [5,5,4,4,5,5,4,4,5, 5,4,3,6,5],
  ga: [5,6,6,5,5,6,4,6,6, 5,5,5,6,6],
};
const seedScores = () => {
  const s = {};
  for (const pid in SEED_GROSS) { s[pid] = {}; SEED_GROSS[pid].forEach((v, i) => { s[pid][i + 1] = v; }); }
  return s;
};

// Seed dots (side pots / achievements)
const seedDots = () => ([
  { id: 'd1', type: 'Longest drive', hole: 9, player: 'mi', value: 5 },
  { id: 'd2', type: 'Closest to pin', hole: 7, player: 'tr', value: 5 },
  { id: 'd3', type: 'Greenie', hole: 4, player: 'de', value: 5 },
  { id: 'd4', type: 'Birdie', hole: 11, player: 'tr', value: 5 },
]);

// ── strokes a player receives on a hole
function strokesOn(hcp, hole) {
  const idx = SI[hole - 1];
  let s = 0;
  if (idx <= (hcp % 18 || (hcp >= 18 ? 18 : 0))) s += 1;
  s += Math.floor(hcp / 18);
  return s;
}
window.strokesOn = strokesOn;

function StrykeProvider({ children }) {
  const [screen, setScreen] = useState('welcome');
  const [dir, setDir] = useState('fwd');
  const histRef = useRef([]);
  const [tab, setTab] = useState('home');

  const [group] = useState({ name: 'Saturday Crew', founded: '2024' });
  const [players, setPlayers] = useState(seedPlayers);
  const [round, setRound] = useState({
    course: 'Pine Hollow', date: 'Sat · 8:10 AM', week: 7,
    formats: { skins: true, dots: true, nassau: false, match: false, teams: false },
    skinValue: 2, dotValue: 5, netGross: 'net', carryover: true, autoPress: true,
    scoringPath: null, status: 'setup', currentHole: 15,
  });
  const [scores, setScores] = useState(seedScores);
  const [dots, setDots] = useState(seedDots);
  const [paid, setPaid] = useState({});
  const [toast, setToast] = useState(null);

  const nav = useCallback((s, d = 'fwd') => {
    setScreen(prev => { if (prev !== s) histRef.current.push(prev); return s; });
    setDir(d);
    document.querySelector('.stu-scroll')?.scrollTo(0, 0);
  }, []);
  window.__strykeNav = nav;
  const back = useCallback(() => {
    const h = histRef.current;
    if (h.length) { setDir('back'); setScreen(h.pop()); }
  }, []);
  const goTab = useCallback((t) => {
    setTab(t);
    const map = { home: 'home', rounds: 'rounds', standings: 'standings', settings: 'settings' };
    histRef.current = [];
    setDir('back'); setScreen(map[t] || 'home');
  }, []);

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

  // ── mutations
  const setScore = useCallback((pid, hole, val) => {
    setScores(prev => ({ ...prev, [pid]: { ...prev[pid], [hole]: val } }));
  }, []);
  const toggleDot = useCallback((type, hole, pid, value) => {
    setDots(prev => {
      const existing = prev.find(d => d.type === type && d.hole === hole);
      if (existing) return prev.map(d => d === existing ? { ...d, player: pid } : d);
      return [...prev, { id: 'd' + Date.now(), type, hole, player: pid, value }];
    });
  }, []);
  const joinPlayer = useCallback((pid) => setPlayers(ps => ps.map(p => p.id === pid ? { ...p, joined: true } : p)), []);
  const addPlayer = useCallback((name, hcp = 14) => setPlayers(ps => [...ps, { id: 'p' + Date.now(), name, hcp, joined: false, guest: true, team: ps.length % 2 ? 'B' : 'A' }]), []);
  const markPaid = useCallback((key) => setPaid(p => ({ ...p, [key]: !p[key] })), []);

  // ───────── selectors (engine)
  const eng = useMemo(() => {
    const inRound = players;
    const holesPlayed = Math.max(0, ...inRound.map(p => Object.keys(scores[p.id] || {}).length));

    const netStrokes = (pid, hole) => {
      const g = (scores[pid] || {})[hole];
      if (g == null) return null;
      const p = inRound.find(x => x.id === pid);
      return round.netGross === 'net' ? g - strokesOn(p.hcp, hole) : g;
    };
    const grossTotal = (pid) => Object.values(scores[pid] || {}).reduce((a, b) => a + b, 0);
    const netTotal = (pid) => {
      const hs = Object.keys(scores[pid] || {}).map(Number);
      return hs.reduce((a, h) => a + netStrokes(pid, h), 0);
    };
    const toPar = (pid) => {
      const hs = Object.keys(scores[pid] || {}).map(Number);
      const par = hs.reduce((a, h) => a + PARS[h - 1], 0);
      return grossTotal(pid) - par;
    };

    // Skins: per hole lowest net wins; ties carry
    const skinResults = []; // {hole, winner|null, carried, value, pot}
    let carry = 0;
    for (let h = 1; h <= holesPlayed; h++) {
      const vals = inRound.map(p => ({ pid: p.id, n: netStrokes(p.id, h) })).filter(x => x.n != null);
      if (vals.length < inRound.length) break;
      const min = Math.min(...vals.map(v => v.n));
      const low = vals.filter(v => v.n === min);
      const pot = round.skinValue * (carry + 1);
      if (low.length === 1) { skinResults.push({ hole: h, winner: low[0].pid, carried: false, value: pot }); carry = 0; }
      else { skinResults.push({ hole: h, winner: null, carried: true, value: pot }); carry += 1; }
    }
    const skinsWon = {}; inRound.forEach(p => skinsWon[p.id] = 0);
    let skinsValueWon = {}; inRound.forEach(p => skinsValueWon[p.id] = 0);
    skinResults.forEach(r => { if (r.winner) { skinsWon[r.winner] += 1; skinsValueWon[r.winner] += r.value; } });
    const carryNow = carry; // skins riding into next hole
    const carryPot = round.skinValue * (carry + (carry ? 0 : 0)) + (carry ? round.skinValue * carry : 0);

    // Dots
    const dotsWon = {}; inRound.forEach(p => dotsWon[p.id] = 0);
    const dotValueWon = {}; inRound.forEach(p => dotValueWon[p.id] = 0);
    dots.forEach(d => { if (d.player) { dotsWon[d.player] += 1; dotValueWon[d.player] += d.value; } });

    // Leaderboard sorted by net total asc
    const leaderboard = [...inRound].map(p => ({
      ...p, net: netTotal(p.id), gross: grossTotal(p.id), toPar: toPar(p.id),
      skins: skinsWon[p.id], dots: dotsWon[p.id],
    })).sort((a, b) => a.net - b.net);

    // Settlement: winnings pooled, funded equally, minimize transfers
    const winnings = {}; let pot = 0;
    inRound.forEach(p => { winnings[p.id] = skinsValueWon[p.id] + dotValueWon[p.id]; pot += winnings[p.id]; });
    const share = pot / inRound.length;
    const netCash = {}; inRound.forEach(p => netCash[p.id] = Math.round(winnings[p.id] - share));
    // fix rounding sum to 0
    const drift = inRound.reduce((a, p) => a + netCash[p.id], 0);
    if (drift !== 0) { const top = [...inRound].sort((a, b) => netCash[b.id] - netCash[a.id])[0]; netCash[top.id] -= drift; }

    let creditors = inRound.filter(p => netCash[p.id] > 0).map(p => ({ id: p.id, amt: netCash[p.id] })).sort((a, b) => b.amt - a.amt);
    let debtors = inRound.filter(p => netCash[p.id] < 0).map(p => ({ id: p.id, amt: -netCash[p.id] })).sort((a, b) => b.amt - a.amt);
    const transfers = [];
    let ci = 0, di = 0;
    creditors = creditors.map(c => ({ ...c })); debtors = debtors.map(d => ({ ...d }));
    while (ci < creditors.length && di < debtors.length) {
      const c = creditors[ci], d = debtors[di];
      const amt = Math.min(c.amt, d.amt);
      if (amt > 0) transfers.push({ from: d.id, to: c.id, amount: amt });
      c.amt -= amt; d.amt -= amt;
      if (c.amt === 0) ci++; if (d.amt === 0) di++;
    }

    const nameOf = (id) => inRound.find(p => p.id === id)?.name.split(' ')[0] || id;

    return {
      inRound, holesPlayed, netStrokes, grossTotal, netTotal, toPar,
      skinResults, skinsWon, skinsValueWon, carryNow, carryPot: round.skinValue * (carryNow ? carryNow + 1 : 1),
      dotsWon, dotValueWon, leaderboard, netCash, transfers, pot, nameOf,
    };
  }, [players, scores, dots, round]);

  const value = {
    screen, dir, nav, back, tab, goTab,
    group, players, setPlayers, round, setRound, scores, setScore, dots, toggleDot,
    joinPlayer, addPlayer, paid, markPaid, eng, toast, flash,
  };
  return <StrykeCtx.Provider value={value}>{children}</StrykeCtx.Provider>;
}

window.StrykeProvider = StrykeProvider;
