// ViG Command Center — Alliance War Room: editable store, calcs, forecasting
const { useState: useStateAS, useEffect: useEffectAS, useMemo: useMemoAS, useCallback: useCbAS, createContext: createCtxAS, useContext: useCtxAS } = React;

const AL_KEY = 'vig2_alliance_data_v1';
const r1 = x => Math.round(x * 10) / 10;
const KEEPS = ['K44', 'K45', 'K46', 'K47', 'K48', 'K49', 'K50'];

/* recompute alliance aggregates from its players */
function recompute(al, months) {
  const power = r1(al.players.reduce((s, p) => s + (+p.power || 0), 0));
  const add = r1(al.players.reduce((s, p) => s + (+p.add || 0), 0));
  const keeps = {}; KEEPS.forEach(k => keeps[k] = 0);
  al.players.forEach(p => { if (keeps[p.keep] != null) keeps[p.keep]++; });
  const history = (months || []).map((_, m) => r1(al.players.reduce((s, p) => s + ((p.history && p.history[m]) || 0), 0)));
  return { ...al, power, add, keeps, history };
}

/* deterministic modeled history for a newly added/edited player */
function modelHistory(power, add, months) {
  const n = months.length, recent = Math.max(add, 0.4);
  const total = Math.min(recent * 3.2 + power * 0.06, power * 0.5);
  const w = months.map((_, m) => 0.6 + (m / (n - 1)) * 1.1 + 0.25);
  const ws = w.reduce((s, x) => s + x, 0);
  const incs = w.map(x => total * x / ws);
  const hist = []; let p = power;
  for (let m = n - 1; m >= 0; m--) { hist[m] = r1(p); p -= incs[m]; }
  return hist;
}

const AllianceCtx = createCtxAS(null);
function AllianceProvider({ children }) {
  const months = window.ALLIANCE.meta.months;
  const [data, setData] = useStateAS(() => {
    const stored = LS.get(AL_KEY, null);
    return stored || window.ALLIANCE;
  });
  useEffectAS(() => { LS.set(AL_KEY, data); }, [data]);

  const commit = useCbAS((alliances) => {
    setData(d => ({ ...d, alliances: alliances.map(a => recompute(a, months)) }));
  }, [months]);

  const updatePlayer = useCbAS((alId, name, patch) => {
    setData(d => ({ ...d, alliances: d.alliances.map(a => {
      if (a.id !== alId) return a;
      const players = a.players.map(p => {
        if (p.name !== name) return p;
        const np = { ...p, ...patch };
        if (patch.keep) np.keepNum = parseInt(patch.keep.slice(1));
        if (patch.power != null) np.power = +patch.power;
        if (patch.add != null) np.add = +patch.add;
        return np;
      });
      return recompute({ ...a, players }, months);
    }) }));
  }, [months]);

  const addPlayer = useCbAS((alId, p) => {
    setData(d => ({ ...d, alliances: d.alliances.map(a => {
      if (a.id !== alId) return a;
      const np = { name: p.name, keep: p.keep, keepNum: parseInt(p.keep.slice(1)), power: +p.power, add: +p.add || 0, rank: a.players.length + 1, history: modelHistory(+p.power, +p.add || 0, months) };
      return recompute({ ...a, players: [...a.players, np] }, months);
    }) }));
  }, [months]);

  const removePlayer = useCbAS((alId, name) => {
    setData(d => ({ ...d, alliances: d.alliances.map(a => a.id === alId ? recompute({ ...a, players: a.players.filter(p => p.name !== name) }, months) : a) }));
  }, [months]);

  const reset = useCbAS(() => { setData(window.ALLIANCE); }, []);

  const importData = useCbAS((rows) => {
    // rows: [{alliance, player, keep, power, add}]
    setData(d => {
      const byId = {}; d.alliances.forEach(a => byId[a.id] = { ...a, players: [...a.players] });
      rows.forEach(row => {
        const id = (row.alliance || '').toUpperCase().trim();
        if (!byId[id]) byId[id] = { id, name: id, tag: id, accent: '#FF6B00', players: [], newKeeps: {} };
        const a = byId[id];
        const keep = /^K?\d+$/i.test(row.keep) ? ('K' + String(row.keep).replace(/k/i, '')) : (row.keep || 'K44');
        const ex = a.players.find(p => p.name.toLowerCase() === String(row.player).toLowerCase());
        if (ex) { ex.power = +row.power; ex.add = +row.add || 0; ex.keep = keep; ex.keepNum = parseInt(keep.slice(1)); }
        else a.players.push({ name: row.player, keep, keepNum: parseInt(keep.slice(1)), power: +row.power, add: +row.add || 0, rank: a.players.length + 1, history: modelHistory(+row.power, +row.add || 0, months) });
      });
      return { ...d, alliances: Object.values(byId).map(a => recompute(a, months)) };
    });
  }, [months]);

  /* ---------- live sync ---------- */
  const SYNC_KEY = 'vig2_sync_cfg';
  const [syncCfg, setSyncCfgState] = useStateAS(() => LS.get(SYNC_KEY, {
    proxy: 'https://api.allorigins.win/raw?url=',
    auto: true,
    sources: {
      VIG: 'https://svs.info/server/428/alliance/vig',
      NEF: 'https://svs.info/server/68/alliance/nef',
      RSP: 'https://svs.info/server/302/alliance/rsp',
    },
  }));
  const setSyncCfg = useCbAS((patch) => setSyncCfgState(c => { const n = { ...c, ...patch }; LS.set(SYNC_KEY, n); return n; }), []);

  // parsed: [{ id, power, players:[{name, keep, power, monarch}] }]
  const applySync = useCbAS((parsed) => {
    setData(d => {
      const newAll = d.alliances.map(a => {
        const ps = parsed.find(x => x.id === a.id);
        if (!ps || !ps.players || !ps.players.length) return a;
        const prevByName = {}; a.players.forEach(p => prevByName[p.name.toLowerCase()] = p);
        const newKeeps = {};
        const players = ps.players.map((np, i) => {
          const prev = prevByName[String(np.name).toLowerCase()];
          const power = Math.round((+np.power) * 10) / 10;
          const keepNum = np.keep ? parseInt(String(np.keep).replace(/[^\d]/g, '')) : (prev ? prev.keepNum : 44);
          const keep = 'K' + keepNum;
          const add = prev ? r1(power - prev.power) : 0;
          if (prev && keepNum > prev.keepNum) newKeeps[keep] = (newKeeps[keep] || 0) + 1;
          const history = prev && prev.history ? prev.history.slice(1).concat(power).map(r1) : modelHistory(power, add, months);
          return { name: np.name, keep, keepNum, power, add, monarch: np.monarch != null ? +np.monarch : (prev ? prev.monarch : null), rank: i + 1, history };
        });
        const prevPower = a.power;
        let merged = recompute({ ...a, players, newKeeps }, months);
        if (ps.power) {
          const totalAdd = r1(ps.power - prevPower);
          const hist = merged.history.slice(1).concat(r1(ps.power)).map(r1);
          merged = { ...merged, power: r1(ps.power), add: totalAdd, history: hist, tracked: players.length, memberCount: ps.memberCount || a.memberCount || players.length };
        } else {
          merged = { ...merged, tracked: players.length, memberCount: ps.memberCount || a.memberCount || players.length };
        }
        return merged;
      });
      return { ...d, alliances: newAll, meta: { ...d.meta, lastSync: Date.now(), live: true } };    });
  }, [months]);

  return <AllianceCtx.Provider value={{ data, months, commit, updatePlayer, addPlayer, removePlayer, reset, importData, syncCfg, setSyncCfg, applySync }}>{children}</AllianceCtx.Provider>;
}
const useAlliance = () => useCtxAS(AllianceCtx);

