import React, { useEffect, useState, useRef, useCallback } from "react";
import { Paper, Stack, Text, ScrollArea, Loader, Center, Button, Group, NumberInput, useMantineTheme, ActionIcon, Box, Tabs } from "@mantine/core";
import { useTranslation } from "react-i18next";
import { pdfWorkerManager } from "../../services/pdfWorkerManager";
import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew";
import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
import FirstPageIcon from "@mui/icons-material/FirstPage";
import LastPageIcon from "@mui/icons-material/LastPage";
import ViewSidebarIcon from "@mui/icons-material/ViewSidebar";
import ViewWeekIcon from "@mui/icons-material/ViewWeek"; // for dual page (book)
import DescriptionIcon from "@mui/icons-material/Description"; // for single page
import CloseIcon from "@mui/icons-material/Close";
import { useLocalStorage } from "@mantine/hooks";
import { fileStorage } from "../../services/fileStorage";
import SkeletonLoader from '../shared/SkeletonLoader';
import { useFileState, useFileActions, useCurrentFile } from "../../contexts/FileContext";
import { useFileWithUrl } from "../../hooks/useFileWithUrl";
import { FileId } from "../../types/file";


// Lazy loading page image component
interface LazyPageImageProps {
  pageIndex: number;
  zoom: number;
  theme: any;
  isFirst: boolean;
  renderPage: (pageIndex: number) => Promise<string | null>;
  pageImages: (string | null)[];
  setPageRef: (index: number, ref: HTMLImageElement | null) => void;
}

const LazyPageImage = ({
  pageIndex, zoom, theme, isFirst, renderPage, pageImages, setPageRef
}: LazyPageImageProps) => {
  const [isVisible, setIsVisible] = useState(false);
  const [imageUrl, setImageUrl] = useState<string | null>(null);
  const imgRef = useRef<HTMLImageElement>(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting && !imageUrl) {
            setIsVisible(true);
          }
        });
      },
      {
        rootMargin: '200px', // Start loading 200px before visible
        threshold: 0.1
      }
    );

    if (imgRef.current) {
      observer.observe(imgRef.current);
    }

    return () => observer.disconnect();
  }, [imageUrl]);

  // Update local state when pageImages changes (from preloading)
  useEffect(() => {
    if (pageImages[pageIndex]) {
      setImageUrl(pageImages[pageIndex]);
    }
  }, [pageImages, pageIndex]);

  useEffect(() => {
    if (isVisible && !imageUrl) {
      renderPage(pageIndex).then((url) => {
        if (url) setImageUrl(url);
      });
    }
  }, [isVisible, imageUrl, pageIndex, renderPage]);

  useEffect(() => {
    if (imgRef.current) {
      setPageRef(pageIndex, imgRef.current);
    }
  }, [pageIndex, setPageRef]);

  if (imageUrl) {
    return (
      <img
        ref={imgRef}
        src={imageUrl}
        alt={`Page ${pageIndex + 1}`}
        style={{
          width: `${100 * zoom}%`,
          maxWidth: 700 * zoom,
          boxShadow: "0 2px 8px rgba(0,0,0,0.08)",
          borderRadius: 8,
          marginTop: isFirst ? theme.spacing.xl : 0,
        }}
      />
    );
  }

  // Placeholder while loading
  return (
    <div
      ref={imgRef}
      style={{
        width: `${100 * zoom}%`,
        maxWidth: 700 * zoom,
        height: 800 * zoom, // Estimated height
        backgroundColor: '#f5f5f5',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        borderRadius: 8,
        marginTop: isFirst ? theme.spacing.xl : 0,
        border: '1px dashed #ccc'
      }}
    >
      {isVisible ? (
        <div style={{ textAlign: 'center' }}>
          <div style={{
            width: 20,
            height: 20,
            border: '2px solid #ddd',
            borderTop: '2px solid #666',
            borderRadius: '50%',
            animation: 'spin 1s linear infinite',
            margin: '0 auto 8px'
          }} />
          <Text size="sm" c="dimmed">Loading page {pageIndex + 1}...</Text>
        </div>
      ) : (
        <Text size="sm" c="dimmed">Page {pageIndex + 1}</Text>
      )}
    </div>
  );
};

export interface ViewerProps {
  sidebarsVisible: boolean;
  setSidebarsVisible: (v: boolean) => void;
  onClose?: () => void;
  previewFile: File | null; // For preview mode - bypasses context
}

