import { useCallback, useEffect, useRef, useState } from "react";
import * as pdfjs from "pdfjs-dist";
import { ReaderError, readerErrorMessage } from "./ReaderError";
import { configurePdfWorker } from "./pdfWorker";
import type { ReaderControls } from "./ReaderShell";
import type { ReaderMode } from "../api/types";

configurePdfWorker(pdfjs);

export function PdfReader({
  url,
  page,
  backHref,
  mode,
  onPageCommit,
  onControlsChange
}: {
  url: string;
  page: number;
  backHref: string;
  mode: ReaderMode;
  onPageCommit: (page: number, pages: number) => void;
  onControlsChange: (controls: ReaderControls) => void;
}) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const frameRef = useRef<HTMLDivElement>(null);
  const [pages, setPages] = useState(1);
  const [error, setError] = useState<string>();
  const [loading, setLoading] = useState(true);
  const [attempt, setAttempt] = useState(0);
  const [frameSize, setFrameSize] = useState({ width: 980, height: 900 });

  const currentPage = Math.max(1, Math.min(page, pages));
  const go = useCallback((nextPage: number) => onPageCommit(Math.max(1, Math.min(nextPage, pages)), pages), [onPageCommit, pages]);

  useEffect(() => {
    onControlsChange({
      canPrevious: !loading && !error && currentPage > 1,
      canNext: !loading && !error && currentPage < pages,
      positionLabel: loading ? "Ouverture PDF" : `${currentPage} / ${pages}`,
      onPrevious: () => go(currentPage - 1),
      onNext: () => go(currentPage + 1)
    });
  }, [currentPage, error, go, loading, onControlsChange, pages]);

  useEffect(() => {
    const frame = frameRef.current;
    if (!frame) return;
    const updateSize = () => {
      const rect = frame.getBoundingClientRect();
      setFrameSize({ width: Math.max(320, rect.width), height: Math.max(320, rect.height) });
    };
    updateSize();
    const observer = new ResizeObserver(updateSize);
    observer.observe(frame);
    return () => observer.disconnect();
  }, []);

  useEffect(() => {
    let cancelled = false;
    let loadingTask: pdfjs.PDFDocumentLoadingTask | undefined;
    let renderTask: pdfjs.RenderTask | undefined;
    async function render() {
      try {
        configurePdfWorker(pdfjs);
        setLoading(true);
        setError(undefined);
        loadingTask = pdfjs.getDocument({ url, withCredentials: true });
        const document = await loadingTask.promise;
        if (cancelled) return;
        setPages(document.numPages);
        const pdfPage = await document.getPage(Math.max(1, Math.min(page, document.numPages)));
        const canvas = canvasRef.current;
        if (!canvas) return;
        const baseViewport = pdfPage.getViewport({ scale: 1 });
        const fitScale =
          mode === "vertical"
            ? frameSize.width / baseViewport.width
            : Math.min(frameSize.width / baseViewport.width, frameSize.height / baseViewport.height);
        const renderScale = Math.max(0.35, Math.min(3, fitScale)) * Math.min(2, window.devicePixelRatio || 1);
        const viewport = pdfPage.getViewport({ scale: renderScale });
        canvas.width = Math.floor(viewport.width);
        canvas.height = Math.floor(viewport.height);
        canvas.style.width = `${Math.floor(viewport.width / Math.min(2, window.devicePixelRatio || 1))}px`;
        canvas.style.height = `${Math.floor(viewport.height / Math.min(2, window.devicePixelRatio || 1))}px`;
        const context = canvas.getContext("2d");
        if (!context) return;
        renderTask = pdfPage.render({ canvas, canvasContext: context, viewport });
        await renderTask.promise;
        if (!cancelled) setLoading(false);
      } catch (renderError) {
        if (!cancelled) {
          setError(readerErrorMessage(renderError, "PDF indisponible"));
          setLoading(false);
        }
      }
    }
    void render();
    return () => {
      cancelled = true;
      renderTask?.cancel();
      void loadingTask?.destroy();
    };
  }, [url, page, attempt, frameSize.height, frameSize.width, mode]);

  return (
    <div className="pdf-reader" ref={frameRef}>
      {error ? (
        <ReaderError
          title="Lecture PDF indisponible"
          detail="ReadaBook n'a pas pu ouvrir ce fichier dans le lecteur web."
          technicalDetail={error}
          downloadUrl={url}
          backHref={backHref}
          onRetry={() => setAttempt((value) => value + 1)}
        />
      ) : (
        <>
          {loading && (
            <div className="reader-fallback">
              <span>Ouverture PDF</span>
            </div>
          )}
          <canvas ref={canvasRef} />
        </>
      )}
    </div>
  );
}
