import { useEffect, useState, useRef, useCallback, type KeyboardEvent as ReactKeyboardEvent } from "react";
import { ProgressSpinner } from 'primereact/progressspinner';
import { Dialog } from 'primereact/dialog';
import { Image } from 'primereact/image';
import IconButton from '@mui/joy/IconButton';
import FileCopyRoundedIcon from '@mui/icons-material/FileCopyRounded';
import { toast } from 'react-toastify';
import { API_URL } from '../config';

const MEME_API_URL = `${API_URL}/memes/list_memes`;
const BASE_IMAGE_URL = "https://codey.horse/meme";

interface MemeImage {
  id: string;
  url: string;
  filename?: string;
  timestamp?: string;
  [key: string]: unknown;
}

interface MemeCache {
  pagesLoaded: Set<number>;
  items: MemeImage[];
}

const Memes: React.FC = () => {
  const [images, setImages] = useState<MemeImage[]>([]);
  const [page, setPage] = useState<number>(1);
  const [loading, setLoading] = useState<boolean>(false);
  const [hasMore, setHasMore] = useState<boolean>(true);
  const [selectedImage, setSelectedImage] = useState<MemeImage | null>(null);
  const [selectedIndex, setSelectedIndex] = useState<number>(-1);
  const [imageLoading, setImageLoading] = useState<Record<string, boolean>>({});
  const observerRef = useRef<IntersectionObserver | null>(null);
  const touchStartRef = useRef<number | null>(null);
  const touchEndRef = useRef<number | null>(null);
  const theme = document.documentElement.getAttribute("data-theme")
  const cacheRef = useRef<MemeCache>({ pagesLoaded: new Set(), items: [] });

  const prefetchImage = useCallback((img: MemeImage | undefined): void => {
    if (!img || typeof window === "undefined") return;
    const preload = new window.Image();
    preload.src = img.url;
  }, []);

  const handleNavigate = useCallback((direction: number): void => {
    setSelectedIndex((prev) => {
      const newIndex = prev + direction;
      if (newIndex < 0 || newIndex >= images.length) {
        return prev;
      }
      const nextImage = images[newIndex];
      setSelectedImage(nextImage);
      prefetchImage(images[newIndex + 1]);
      return newIndex;
    });
  }, [images, prefetchImage]);

  useEffect(() => {
    if (!selectedImage) {
      return;
    }
    const handleKeyDown = (event: globalThis.KeyboardEvent): void => {
      if (event.key === 'ArrowLeft') {
        event.preventDefault();
        handleNavigate(-1);
      } else if (event.key === 'ArrowRight') {
        event.preventDefault();
        handleNavigate(1);
      }
    };

    window.addEventListener('keydown', handleKeyDown);
    return () => {
      window.removeEventListener('keydown', handleKeyDown);
    };
  }, [selectedImage, handleNavigate]);
  useEffect(() => {
    const cached = sessionStorage.getItem("memes-cache");
    if (cached) {
      try {
        const parsed = JSON.parse(cached);
        cacheRef.current = {
          pagesLoaded: new Set(parsed.pagesLoaded || []),
          items: parsed.items || [],
        };
        setImages(parsed.items || []);
        setPage(parsed.nextPage || 1);
        setHasMore(parsed.hasMore ?? true);
      } catch (e) {
        console.warn("Failed to parse meme cache", e);
      }
    }
  }, []);
  const persistCache = useCallback((nextPage: number): void => {
    sessionStorage.setItem("memes-cache", JSON.stringify({
      items: cacheRef.current.items,
      pagesLoaded: Array.from(cacheRef.current.pagesLoaded),
      nextPage,
      hasMore,
    }));
  }, [hasMore]);

  const loadImages = async (pageNum: number, attempt: number = 0): Promise<void> => {
    if (loading || !hasMore || cacheRef.current.pagesLoaded.has(pageNum)) return;
    setLoading(true);
    try {
      const res = await fetch(`${MEME_API_URL}?page=${pageNum}`);

      if (res.status === 429) {
        const backoff = Math.min(1000 * Math.pow(2, attempt), 10000); // max 10s
        console.warn(`Rate limited. Retrying in ${backoff}ms`);
        setTimeout(() => {
          loadImages(pageNum, attempt + 1);
        }, backoff);
        return;
      }

      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const data = await res.json();

      const newMemes = data?.memes || [];

      if (newMemes.length === 0 || data.paging.current >= data.paging.of) {
        setHasMore(false);
      }

      const imageObjects = newMemes.map(m => ({
        id: m.id,
        timestamp: new Date(m.timestamp * 1000)
          .toString().split(" ")
          .splice(0, 4).join(" "),
        url: `${BASE_IMAGE_URL}/${m.id}.png`,
      }));

      cacheRef.current.pagesLoaded.add(pageNum);
      cacheRef.current.items = [...cacheRef.current.items, ...imageObjects];
      setImages(cacheRef.current.items);
      setPage(prev => {
        const next = prev + 1;
        persistCache(next);
        return next;
      });
    } catch (e) {
      console.error("Failed to load memes", e);
      toast.error("Failed to load more memes.");
    } finally {
      setLoading(false);
    }
  };

  const lastImageRef = useCallback(node => {
    if (loading) return;
    if (observerRef.current) observerRef.current.disconnect();
    observerRef.current = new IntersectionObserver(entries => {
      if (entries[0].isIntersecting && hasMore) {
        loadImages(page);
      }
    });
    if (node) observerRef.current.observe(node);
  }, [loading, hasMore, page]);

  useEffect(() => {
    if (!cacheRef.current.pagesLoaded.size) {
      loadImages(1);
    }
  }, []);

  const canGoPrev = selectedIndex > 0;
  const canGoNext = selectedIndex >= 0 && selectedIndex < images.length - 1;

  const closeDialog = useCallback(() => {
    setSelectedImage(null);
    setSelectedIndex(-1);
  }, []);

  // Touch swipe handlers for mobile navigation
  const handleTouchStart = useCallback((e) => {
    touchStartRef.current = e.touches[0].clientX;
    touchEndRef.current = null;
  }, []);

  const handleTouchMove = useCallback((e) => {
    touchEndRef.current = e.touches[0].clientX;
  }, []);

  const handleTouchEnd = useCallback(() => {
    if (!touchStartRef.current || !touchEndRef.current) return;
    const distance = touchStartRef.current - touchEndRef.current;
    const minSwipeDistance = 50;

    if (Math.abs(distance) > minSwipeDistance) {
      if (distance > 0) {
        // Swiped left -> next
        handleNavigate(1);
      } else {
        // Swiped right -> prev
        handleNavigate(-1);
      }
    }
    touchStartRef.current = null;
    touchEndRef.current = null;
  }, [handleNavigate]);

  // Track image loading state
  const handleImageLoad = useCallback((id) => {
    setImageLoading(prev => ({ ...prev, [id]: false }));
  }, []);

  const handleImageLoadStart = useCallback((id) => {
    setImageLoading(prev => ({ ...prev, [id]: true }));
  }, []);

  const handleCopyImage = useCallback(async () => {
    if (!selectedImage) return;
    try {
      const res = await fetch(selectedImage.url, { mode: "cors" });
      const blob = await res.blob();
      if (!navigator.clipboard?.write) {
        throw new Error("Clipboard API not available");
      }
      await navigator.clipboard.write([
        new window.ClipboardItem({ [blob.type || "image/png"]: blob })
      ]);
      toast.success("Image copied", { autoClose: 1500 });
    } catch (err) {
      console.error("Copy image failed", err);
      toast.error("Unable to copy image");
    }
  }, [selectedImage]);

  return (
    <>
      <div className="grid-container">
        {images.map((img, i) => {
          const isLast = i === images.length - 1;
          const isLoading = imageLoading[img.id] !== false;
          return (
            <div
              key={img.id}
              className="grid-item"
              ref={isLast ? lastImageRef : null}
              onClick={() => {
                setSelectedImage(img);
                setSelectedIndex(i);
                prefetchImage(images[i + 1]);
              }}
              style={{ cursor: 'pointer', position: 'relative' }}
            >
              {isLoading && (
                <div className="meme-skeleton" />
              )}
              <Image
                src={img.url}
                alt={`meme-${img.id}`}
                imageClassName={`meme-img ${isLoading ? 'meme-img-loading' : ''}`}
                loading="lazy"
                onLoad={() => handleImageLoad(img.id)}
                onLoadStart={() => handleImageLoadStart(img.id)}
              />
            </div>
          );
        })}
        {loading && (
          <div className="loading">
            <ProgressSpinner style={{ width: '50px', height: '50px' }} />
          </div>
        )}
      </div>

      {/* Dialog for enlarged image */}
      <Dialog
        header={
          <div className="meme-dialog-header">
            <span>Meme #{selectedImage?.id} - {selectedImage?.timestamp}</span>
            <div className="meme-dialog-actions">
              <div className="meme-dialog-tooltip-wrapper">
                <IconButton
                  size="sm"
                  variant="plain"
                  color="neutral"
                  aria-label="Copy image"
                  onClick={handleCopyImage}
                >
                  <FileCopyRoundedIcon fontSize="small" />
                </IconButton>
                <span className="meme-dialog-tooltip">Copy image</span>
              </div>
            </div>
          </div>
        }
        visible={!!selectedImage}
        onHide={closeDialog}
        style={{ width: '90vw', maxWidth: '720px' }}
        className={`d-${theme}`}
        modal
        closable
        dismissableMask={true}
      >
        {selectedImage && (
          <div
            className="meme-dialog-body"
            onTouchStart={handleTouchStart}
            onTouchMove={handleTouchMove}
            onTouchEnd={handleTouchEnd}
          >
            <button
              type="button"
              className="meme-dialog-nav meme-dialog-nav-prev"
              onClick={() => handleNavigate(-1)}
              disabled={!canGoPrev}
              aria-label="Previous meme"
            >
              ‹
            </button>
            <img
              src={selectedImage.url}
              alt={`meme-${selectedImage.id}`}
              style={{
                maxWidth: '100%',
                maxHeight: '70vh',
                objectFit: 'contain',
                display: 'block',
                margin: '0 auto',
                borderRadius: '6px',
              }}
            />
            <button
              type="button"
              className="meme-dialog-nav meme-dialog-nav-next"
              onClick={() => handleNavigate(1)}
              disabled={!canGoNext}
              aria-label="Next meme"
            >
              ›
            </button>
          </div>

        )}
      </Dialog>
    </>
  );
};

export default Memes;
