import { useEffect, useState } from 'react';

// Breakpoints (px): mobile <768, tablet 768-1023, desktop >=1024.
const MOBILE_MAX = 767;
const TABLET_MAX = 1023;

export interface ViewportSnapshot {
  isMobile: boolean;
  isTablet: boolean;
  width: number;
}

function snapshot(): ViewportSnapshot {
  if (typeof window === 'undefined') {
    return { isMobile: false, isTablet: false, width: 1280 };
  }
  const width = window.innerWidth;
  return {
    isMobile: width <= MOBILE_MAX,
    isTablet: width > MOBILE_MAX && width <= TABLET_MAX,
    width,
  };
}

// matchMedia-based, no resize polling. We listen to two breakpoint queries
// and recompute the snapshot on any change.
export function useViewport(): ViewportSnapshot {
  const [state, setState] = useState<ViewportSnapshot>(snapshot);

  useEffect(() => {
    if (typeof window === 'undefined') return;
    const mobileMq = window.matchMedia(`(max-width: ${MOBILE_MAX}px)`);
    const tabletMq = window.matchMedia(`(min-width: ${MOBILE_MAX + 1}px) and (max-width: ${TABLET_MAX}px)`);
    const update = () =>
      setState((prev) => {
        const next = snapshot();
        // Bail if nothing changed — visualViewport 'resize' fires on every
        // URL-bar show/hide and scroll, and a fresh object would re-render
        // every consumer needlessly.
        if (
          prev.isMobile === next.isMobile &&
          prev.isTablet === next.isTablet &&
          prev.width === next.width
        ) {
          return prev;
        }
        return next;
      });

    // matchMedia 'change' alone is not enough on iOS Safari/Vivaldi: when a
    // backgrounded tab is restored (bfcache) or refocused, no 'change' fires,
    // and the width captured at first paint can be a stale/oversized value
    // (iOS reports the wrong innerWidth for a beat before layout settles). That
    // leaves isMobile=false on a phone, so the sidebar renders as a permanent
    // desktop column with no way to close it. Re-snapshot on every signal that
    // accompanies a rejoin/viewport correction, not just breakpoint crossings.
    const onVisibility = () => {
      if (document.visibilityState === 'visible') update();
    };
    mobileMq.addEventListener('change', update);
    tabletMq.addEventListener('change', update);
    window.addEventListener('resize', update);
    window.addEventListener('orientationchange', update);
    window.addEventListener('pageshow', update);
    document.addEventListener('visibilitychange', onVisibility);
    window.visualViewport?.addEventListener('resize', update);
    update();
    return () => {
      mobileMq.removeEventListener('change', update);
      tabletMq.removeEventListener('change', update);
      window.removeEventListener('resize', update);
      window.removeEventListener('orientationchange', update);
      window.removeEventListener('pageshow', update);
      document.removeEventListener('visibilitychange', onVisibility);
      window.visualViewport?.removeEventListener('resize', update);
    };
  }, []);

  return state;
}
