/**
 * usePdfReader — load PDF blob, page navigation, and persist reading progress.
 */

import { useCallback, useEffect, useRef, useState } from "react";
import { booksApi } from "../api/books";
import { getReadingProgress, updateReadingProgress } from "../api/reader";
import type { ReadingProgress } from "../types/reader";
import { parsePdfAnchor } from "../utils/pdfAnchor";
import { pdfjs, type PdfDocumentProxy } from "../utils/pdfjsSetup";

export interface PdfReadingAnchor {
  page: number;
  percentage: number;
}

export interface UsePdfReaderReturn {
  pdfDocument: PdfDocumentProxy | null;
  currentPage: number;
  pageCount: number;
  scale: number;
  setScale: (scale: number) => void;
  chapterTitle: string;
  progress: ReadingProgress | null;
  isLoading: boolean;
  error: string | null;
  isBookmarkPeekActive: boolean;
  readingAnchor: PdfReadingAnchor | null;
  pageWrapRef: React.RefObject<HTMLDivElement | null>;
  goToPage: (page: number) => void;
  goToNextPage: () => void;
  goToPrevPage: () => void;
  beginBookmarkPeek: (targetAnchor: string) => void;
  resumeReadingAnchor: () => void;
  goToAnchor: (anchor: string) => void;
}

function pageToPercentage(page: number, pageCount: number): number {
  if (pageCount <= 0) return 0;
  return Math.round((page / pageCount) * 100);
}

