/**
 * Lazy loading utilities for performance optimization
 */

import { lazy, ComponentType } from 'react'

/**
 * Create a lazy-loaded component with better error handling
 */
export function createLazyComponent<T extends ComponentType<any>>(
  importFunction: () => Promise<{ default: T }>,
  displayName?: string
): T {
  const LazyComponent = lazy(importFunction)

  if (displayName) {
    ;(LazyComponent as any).displayName = `Lazy(${displayName})`
  }

  return LazyComponent as unknown as T
}

/**
 * Intersection Observer hook for lazy loading elements
 */
import { useEffect, useRef, useState } from 'react'

interface UseIntersectionObserverOptions {
  threshold?: number
  root?: Element | null
  rootMargin?: string
  freezeOnceVisible?: boolean
}

export function useIntersectionObserver(options: UseIntersectionObserverOptions = {}) {
  const { threshold = 0, root = null, rootMargin = '0%', freezeOnceVisible = false } = options

  const [entry, setEntry] = useState<IntersectionObserverEntry>()
  const [node, setNode] = useState<Element | null>(null)
  const observer = useRef<IntersectionObserver | null>(null)

  const frozen = entry?.isIntersecting && freezeOnceVisible

  const updateEntry = ([entry]: IntersectionObserverEntry[]): void => {
    setEntry(entry)
  }

  useEffect(() => {
    const hasIOSupport = !!window.IntersectionObserver

    if (!hasIOSupport || frozen || !node) return

    const observerParams = { threshold, root, rootMargin }
    const isNewObserver = !observer.current
    const hasOptionsChanged =
      observer.current &&
      (observer.current.root !== root ||
        observer.current.rootMargin !== rootMargin ||
        observer.current.thresholds[0] !== threshold)

    if (isNewObserver || hasOptionsChanged) {
      if (observer.current) {
        observer.current.disconnect()
      }
      observer.current = new window.IntersectionObserver(updateEntry, observerParams)
    }

    observer.current.observe(node)

    return () => observer.current?.disconnect()
  }, [node, threshold, root, rootMargin, frozen])

  const cleanup = () => {
    if (observer.current) {
      observer.current.disconnect()
      observer.current = null
    }
  }

  useEffect(() => {
    return cleanup
  }, [])

  return [setNode, !!entry?.isIntersecting, entry, cleanup] as const
}

/**
 * Lazy loading image component
 */
import Image from 'next/image'
import { cn } from './utils'

interface LazyImageProps {
  src: string
  alt: string
  width?: number
  height?: number
  className?: string
  fill?: boolean
  placeholder?: 'blur' | 'empty'
  blurDataURL?: string
  priority?: boolean
  sizes?: string
}

export function LazyImage({
  src,
  alt,
  width,
  height,
  className,
  fill = false,
  placeholder = 'empty',
  blurDataURL,
  priority = false,
  sizes,
  ...props
}: LazyImageProps) {
  const [setRef, isVisible] = useIntersectionObserver({
    threshold: 0.1,
    freezeOnceVisible: true,
  })

  return (
    <div ref={setRef} className={cn('overflow-hidden', className)}>
      {(isVisible || priority) && (
        <Image
          src={src}
          alt={alt}
          width={width}
          height={height}
          fill={fill}
          placeholder={placeholder}
          blurDataURL={blurDataURL}
          priority={priority}
          sizes={sizes}
          className="transition-opacity duration-300"
          {...props}
        />
      )}
    </div>
  )
}

/**
 * Lazy loading wrapper for any content
 */
interface LazyContentProps {
  children: React.ReactNode
  className?: string
  fallback?: React.ReactNode
  threshold?: number
  rootMargin?: string
}

export function LazyContent({
  children,
  className,
  fallback = null,
  threshold = 0.1,
  rootMargin = '50px',
}: LazyContentProps) {
  const [setRef, isVisible] = useIntersectionObserver({
    threshold,
    rootMargin,
    freezeOnceVisible: true,
  })

  return (
    <div ref={setRef} className={className}>
      {isVisible ? children : fallback}
    </div>
  )
}

/**
 * Preload images for better performance
 */
export function preloadImage(src: string): Promise<void> {
  return new Promise((resolve, reject) => {
    const img = new window.Image()
    img.onload = () => resolve()
    img.onerror = reject
    img.src = src
  })
}

/**
 * Preload multiple images
 */
export async function preloadImages(sources: string[]): Promise<void[]> {
  return Promise.all(sources.map(preloadImage))
}
