// SPEC: PJ2026-010401 Web工作台 draft-2026-06-20-p1-view-local-timing-ticker; PJ2026-0104010803 唯一投影 draft-2026-06-20-p1-view-local-timing-ticker.
// Responsibility: Shared browser-only render ticker for relative Workbench timing labels; it never writes server-state or projection facts.

import { onBeforeUnmount, readonly, ref, watch, type Ref } from "vue";

const TICK_INTERVAL_MS = 1000;

const nowMs = ref(Date.now());
const activeSubscribers = new Map<symbol, boolean>();
let timer: number | null = null;
let visibilityListenerAttached = false;

export function useWorkbenchNowTicker(enabled: Ref<boolean>) {
  const subscriber = Symbol("workbench-now-ticker");
  activeSubscribers.set(subscriber, false);
  ensureVisibilityListener();

  const stopWatch = watch(() => enabled.value, (value) => {
    activeSubscribers.set(subscriber, value === true);
    reconcileTimer();
  }, { immediate: true });

  onBeforeUnmount(() => {
    stopWatch();
    activeSubscribers.delete(subscriber);
    reconcileTimer();
    detachVisibilityListenerIfIdle();
  });

  return { nowMs: readonly(nowMs), refreshNow };
}

function refreshNow(): void {
  nowMs.value = Date.now();
}

function reconcileTimer(): void {
  // Passive observer pages may be backgrounded while still being the source of
  // web-probe samples. Keep the render ticker alive for active subscribers so
  // elapsed/recent labels advance monotonically instead of jumping on resume.
  const shouldRun = isBrowser() && hasActiveSubscriber();
  if (shouldRun && timer === null) {
    refreshNow();
    timer = window.setInterval(refreshNow, TICK_INTERVAL_MS);
    return;
  }
  if (!shouldRun && timer !== null) {
    window.clearInterval(timer);
    timer = null;
  }
}

function hasActiveSubscriber(): boolean {
  for (const active of activeSubscribers.values()) {
    if (active) return true;
  }
  return false;
}

function ensureVisibilityListener(): void {
  if (visibilityListenerAttached || typeof document === "undefined") return;
  document.addEventListener("visibilitychange", handleVisibilityChange);
  visibilityListenerAttached = true;
}

function detachVisibilityListenerIfIdle(): void {
  if (activeSubscribers.size > 0 || !visibilityListenerAttached || typeof document === "undefined") return;
  document.removeEventListener("visibilitychange", handleVisibilityChange);
  visibilityListenerAttached = false;
}

function handleVisibilityChange(): void {
  refreshNow();
  reconcileTimer();
}

function isBrowser(): boolean {
  return typeof window !== "undefined";
}
