/*
 * Shared IntersectionObserver + ResizeObserver (pro defaultView).
 * Statt pro Chart je einen Observer instanziieren wir genau einen pro Document/Window
 * (relevant: Editor-Iframe hat eigenen defaultView). Reduziert bei 10 Charts 20 Observer
 * auf 2 → deutlich weniger OS-Handles und weniger Observer-Dispatch-Overhead.
 * Jeder Chart registriert seinen Callback; Dispatch geschieht per Target-Lookup.
 */

import { IS_MOBILE_GLOBAL } from './rendererPool.js';

const _ceSharedVisibilityIOs = new WeakMap(); // defaultView -> { observer, callbacks: Map<Element, Function> }
const _ceSharedResizeROs = new WeakMap();     // defaultView -> { observer, callbacks: Map<Element, Function> }

export const VIEWPORT_ANIM_MIN_RATIO_GLOBAL = 0.06;
export const VISIBILITY_THRESHOLDS_GLOBAL = [0, 0.06, 0.25, 0.5, 1.0];

function _ceGetSharedVisibilityObserver(defaultView) {
  const view = defaultView || (typeof window !== 'undefined' ? window : null);
  if (!view) return null;
  let rec = _ceSharedVisibilityIOs.get(view);
  if (rec) return rec;
  const IOCtor = view.IntersectionObserver || (typeof IntersectionObserver !== 'undefined' ? IntersectionObserver : null);
  if (!IOCtor) return null;
  const callbacks = new Map();
  const observer = new IOCtor((entries) => {
    for (let i = 0; i < entries.length; i++) {
      const entry = entries[i];
      const cb = callbacks.get(entry.target);
      if (cb) { try { cb(entry); } catch (_) { /* ignore */ } }
    }
  }, { threshold: VISIBILITY_THRESHOLDS_GLOBAL, rootMargin: IS_MOBILE_GLOBAL ? '0px' : '-32px' });
  rec = { observer, callbacks };
  _ceSharedVisibilityIOs.set(view, rec);
  return rec;
}

export function observeVisibility(defaultView, target, cb) {
  const rec = _ceGetSharedVisibilityObserver(defaultView);
  if (!rec) return false;
  rec.callbacks.set(target, cb);
  rec.observer.observe(target);
  return true;
}

export function unobserveVisibility(defaultView, target) {
  const rec = _ceSharedVisibilityIOs.get(defaultView || (typeof window !== 'undefined' ? window : null));
  if (!rec) return;
  rec.callbacks.delete(target);
  try { rec.observer.unobserve(target); } catch (_) { /* ignore */ }
}

function _ceGetSharedResizeObserver(defaultView) {
  const view = defaultView || (typeof window !== 'undefined' ? window : null);
  if (!view) return null;
  let rec = _ceSharedResizeROs.get(view);
  if (rec) return rec;
  const ROCtor = view.ResizeObserver || (typeof ResizeObserver !== 'undefined' ? ResizeObserver : null);
  if (!ROCtor) return null;
  const callbacks = new Map();
  const observer = new ROCtor((entries) => {
    for (let i = 0; i < entries.length; i++) {
      const cb = callbacks.get(entries[i].target);
      if (cb) { try { cb(entries[i]); } catch (_) { /* ignore */ } }
    }
  });
  rec = { observer, callbacks };
  _ceSharedResizeROs.set(view, rec);
  return rec;
}

export function observeResize(defaultView, target, cb) {
  const rec = _ceGetSharedResizeObserver(defaultView);
  if (!rec) return false;
  rec.callbacks.set(target, cb);
  rec.observer.observe(target);
  return true;
}

export function unobserveResize(defaultView, target) {
  const rec = _ceSharedResizeROs.get(defaultView || (typeof window !== 'undefined' ? window : null));
  if (!rec) return;
  rec.callbacks.delete(target);
  try { rec.observer.unobserve(target); } catch (_) { /* ignore */ }
}