const Viewer = ({
  sidebarsVisible,
  setSidebarsVisible,
  onClose,
  previewFile,
}: ViewerProps) => {
  const { t } = useTranslation();
  const theme = useMantineTheme();

  // Get current file from FileContext
  const { selectors } = useFileState();
  const { actions } = useFileActions();
  const currentFile = useCurrentFile();

  const getCurrentFile = () => currentFile.file;
  const getCurrentProcessedFile = () => currentFile.record?.processedFile || undefined;
  const clearAllFiles = actions.clearAllFiles;
  const addFiles = actions.addFiles;
  const activeFiles = selectors.getFiles();

  // Tab management for multiple files
  const [activeTab, setActiveTab] = useState<string>("0");

  // Reset PDF state when switching tabs
  const handleTabChange = (newTab: string) => {
    setActiveTab(newTab);
    setNumPages(0);
    setPageImages([]);
    setCurrentPage(null);
    setLoading(true);
  };
  const [numPages, setNumPages] = useState<number>(0);
  const [pageImages, setPageImages] = useState<string[]>([]);
  const [loading, setLoading] = useState<boolean>(false);
  const [currentPage, setCurrentPage] = useState<number | null>(null);
  const [dualPage, setDualPage] = useState(false);
  const [zoom, setZoom] = useState(1); // 1 = 100%
  const pageRefs = useRef<(HTMLImageElement | null)[]>([]);

  // Memoize setPageRef to prevent infinite re-renders
  const setPageRef = useCallback((index: number, ref: HTMLImageElement | null) => {
    pageRefs.current[index] = ref;
  }, []);

  // Get files with URLs for tabs - we'll need to create these individually
  const file0WithUrl = useFileWithUrl(activeFiles[0]);
  const file1WithUrl = useFileWithUrl(activeFiles[1]);
  const file2WithUrl = useFileWithUrl(activeFiles[2]);
  const file3WithUrl = useFileWithUrl(activeFiles[3]);
  const file4WithUrl = useFileWithUrl(activeFiles[4]);

  const filesWithUrls = React.useMemo(() => {
    return [file0WithUrl, file1WithUrl, file2WithUrl, file3WithUrl, file4WithUrl]
      .slice(0, activeFiles.length)
      .filter(Boolean);
  }, [file0WithUrl, file1WithUrl, file2WithUrl, file3WithUrl, file4WithUrl, activeFiles.length]);

  // Use preview file if available, otherwise use active tab file
  const effectiveFile = React.useMemo(() => {
    if (previewFile) {
      // Validate the preview file
      if (!(previewFile instanceof File)) {
        return null;
      }

      if (previewFile.size === 0) {
        return null;
      }

      return { file: previewFile, url: null };
    } else {
      // Use the file from the active tab
      const tabIndex = parseInt(activeTab);
      return filesWithUrls[tabIndex] || null;
    }
  }, [previewFile, filesWithUrls, activeTab]);

  const scrollAreaRef = useRef<HTMLDivElement>(null);
  const pdfDocRef = useRef<any>(null);
  const renderingPagesRef = useRef<Set<number>>(new Set());
  const currentArrayBufferRef = useRef<ArrayBuffer | null>(null);
  const preloadingRef = useRef<boolean>(false);

  // Function to render a specific page on-demand
  const renderPage = async (pageIndex: number): Promise<string | null> => {
    if (!pdfDocRef.current || renderingPagesRef.current.has(pageIndex)) {
      return null;
    }

    const pageNum = pageIndex + 1;
    if (pageImages[pageIndex]) {
      return pageImages[pageIndex]; // Already rendered
    }

    renderingPagesRef.current.add(pageIndex);

    try {
      const page = await pdfDocRef.current.getPage(pageNum);
      const viewport = page.getViewport({ scale: 1.2 });
      const canvas = document.createElement("canvas");
      canvas.width = viewport.width;
      canvas.height = viewport.height;
      const ctx = canvas.getContext("2d");

      if (ctx) {
        await page.render({ canvasContext: ctx, viewport }).promise;
        const dataUrl = canvas.toDataURL();

        // Update the pageImages array
        setPageImages(prev => {
          const newImages = [...prev];
          newImages[pageIndex] = dataUrl;
          return newImages;
        });

        renderingPagesRef.current.delete(pageIndex);
        return dataUrl;
      }
    } catch (error) {
      console.error(`Failed to render page ${pageNum}:`, error);
    }

    renderingPagesRef.current.delete(pageIndex);
    return null;
  };

  // Progressive preloading function
  const startProgressivePreload = async () => {
    if (!pdfDocRef.current || preloadingRef.current || numPages === 0) return;

    preloadingRef.current = true;

    // Start with first few pages for immediate viewing
    const priorityPages = [0, 1, 2, 3, 4]; // First 5 pages

    // Render priority pages first
    for (const pageIndex of priorityPages) {
      if (pageIndex < numPages && !pageImages[pageIndex]) {
        await renderPage(pageIndex);
        // Small delay to allow UI to update
        await new Promise(resolve => setTimeout(resolve, 50));
      }
    }

    // Then render remaining pages in background
    for (let pageIndex = 5; pageIndex < numPages; pageIndex++) {
      if (!pageImages[pageIndex]) {
        await renderPage(pageIndex);
        // Longer delay for background loading to not block UI
        await new Promise(resolve => setTimeout(resolve, 100));
      }
    }

    preloadingRef.current = false;
  };

  // Initialize current page when PDF loads
  useEffect(() => {
    if (numPages > 0 && !currentPage) {
      setCurrentPage(1);
    }
  }, [numPages, currentPage]);

  // Function to scroll to a specific page
  const scrollToPage = (pageNumber: number) => {
    const el = pageRefs.current[pageNumber - 1];
    const scrollArea = scrollAreaRef.current;

    if (el && scrollArea) {
      const scrollAreaRect = scrollArea.getBoundingClientRect();
      const elRect = el.getBoundingClientRect();
      const currentScrollTop = scrollArea.scrollTop;

      // Position page near top of viewport with some padding
      const targetScrollTop = currentScrollTop + (elRect.top - scrollAreaRect.top) - 20;

      scrollArea.scrollTo({
        top: targetScrollTop,
        behavior: "smooth"
      });
    }
  };

  // Throttled scroll handler to prevent jerky updates
  const handleScrollThrottled = useCallback(() => {
    const scrollArea = scrollAreaRef.current;
    if (!scrollArea || !pageRefs.current.length) return;

    const areaRect = scrollArea.getBoundingClientRect();
    const viewportCenter = areaRect.top + areaRect.height / 2;
    let closestIdx = 0;
    let minDist = Infinity;

    pageRefs.current.forEach((img, idx) => {
      if (img) {
        const imgRect = img.getBoundingClientRect();
        const imgCenter = imgRect.top + imgRect.height / 2;
        const dist = Math.abs(imgCenter - viewportCenter);
        if (dist < minDist) {
          minDist = dist;
          closestIdx = idx;
        }
      }
    });

    // Update page number display only if changed
    if (currentPage !== closestIdx + 1) {
      setCurrentPage(closestIdx + 1);
    }
  }, [currentPage]);

  // Throttle scroll events to reduce jerkiness
  const handleScroll = useCallback(() => {
    if (window.requestAnimationFrame) {
      window.requestAnimationFrame(handleScrollThrottled);
    } else {
      handleScrollThrottled();
    }
  }, [handleScrollThrottled]);

  useEffect(() => {
    let cancelled = false;
    async function loadPdfInfo() {
      if (!effectiveFile) {
        setNumPages(0);
        setPageImages([]);
        return;
      }
      setLoading(true);
      try {
        let pdfData;

        // For preview files, use ArrayBuffer directly to avoid blob URL issues
        if (previewFile && effectiveFile.file === previewFile) {
          const arrayBuffer = await previewFile.arrayBuffer();
          pdfData = { data: arrayBuffer };
        }
        // Handle special IndexedDB URLs for large files
        else if (effectiveFile.url?.startsWith('indexeddb:')) {
          const fileId = effectiveFile.url.replace('indexeddb:', '') as FileId /* FIX ME: Not sure this is right - at least probably not the right place for this logic */;

          // Get data directly from IndexedDB
          const arrayBuffer = await fileStorage.getFileData(fileId);
          if (!arrayBuffer) {
            throw new Error('File not found in IndexedDB - may have been purged by browser');
          }

          // Store reference for cleanup
          currentArrayBufferRef.current = arrayBuffer;
          pdfData = { data: arrayBuffer };
        } else if (effectiveFile.url) {
          // Standard blob URL or regular URL
          pdfData = effectiveFile.url;
        } else {
          throw new Error('No valid PDF source available');
        }

        const pdf = await pdfWorkerManager.createDocument(pdfData);
        pdfDocRef.current = pdf;
        setNumPages(pdf.numPages);
        if (!cancelled) {
          setPageImages(new Array(pdf.numPages).fill(null));
          // Start progressive preloading after a short delay
          setTimeout(() => startProgressivePreload(), 100);
        }
      } catch (error) {
        if (!cancelled) {
          setPageImages([]);
          setNumPages(0);
        }
      }
      if (!cancelled) setLoading(false);
    }
    loadPdfInfo();
    return () => {
      cancelled = true;
      // Stop any ongoing preloading
      preloadingRef.current = false;
      // Cleanup PDF document using worker manager
      if (pdfDocRef.current) {
        pdfWorkerManager.destroyDocument(pdfDocRef.current);
        pdfDocRef.current = null;
      }
      // Cleanup ArrayBuffer reference to help garbage collection
      currentArrayBufferRef.current = null;
    };
  }, [effectiveFile, previewFile]);

  useEffect(() => {
    const viewport = scrollAreaRef.current;
    if (!viewport) return;
    const handler = () => {
      handleScroll();
    };
    viewport.addEventListener("scroll", handler);
    return () => viewport.removeEventListener("scroll", handler);
  }, [pageImages]);

  return (
    <Box style={{ position: 'relative', height: '100%', display: 'flex', flexDirection: 'column' }}>
      {/* Close Button - Only show in preview mode */}
      {onClose && previewFile && (
        <ActionIcon
          variant="filled"
          color="gray"
          size="lg"
          style={{
            position: 'absolute',
            top: '1rem',
            right: '1rem',
            zIndex: 1000,
            borderRadius: '50%',
          }}
          onClick={onClose}
        >
          <CloseIcon />
        </ActionIcon>
      )}

      {!effectiveFile ? (
        <Center style={{ flex: 1 }}>
          <Text c="red">Error: No file provided to viewer</Text>
        </Center>
      ) : (
        <>
          {/* Tabs for multiple files */}
          {activeFiles.length > 1 && !previewFile && (
            <Box
              style={{
                borderBottom: '1px solid var(--mantine-color-gray-3)',
                backgroundColor: 'var(--mantine-color-body)',
                position: 'relative',
                zIndex: 100,
                marginTop: '60px' // Push tabs below TopControls
              }}
            >
              <Tabs value={activeTab} onChange={(value) => handleTabChange(value || "0")}>
                <Tabs.List>
                  {activeFiles.map((file: any, index: number) => (
                    <Tabs.Tab key={index} value={index.toString()}>
                      {file.name.length > 20 ? `${file.name.substring(0, 20)}...` : file.name}
                    </Tabs.Tab>
                  ))}
                </Tabs.List>
              </Tabs>
            </Box>
          )}

          {loading ? (
            <div style={{ flex: 1, padding: '1rem' }}>
              <SkeletonLoader type="viewer" />
            </div>
          ) : (
        <ScrollArea
          style={{ flex: 1, position: "relative"}}
          viewportRef={scrollAreaRef}
        >
          <Stack gap="xl" align="center" >
            {numPages === 0 && (
              <Text color="dimmed">{t("viewer.noPagesToDisplay", "No pages to display.")}</Text>
            )}
            {dualPage
              ? Array.from({ length: Math.ceil(numPages / 2) }).map((_, i) => (
                  <Group key={i} gap="md" align="flex-start" style={{ width: "100%", justifyContent: "center" }}>
                    <LazyPageImage
                      pageIndex={i * 2}
                      zoom={zoom}
                      theme={theme}
                      isFirst={i === 0}
                      renderPage={renderPage}
                      pageImages={pageImages}
                      setPageRef={setPageRef}
                    />
                    {i * 2 + 1 < numPages && (
                      <LazyPageImage
                        pageIndex={i * 2 + 1}
                        zoom={zoom}
                        theme={theme}
                        isFirst={i === 0}
                        renderPage={renderPage}
                        pageImages={pageImages}
                        setPageRef={setPageRef}
                      />
                    )}
                  </Group>
                ))
              : Array.from({ length: numPages }).map((_, idx) => (
                  <LazyPageImage
                    key={idx}
                    pageIndex={idx}
                    zoom={zoom}
                    theme={theme}
                    isFirst={idx === 0}
                    renderPage={renderPage}
                    pageImages={pageImages}
                    setPageRef={setPageRef}
                  />
                ))}
          </Stack>
          {/* Navigation bar overlays the scroll area */}
          <div
            style={{
              position: "absolute",
              left: 0,
              right: 0,
              bottom: 0,
              zIndex: 50,
              display: "flex",
              justifyContent: "center",
              pointerEvents: "none",
              background: "transparent",

            }}
          >
            <Paper
              radius="xl xl 0 0"
              shadow="sm"
              p={12}
              pb={12}
              style={{
                display: "flex",
                alignItems: "center",
                gap: 10,
                borderTopLeftRadius: 16,
                borderTopRightRadius: 16,
                borderBottomLeftRadius: 0,
                borderBottomRightRadius: 0,
                boxShadow: "0 -2px 8px rgba(0,0,0,0.04)",
                pointerEvents: "auto",
                minWidth: 420,
                maxWidth: 700,
                flexWrap: "wrap",
              }}
            >
              <Button
                variant="subtle"
                color="blue"
                size="md"
                px={8}
                radius="xl"
                onClick={() => {
                  scrollToPage(1);
                }}
                disabled={currentPage === 1}
                style={{ minWidth: 36 }}
              >
                <FirstPageIcon fontSize="small" />
              </Button>
              <Button
                variant="subtle"
                color="blue"
                size="md"
                px={8}
                radius="xl"
                onClick={() => {
                  const prevPage = Math.max(1, (currentPage || 1) - 1);
                  scrollToPage(prevPage);
                }}
                disabled={currentPage === 1}
                style={{ minWidth: 36 }}
              >
                <ArrowBackIosNewIcon fontSize="small" />
              </Button>
              <NumberInput
                value={currentPage || 1}
                onChange={value => {
                  const page = Number(value);
                  if (!isNaN(page) && page >= 1 && page <= numPages) {
                    scrollToPage(page);
                  }
                }}
                min={1}
                max={numPages}
                hideControls
                styles={{
                  input: { width: 48, textAlign: "center", fontWeight: 500, fontSize: 16},
                }}
              />
              <span style={{ fontWeight: 500, fontSize: 16 }}>
                / {numPages}
              </span>
              <Button
                variant="subtle"
                color="blue"
                size="md"
                px={8}
                radius="xl"
                onClick={() => {
                  const nextPage = Math.min(numPages, (currentPage || 1) + 1);
                  scrollToPage(nextPage);
                }}
                disabled={currentPage === numPages}
                style={{ minWidth: 36 }}
              >
                <ArrowForwardIosIcon fontSize="small" />
              </Button>
              <Button
                variant="subtle"
                color="blue"
                size="md"
                px={8}
                radius="xl"
                onClick={() => {
                  scrollToPage(numPages);
                }}
                disabled={currentPage === numPages}
                style={{ minWidth: 36 }}
              >
                <LastPageIcon fontSize="small" />
              </Button>
              <Button
                variant={dualPage ? "filled" : "light"}
                color="blue"
                size="md"
                radius="xl"
                onClick={() => setDualPage(v => !v)}
                style={{ minWidth: 36 }}
                title={dualPage ? t("viewer.singlePageView", "Single Page View") : t("viewer.dualPageView", "Dual Page View")}
              >
                {dualPage ? <DescriptionIcon fontSize="small" /> : <ViewWeekIcon fontSize="small" />}
              </Button>
              <Group gap={4} align="center" style={{ marginLeft: 16 }}>
                <Button
                  variant="subtle"
                  color="blue"
                  size="md"
                  radius="xl"
                  onClick={() => setZoom(z => Math.max(0.1, z - 0.1))}
                  style={{ minWidth: 32, padding: 0 }}
                  title={t("viewer.zoomOut", "Zoom out")}
                >−</Button>
                <span style={{ minWidth: 40, textAlign: "center" }}>{Math.round(zoom * 100)}%</span>
                <Button
                  variant="subtle"
                  color="blue"
                  size="md"
                  radius="xl"
                  onClick={() => setZoom(z => Math.min(5, z + 0.1))}
                  style={{ minWidth: 32, padding: 0 }}
                  title={t("viewer.zoomIn", "Zoom in")}
                >+</Button>
              </Group>
            </Paper>
          </div>
        </ScrollArea>
          )}
        </>
      )}

    </Box>
  );
};

export default Viewer;
