/**
 * Card glare effect — cursor-tracking spotlight on .card and .template-card elements.
 *
 * Uses a single document-level mousemove listener (event delegation) and
 * CSS custom properties (--glare-x, --glare-y) to position a radial gradient
 * overlay via the ::after pseudo-element defined in cards.css.
 *
 * All listeners are owned by an AbortController so callers can call
 * `disposeCardGlare()` to detach everything in one go (e.g. before tests
 * or a hot-reload). Mousemove updates are throttled to one per animation
 * frame, and the cached bounding rect is invalidated on scroll/resize so
 * the spotlight follows layout changes.
 */

const CARD_SEL = '.card, .template-card, .dashboard-target, .perf-chart-card';

let _active: Element | null = null;     // currently illuminated card element
let _cachedRect: DOMRect | null = null; // cached bounding rect for current card
let _controller: AbortController | null = null;

// rAF throttle state
let _pendingX = 0;
let _pendingY = 0;
let _rafScheduled = false;

function _flush(): void {
    _rafScheduled = false;
    if (!_active || !_cachedRect) return;
    if (!(_active instanceof HTMLElement)) return;
    const x = _pendingX - _cachedRect.left;
    const y = _pendingY - _cachedRect.top;
    _active.style.setProperty('--glare-x', `${x}px`);
    _active.style.setProperty('--glare-y', `${y}px`);
}

function _onMove(e: MouseEvent): void {
    if (!(e.target instanceof Element)) return;
    const card = e.target.closest(CARD_SEL);

    if (card && !card.classList.contains('add-device-card')) {
        if (_active !== card) {
            if (_active) _active.classList.remove('card-glare');
            card.classList.add('card-glare');
            _active = card;
            _cachedRect = card.getBoundingClientRect();
        }
        _pendingX = e.clientX;
        _pendingY = e.clientY;
        if (!_rafScheduled) {
            _rafScheduled = true;
            requestAnimationFrame(_flush);
        }
    } else if (_active) {
        _active.classList.remove('card-glare');
        _active = null;
        _cachedRect = null;
    }
}

function _onLeave(): void {
    if (_active) {
        _active.classList.remove('card-glare');
        _active = null;
        _cachedRect = null;
    }
}

function _invalidateRect(): void {
    if (_active) _cachedRect = _active.getBoundingClientRect();
}

export function initCardGlare(): void {
    if (_controller) return; // already initialized
    _controller = new AbortController();
    const { signal } = _controller;
    document.addEventListener('mousemove', _onMove, { passive: true, signal });
    document.addEventListener('mouseleave', _onLeave, { signal });
    // Cached rect goes stale when the page scrolls or the viewport resizes —
    // re-measure so the spotlight stays glued to the cursor.
    window.addEventListener('scroll', _invalidateRect, { passive: true, signal });
    window.addEventListener('resize', _invalidateRect, { passive: true, signal });
}

/** Tear down all listeners. Call before hot-reload or in tests. */
export function disposeCardGlare(): void {
    if (_controller) {
        _controller.abort();
        _controller = null;
    }
    if (_active) {
        _active.classList.remove('card-glare');
        _active = null;
    }
    _cachedRect = null;
    _rafScheduled = false;
}