/* ---------- derived analytics ---------- */
const ALCALC = {
  ranked(alliances) { return [...alliances].sort((a, b) => b.power - a.power); },
  allPlayers(alliances) {
    return alliances.flatMap(a => a.players.map(p => ({ ...p, allianceId: a.id, accent: a.accent }))); },
  avgKeep(al) { return al.players.length ? r1(al.players.reduce((s, p) => s + p.keepNum, 0) / al.players.length) : 0; },
  k49share(al) { const n = al.players.filter(p => p.keepNum >= 49).length; return al.players.length ? Math.round(n / al.players.length * 100) : 0; },
  momentum(al) { return al.power ? r1(al.add / al.power * 100) : 0; }, // monthly growth %
  avgPower(al) { return al.players.length ? r1(al.power / al.players.length) : 0; },
  // forecast: linear projection using monthly add
  project(power, add, monthsAhead) { return r1(power + add * monthsAhead); },
  // months until alliance A overtakes B (or null)
  overtakeETA(a, b) {
    if (a.power >= b.power) return 0;
    const rate = a.add - b.add;
    if (rate <= 0) return null;
    return Math.ceil((b.power - a.power) / rate);
  },
  // keep tier power benchmarks across all alliances
  keepBenchmarks(alliances) {
    const byKeep = {};
    ALCALC.allPlayers(alliances).forEach(p => { (byKeep[p.keepNum] = byKeep[p.keepNum] || []).push(p.power); });
    const bm = {};
    Object.entries(byKeep).forEach(([k, arr]) => { arr.sort((x, y) => x - y); bm[k] = { min: arr[0], median: arr[Math.floor(arr.length / 2)], max: arr[arr.length - 1], n: arr.length }; });
    return bm;
  },
  // promotion-ready: power >= median of next keep tier, not yet K50
  promotionReady(alliances) {
    const bm = ALCALC.keepBenchmarks(alliances);
    return ALCALC.allPlayers(alliances).filter(p => {
      if (p.keepNum >= 50) return false;
      const next = bm[p.keepNum + 1];
      return next && p.power >= next.median;
    }).map(p => {
      const next = bm[p.keepNum + 1];
      const over = next ? Math.round((p.power - next.median) / next.median * 100) : 0;
      return { ...p, nextKeep: 'K' + (p.keepNum + 1), over };
    }).sort((a, b) => b.over - a.over);
  },
  // notable changes (big movers + decliners)
  alerts(alliances) {
    const ps = ALCALC.allPlayers(alliances);
    const out = [];
    ps.forEach(p => {
      if (p.add >= 12) out.push({ type: 'surge', player: p.name, allianceId: p.allianceId, value: p.add });
      else if (p.add < 0) out.push({ type: 'decline', player: p.name, allianceId: p.allianceId, value: p.add });
    });
    return out.sort((a, b) => Math.abs(b.value) - Math.abs(a.value));
  },
};

Object.assign(window, { AllianceProvider, useAlliance, ALCALC, AL_KEEPS: KEEPS, alR1: r1 });
