import { useEffect, useRef, useState, useCallback } from 'react'

export interface WindowSize {
  width: number
  height: number
}

export interface UseWindowSizeOptions {
  /**
   * Debounce delay in milliseconds before reporting resize.
   * Only the final size after resize stops is reported.
   * @default 500
   */
  debounceMs?: number

  /**
   * Callback fired when resize is detected (after debounce).
   * Use this to track resize events in analytics.
   */
  onResize?: (size: WindowSize) => void

  /**
   * Whether to track resizes at all.
   * When false, onResize is never called.
   * @default true
   */
  enabled?: boolean
}

/**
 * Hook that returns the current window size and optionally
 * tracks resize events with debouncing.
 *
 * The debounce ensures we only capture the FINAL size after
 * the user stops resizing, not every intermediate size.
 *
 * @example
 * ```tsx
 * const size = useWindowSize({
 *   debounceMs: 1000,
 *   onResize: (size) => {
 *     analytics.trackResize(size)
 *   }
 * })
 * ```
 */
export function useWindowSize(options: UseWindowSizeOptions = {}): WindowSize {
  const { debounceMs = 500, onResize, enabled = true } = options

  // Initialize with current window size (or 0 for SSR)
  const [size, setSize] = useState<WindowSize>(() => ({
    width: typeof window !== 'undefined' ? window.innerWidth : 0,
    height: typeof window !== 'undefined' ? window.innerHeight : 0,
  }))

  // Refs for debounce timer and previous size
  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const previousSizeRef = useRef<WindowSize>(size)
  const isFirstRenderRef = useRef(true)

  // Memoized resize handler
  const handleResize = useCallback(() => {
    // Clear any pending debounce timer
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current)
    }

    // Set new debounce timer
    timeoutRef.current = setTimeout(() => {
      const newSize: WindowSize = {
        width: window.innerWidth,
        height: window.innerHeight,
      }

      // Only update if size actually changed
      const hasChanged =
        newSize.width !== previousSizeRef.current.width ||
        newSize.height !== previousSizeRef.current.height

      if (hasChanged) {
        previousSizeRef.current = newSize
        setSize(newSize)

        // Skip callback on first render (initial load, not a resize)
        if (!isFirstRenderRef.current && enabled) {
          onResize?.(newSize)
        }
        isFirstRenderRef.current = false
      }
    }, debounceMs)
  }, [debounceMs, onResize, enabled])

  useEffect(() => {
    // Guard for SSR
    if (typeof window === 'undefined') {
      return
    }

    // Mark first render complete after mount
    isFirstRenderRef.current = false

    // Listen for resize events with passive option for performance
    window.addEventListener('resize', handleResize, { passive: true })

    // Cleanup
    return () => {
      window.removeEventListener('resize', handleResize)
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current)
      }
    }
  }, [handleResize])

  return size
}

/**
 * Lightweight version that only returns current size without tracking.
 * Use when you just need dimensions without the resize callback.
 */
export function useViewportSize(): WindowSize {
  const [size, setSize] = useState<WindowSize>(() => ({
    width: typeof window !== 'undefined' ? window.innerWidth : 0,
    height: typeof window !== 'undefined' ? window.innerHeight : 0,
  }))

  useEffect(() => {
    if (typeof window === 'undefined') return

    const handleResize = () => {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight,
      })
    }

    window.addEventListener('resize', handleResize, { passive: true })
    return () => window.removeEventListener('resize', handleResize)
  }, [])

  return size
}