export function usePdfReader(
  bookId: number,
  pageCountHint = 0,
  initialAnchor?: string,
): UsePdfReaderReturn {
  const [pdfDocument, setPdfDocument] = useState<PdfDocumentProxy | null>(null);
  const [currentPage, setCurrentPage] = useState(1);
  const [pageCount, setPageCount] = useState(pageCountHint);
  const [scale, setScale] = useState(1.25);
  const [progress, setProgress] = useState<ReadingProgress | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [isBookmarkPeekActive, setIsBookmarkPeekActive] = useState(false);
  const [readingAnchor, setReadingAnchor] = useState<PdfReadingAnchor | null>(null);

  const pageWrapRef = useRef<HTMLDivElement | null>(null);
  const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const isBookmarkPeekRef = useRef(false);
  const readingAnchorRef = useRef<PdfReadingAnchor | null>(null);
  const pageCountRef = useRef(pageCountHint);

  const clearPeekState = useCallback(() => {
    isBookmarkPeekRef.current = false;
    readingAnchorRef.current = null;
    setIsBookmarkPeekActive(false);
    setReadingAnchor(null);
  }, []);

  const setPeekState = useCallback((anchor: PdfReadingAnchor) => {
    readingAnchorRef.current = anchor;
    isBookmarkPeekRef.current = true;
    setReadingAnchor(anchor);
    setIsBookmarkPeekActive(true);
  }, []);

  const flushProgress = useCallback(
    async (page: number, total: number) => {
      if (isBookmarkPeekRef.current) return;
      const percentage = pageToPercentage(page, total);
      try {
        const updated = await updateReadingProgress(bookId, {
          current_chapter: page,
          percentage,
          current_position: percentage,
        });
        setProgress(updated);
      } catch {
        /* best-effort */
      }
    },
    [bookId],
  );

  const scheduleProgress = useCallback(
    (page: number, total: number) => {
      if (isBookmarkPeekRef.current) return;
      if (debounceTimer.current) clearTimeout(debounceTimer.current);
      debounceTimer.current = setTimeout(() => {
        void flushProgress(page, total);
      }, 800);
    },
    [flushProgress],
  );

  const goToPage = useCallback(
    (page: number) => {
      const total = pageCountRef.current || pageCount;
      const clamped = Math.max(1, Math.min(page, total || page));
      setCurrentPage(clamped);
      if (total > 0) scheduleProgress(clamped, total);
    },
    [pageCount, scheduleProgress],
  );

  const goToAnchor = useCallback(
    (anchor: string) => {
      const parsed = parsePdfAnchor(anchor);
      if (!parsed) return;
      goToPage(parsed.page);
    },
    [goToPage],
  );

  const captureAnchorFromCurrent = useCallback((): PdfReadingAnchor | null => {
    const total = pageCountRef.current || pageCount;
    if (total <= 0) return null;
    return {
      page: currentPage,
      percentage: pageToPercentage(currentPage, total),
    };
  }, [currentPage, pageCount]);

  const beginBookmarkPeek = useCallback(
    (targetAnchor: string) => {
      const parsed = parsePdfAnchor(targetAnchor);
      if (!parsed) return;

      if (!isBookmarkPeekRef.current) {
        let anchor = captureAnchorFromCurrent();
        if (!anchor && progress) {
          const page = progress.current_chapter || 1;
          const pct = progress.percentage ?? progress.current_position ?? 0;
          anchor = { page, percentage: pct > 0 ? Math.round(pct) : pageToPercentage(page, pageCountRef.current) };
        }
        if (anchor) setPeekState(anchor);
      }

      goToPage(parsed.page);
    },
    [captureAnchorFromCurrent, goToPage, progress, setPeekState],
  );

  const resumeReadingAnchor = useCallback(() => {
    const anchor = readingAnchorRef.current;
    clearPeekState();
    if (!anchor) return;
    goToPage(anchor.page);
    const total = pageCountRef.current || pageCount;
    void flushProgress(anchor.page, total);
  }, [clearPeekState, flushProgress, goToPage, pageCount]);

  const goToNextPage = useCallback(() => {
    goToPage(currentPage + 1);
  }, [currentPage, goToPage]);

  const goToPrevPage = useCallback(() => {
    goToPage(currentPage - 1);
  }, [currentPage, goToPage]);

  useEffect(() => {
    let cancelled = false;
    clearPeekState();

    async function loadPdf() {
      setIsLoading(true);
      setError(null);
      try {
        const [progressData, blob] = await Promise.all([
          getReadingProgress(bookId).catch(() => null),
          booksApi.getEbookFile(bookId),
        ]);
        if (cancelled) return;

        const loadingTask = pdfjs.getDocument({ data: await blob.arrayBuffer() });
        const doc = await loadingTask.promise;
        if (cancelled) return;

        const numPages = doc.numPages;
        pageCountRef.current = numPages;
        setPdfDocument(doc);
        setPageCount(numPages);

        const peekParsed =
          initialAnchor && parsePdfAnchor(initialAnchor) ? parsePdfAnchor(initialAnchor) : null;
        const savedPage = progressData?.current_chapter || 0;
        const startPage = peekParsed?.page ?? (savedPage > 0 ? savedPage : 1);

        if (progressData) setProgress(progressData);

        if (peekParsed && savedPage > 0) {
          setPeekState({
            page: savedPage,
            percentage: pageToPercentage(savedPage, numPages),
          });
        }

        setCurrentPage(Math.min(startPage, numPages));
      } catch (err: unknown) {
        if (!cancelled) {
          const message = err instanceof Error ? err.message : "Failed to load PDF";
          setError(message.includes("password") ? "PDF_PASSWORD" : message);
        }
      } finally {
        if (!cancelled) setIsLoading(false);
      }
    }

    void loadPdf();

    return () => {
      cancelled = true;
      if (debounceTimer.current) clearTimeout(debounceTimer.current);
      clearPeekState();
    };
  }, [bookId, initialAnchor, clearPeekState, setPeekState]);

  useEffect(() => {
    pageCountRef.current = pageCount;
  }, [pageCount]);

  const chapterTitle =
    pageCount > 0
      ? `Page ${currentPage} / ${pageCount}`
      : "";

  return {
    pdfDocument,
    currentPage,
    pageCount,
    scale,
    setScale,
    chapterTitle,
    progress,
    isLoading,
    error,
    isBookmarkPeekActive,
    readingAnchor,
    pageWrapRef,
    goToPage,
    goToNextPage,
    goToPrevPage,
    beginBookmarkPeek,
    resumeReadingAnchor,
    goToAnchor,
  };
}
