// Live data ticker — appends logs, jitters latency, simulates new mod actions.
// Components subscribe via useLive() to re-render on tick.

const LIVE = (() => {
  // We mutate copies of the seed data; pages will read from these arrays.
  const state = {
    logs: [...window.LOGS],
    latency: [...window.LATENCY],
    modActions: [...window.MOD_ACTIONS],
    cmdsToday: window.TOTAL_USES_24H,
    paused: false,
    realMode: false, // when true, real logs from the bot drive the stream
    listeners: new Set(),
  };

  // Map a real bot log {t(epoch ms), level, message} → the stream shape used by pages.
  function mapRealLog(l) {
    const d = new Date(typeof l.t === 'number' ? l.t : Date.now());
    const t = `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`;
    const level = (l.level === 'warn' || l.level === 'error') ? l.level : 'info';
    return { t, level, source: 'bot', msg: l.message != null ? String(l.message) : '' };
  }

  const SOURCES = ['cmd', 'duty', 'gw', 'sheets', 'mod', 'rate', 'scheduler'];
  const SAMPLE_MSGS = {
    cmd: [
      "/myhours by user 728112… in #bot-commands",
      "/duty panel created in #shift-control",
      "/userinfo for user 102998… returned in 64ms",
      "/active in RP · Bangkok Underground · 21 active",
      "/poll by user 552901… (4 options, 10 minutes)",
      "/8ball by user 991028… answered 'Ask again later.'",
      "/avatar for user 401123…",
      "/serverinfo in Pacific Logistics",
    ],
    duty: [
      "user 884330… clocked IN at Pacific Logistics · shift #4292",
      "user 661044… clocked OUT at Mercy General · 2h 41m",
      "user 729811… clocked IN at Highway Patrol HQ · shift #4293",
      "user 102998… break started at Saint Andrews PD",
    ],
    gw: [
      "heartbeat ack · latency 41ms",
      "heartbeat ack · latency 38ms",
      "heartbeat ack · latency 47ms",
      "heartbeat ack · latency 52ms",
    ],
    sheets: [
      "appended 8 rows to 'Shift Log · May 2026' (198ms)",
      "appended 4 rows to 'Shift Log · May 2026' (156ms)",
      "sync ok · 1 worksheet updated",
    ],
    mod: [
      "/timeout 10m → user 552901… reason: caps lock (by mod 401123…)",
      "/clear 12 in #spam-traps by mod 401123…",
      "/warn → user 661044… reason: rule 3 OOC",
    ],
    rate: [
      "rate limit avoided (route: /channels/:id/messages) backoff 180ms",
      "rate limit avoided (route: /interactions) backoff 220ms",
    ],
    scheduler: [
      "scheduler tick · 0 due reminders",
      "scheduler tick · 1 reminder fired",
    ],
  };

  let tickN = 0;
  function tick() {
    // While connected to a real bot, real logs drive the stream — skip the simulator.
    if (state.paused || state.realMode) { schedule(); return; }
    tickN++;

    // Latency jitter
    const prev = state.latency[state.latency.length - 1];
    let nxt = prev + (Math.random() - 0.5) * 14;
    if (Math.random() < 0.04) nxt += 60; // occasional spike
    nxt = Math.max(28, Math.min(180, nxt));
    state.latency = [...state.latency.slice(-119), Math.round(nxt)];

    // Append 1-3 new log lines
    const n = 1 + Math.floor(Math.random() * 2);
    const now = new Date();
    for (let i = 0; i < n; i++) {
      const src = SOURCES[Math.floor(Math.random() * SOURCES.length)];
      const msgs = SAMPLE_MSGS[src];
      const msg = msgs[Math.floor(Math.random() * msgs.length)];
      const level = src === 'rate' ? 'warn' : (Math.random() < 0.03 ? 'error' : 'info');
      const t = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
      state.logs = [{ t, level, source: src, msg }, ...state.logs].slice(0, 200);
      if (src === 'cmd') state.cmdsToday += 1;
    }

    state.listeners.forEach((cb) => cb());
    schedule();
  }
  function schedule() {
    setTimeout(tick, 2200 + Math.random() * 1800);
  }
  schedule();

  function subscribe(cb) {
    state.listeners.add(cb);
    return () => state.listeners.delete(cb);
  }

  return {
    get logs() { return state.logs; },
    get latency() { return state.latency; },
    // While connected to a real bot, mirror the real 24h count instead of the sim total.
    get cmdsToday() { return state.realMode ? (window.TOTAL_USES_24H ?? state.cmdsToday) : state.cmdsToday; },
    get paused() { return state.paused; },
    get realMode() { return state.realMode; },
    pause(v) { state.paused = v; state.listeners.forEach((cb) => cb()); },
    // Replace the stream with real bot logs and pause the random simulator.
    ingestReal(logs) {
      state.realMode = true;
      const mapped = (Array.isArray(logs) ? logs : []).map(mapRealLog);
      // Real logs come oldest→newest (or arbitrary); show newest first, cap at 200.
      state.logs = mapped.slice().reverse().slice(0, 200);
      state.listeners.forEach((cb) => cb());
    },
    // Hand the stream back to the simulator (called on disconnect).
    setRealMode(v) {
      const was = state.realMode;
      state.realMode = !!v;
      if (was && !state.realMode) {
        // restore seed logs so the simulator has something to build on
        state.logs = [...window.LOGS];
      }
      state.listeners.forEach((cb) => cb());
    },
    subscribe,
  };
})();

function useLive() {
  const [, force] = React.useReducer((x) => x + 1, 0);
  React.useEffect(() => LIVE.subscribe(force), []);
  return LIVE;
}

window.LIVE = LIVE;
window.useLive = useLive;
