// Setup Completion Watcher
//
// Listens for the `taskCompleted` event the TaskEventBus dispatches when any
// task hits a terminal state. If the completed task is the finalize task
// recorded by the Setup Wizard handoff (stashed in sessionStorage), trigger
// the celebratory exit: toast notification, re-enable the topbar nav, then
// navigate to the dashboard.

(function setupCompletionWatcher() {
  function readHandoff() {
    try {
      const raw = sessionStorage.getItem('libreportal_setup_handoff');
      return raw ? JSON.parse(raw) : null;
    } catch { return null; }
  }

  function clearHandoff() {
    try { sessionStorage.removeItem('libreportal_setup_handoff'); } catch { /* noop */ }
  }

  // Floating banner shown across the top of any page while setup tasks are
  // running. Surfaces the X-of-Y completion count from the live TaskEventBus
  // snapshot so the user always knows how far through they are. Vanishes
  // automatically when the finalize event fires (the same handler that runs
  // the toast + dashboard redirect down below).
  let bannerEl = null;

  function isOnTasksPage() {
    const p = window.location.pathname;
    return p === '/tasks' || p.startsWith('/tasks') || p === '/tasks.html';
  }

  function ensureBanner() {
    if (bannerEl && document.body.contains(bannerEl)) return bannerEl;
    bannerEl = document.createElement('div');
    bannerEl.className = 'setup-progress-banner';
    bannerEl.innerHTML = `
      <div class="setup-progress-banner-inner">
        <div class="setup-progress-banner-icon" aria-hidden="true">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="12" cy="12" r="10"/>
            <path d="M12 6v6l4 2"/>
          </svg>
        </div>
        <div class="setup-progress-banner-text">
          <strong>Setting up your install</strong>
          <span class="setup-progress-banner-count">— starting…</span>
        </div>
        <div class="setup-progress-banner-bar"><div class="setup-progress-banner-fill"></div></div>
      </div>
    `;
    document.body.appendChild(bannerEl);
    return bannerEl;
  }

  function removeBanner() {
    if (bannerEl) {
      bannerEl.classList.add('leaving');
      setTimeout(() => { bannerEl?.remove(); bannerEl = null; }, 350);
    }
  }

  // Local cache keyed by task id. SSE bus only registers tasks once they fire
  // an event AFTER it connects, so tasks that already finished before the page
  // loaded are invisible to it. We seed this from /api/tasks on start and
  // refresh from each SSE event so the banner reflects the true count.
  const groupTasks = new Map();

  async function seedGroupTasks() {
    const handoff = readHandoff();
    if (!handoff || !handoff.setupGroup) return;
    try {
      const res = await fetch('/api/tasks', { cache: 'no-store' });
      if (!res.ok) return;
      const all = await res.json();
      for (const t of all) {
        if (t && t.setupGroup === handoff.setupGroup) groupTasks.set(t.id, t);
      }
      refreshBanner();
      // Reload-mid-setup case: if the finalize task already terminated
      // before the page came back up, no SSE event will fire for it. The
      // /api/tasks fetch above is the only signal — kick off the completion
      // handler from here too so the toast/redirect still runs.
      checkAlreadyCompleted();
    } catch { /* network blip — SSE events will fill in the gaps */ }
  }

  function ingestEventTask(detail) {
    const t = detail && detail.task;
    if (!t || !t.id) return;
    const handoff = readHandoff();
    if (!handoff || !handoff.setupGroup) return;
    if (t.setupGroup !== handoff.setupGroup) return;
    groupTasks.set(t.id, t);
  }

  function refreshBanner() {
    if (!isOnTasksPage()) { removeBanner(); return; }
    const handoff = readHandoff();
    if (!handoff || !handoff.setupGroup) { removeBanner(); return; }

    const tasksInGroup = Array.from(groupTasks.values());

    // Trust the handoff for the denominator — see seedGroupTasks for why
    // the SSE bus alone undercounts.
    const total = handoff.totalTaskCount || tasksInGroup.length;
    if (total === 0) return;

    const done = tasksInGroup.filter(t => t.status === 'completed' || t.status === 'failed' || t.status === 'cancelled').length;
    const failed = tasksInGroup.some(t => t.status === 'failed');

    const banner = ensureBanner();
    banner.classList.toggle('failed', failed);
    banner.querySelector('.setup-progress-banner-count').textContent = `— ${done} of ${total} tasks complete`;
    const pct = total ? Math.round((done / total) * 100) : 0;
    banner.querySelector('.setup-progress-banner-fill').style.width = `${pct}%`;
  }

  function showWelcomeToast(installName, success) {
    const sys = (typeof window.ensureNotificationSystem === 'function')
      ? window.ensureNotificationSystem()
      : window.notificationSystem;
    if (!sys || typeof sys.show !== 'function') return;

    if (success) {
      sys.show(
        `Setup complete — welcome aboard, <strong>${installName || 'Quantum Traveler'}</strong>. Your install is ready.`,
        'success'
      );
    } else {
      sys.show(
        `Setup ran into an issue. Check the failed task above for details.`,
        'error'
      );
    }
  }

  function reEnableNav() {
    document.querySelectorAll('.topbar-nav.setup-needed').forEach((nav) => {
      nav.classList.remove('setup-needed');
    });
  }

  function navigateToRecommendedApps() {
    const route = '/apps/recommended';
    if (window.spaClean && typeof window.spaClean.navigate === 'function') {
      window.spaClean.navigate(route);
    } else if (typeof window.navigateToRoute === 'function') {
      window.navigateToRoute(route);
    } else {
      window.location.href = '/apps/recommended';
    }
  }

  // Refresh the banner on every task event so the count stays live.
  const onTaskEvent = (e) => { ingestEventTask(e.detail || {}); refreshBanner(); };
  window.addEventListener('taskCreated', onTaskEvent);
  window.addEventListener('taskUpdated', onTaskEvent);
  window.addEventListener('taskCompleted', onTaskEvent);

  // Re-evaluate on navigation: covers back/forward (popstate) and SPA
  // pushState/replaceState (monkey-patched once). Without this the banner
  // would linger after leaving /tasks (or never appear when arriving there)
  // because nothing else triggers refreshBanner.
  window.addEventListener('popstate', refreshBanner);
  ['pushState', 'replaceState'].forEach((m) => {
    const orig = history[m];
    history[m] = function (...args) {
      const r = orig.apply(this, args);
      refreshBanner();
      return r;
    };
  });

  // Seed once now and again every 3s as a safety net — covers the window
  // between the page loading and the SSE bus connecting, plus any FS-watch
  // hiccups that swallow a task.upsert event.
  seedGroupTasks();
  setInterval(() => {
    if (readHandoff()) seedGroupTasks();
  }, 3000);

  window.addEventListener('taskCompleted', (event) => {
    const detail = event.detail || {};
    const handoff = readHandoff();
    if (!handoff || !handoff.finalizeTaskId) return;
    if (detail.taskId !== handoff.finalizeTaskId) return;

    const success = detail.status === 'completed';
    clearHandoff();

    // Defer to the next frame so the tasks-manager listener (registered on the
    // same `taskCompleted` event) gets to flip the row to "completed" first —
    // otherwise the welcome toast appears while the finalize row still shows
    // "running", which reads as out-of-order.
    requestAnimationFrame(() => {
      removeBanner();
      reEnableNav();
      showWelcomeToast(handoff.installName, success);
      if (success) setTimeout(navigateToRecommendedApps, 1800);
    });
  });

  // Also handle the case where the user reloads the tasks page after the
  // finalize task has already completed — we won't get a fresh `taskCompleted`
  // event, so check the bus snapshot once it's ready.
  function checkAlreadyCompleted() {
    const handoff = readHandoff();
    if (!handoff || !handoff.finalizeTaskId) return;
    // Prefer the /api/tasks-seeded cache (groupTasks) — the SSE bus only
    // knows tasks that emitted events after it connected, so reload-mid-
    // setup leaves it blind to anything that already finished.
    const task = groupTasks.get(handoff.finalizeTaskId)
              || (window.taskEventBus && window.taskEventBus.getTask(handoff.finalizeTaskId));
    if (!task) return;
    if (task.status === 'completed' || task.status === 'failed' || task.status === 'cancelled') {
      const success = task.status === 'completed';
      clearHandoff();
      removeBanner();
      reEnableNav();
      showWelcomeToast(handoff.installName, success);
      if (success) setTimeout(navigateToRecommendedApps, 1800);
    }
  }

  window.addEventListener('taskBusReady', () => {
    checkAlreadyCompleted();
    refreshBanner();
  });
  if (window.taskEventBus && window.taskEventBus.connected) {
    checkAlreadyCompleted();
    refreshBanner();
  }
})();
