"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import { Document, Page, pdfjs } from "react-pdf";
import { ChevronLeft, ChevronRight, Loader2, ZoomIn, ZoomOut } from "lucide-react";

import { reviewerPaperApi } from "@/lib/api/client";
import { cn } from "@/lib/utils/cn";

import { PaperPreviewError } from "./PaperPreviewError";

// react-pdf 9.x worker setup. The worker is vendored as a static
// asset at `public/pdf.worker.min.mjs` (copied from pdfjs-dist at the
// version pinned by package-lock.json) and served from the site root.
// We previously used `new URL("pdfjs-dist/build/pdf.worker.min.mjs",
// import.meta.url)`, which asks webpack to emit the worker as a
// chunk; Terser then fails on the .mjs file's top-level import/export
// syntax in production builds. The static-public-asset pattern avoids
// Terser entirely and is the standard Next.js recipe for react-pdf.
pdfjs.GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs";

const ZOOM_LEVELS = [1.0, 1.25, 1.5] as const;
type ZoomLevel = (typeof ZOOM_LEVELS)[number];

interface PreviewErrorState {
  code: string | null;
  message: string | null;
}

export interface PaperPreviewPaneProps {
  /** Paper UUID — used to call {@code /reviewer/papers/{paperId}/manuscript}. */
  paperId: string;
  className?: string;
}

/**
 * Reviewer-facing inline preview of a (metadata-stripped) manuscript
 * PDF. Fetches the PDF as a Blob via
 * {@link reviewerPaperApi.getManuscriptPreview}, renders it with
 * react-pdf, and exposes prev/next + zoom controls. On failure, falls
 * back to {@link PaperPreviewError}.
 *
 * <p>This pane does NOT trigger downloads and does NOT render the
 * confidentiality agreement modal — preview is the unrestricted path
 * (Phase 2B-ii D18). Both of those concerns are added by the review
 * page wrapper in Phase 3c-ii.
 *
 * <p>Lifecycle: the Blob URL is created once per successful fetch and
 * revoked on unmount (or before replacement on retry) via
 * {@code URL.revokeObjectURL}.
 */
export function PaperPreviewPane({ paperId, className }: PaperPreviewPaneProps) {
  const [blobUrl, setBlobUrl] = useState<string | null>(null);
  const [numPages, setNumPages] = useState<number>(0);
  const [pageNumber, setPageNumber] = useState<number>(1);
  const [zoom, setZoom] = useState<ZoomLevel>(1.0);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<PreviewErrorState | null>(null);
  const [reloadKey, setReloadKey] = useState<number>(0);

  // Fetch the preview blob whenever paperId (or retry nonce) changes.
  useEffect(() => {
    let cancelled = false;
    let createdUrl: string | null = null;

    setLoading(true);
    setError(null);
    setNumPages(0);
    setPageNumber(1);

    reviewerPaperApi
      .getManuscriptPreview(paperId)
      .then((res) => {
        if (cancelled) return;
        // Check if the response is a PDF — react-pdf can only render PDFs.
        // Non-PDF types (DOCX, LaTeX) get a friendly "download instead" message.
        const contentType = res.headers?.["content-type"] ?? "";
        if (!contentType.includes("pdf")) {
          setError({
            code: "NON_PDF_MANUSCRIPT",
            message: "This manuscript is not a PDF and cannot be previewed in the browser. Please download it to view.",
          });
          setLoading(false);
          return;
        }
        const url = URL.createObjectURL(res.data);
        createdUrl = url;
        setBlobUrl(url);
        setLoading(false);
      })
      .catch((err: unknown) => {
        if (cancelled) return;
        const resp = (err as { response?: { data?: { code?: string; message?: string } } })
          .response;
        setError({
          code: resp?.data?.code ?? null,
          message: resp?.data?.message ?? null,
        });
        setBlobUrl(null);
        setLoading(false);
      });

    return () => {
      cancelled = true;
      if (createdUrl) {
        URL.revokeObjectURL(createdUrl);
      }
    };
  }, [paperId, reloadKey]);

  const onDocumentLoadSuccess = useCallback(({ numPages: n }: { numPages: number }) => {
    setNumPages(n);
    setPageNumber(1);
  }, []);

  const onDocumentLoadError = useCallback(() => {
    setError({
      code: "MANUSCRIPT_PREVIEW_UNAVAILABLE",
      message: "The PDF could not be rendered in your browser.",
    });
  }, []);

  const retry = useCallback(() => setReloadKey((k) => k + 1), []);

  const goPrev = useCallback(
    () => setPageNumber((p) => Math.max(1, p - 1)),
    []
  );
  const goNext = useCallback(
    () => setPageNumber((p) => Math.min(numPages || 1, p + 1)),
    [numPages]
  );

  const zoomIndex = useMemo(() => ZOOM_LEVELS.indexOf(zoom), [zoom]);
  const zoomOut = useCallback(
    () => setZoom(ZOOM_LEVELS[Math.max(0, zoomIndex - 1)]),
    [zoomIndex]
  );
  const zoomIn = useCallback(
    () => setZoom(ZOOM_LEVELS[Math.min(ZOOM_LEVELS.length - 1, zoomIndex + 1)]),
    [zoomIndex]
  );

  if (error) {
    return (
      <PaperPreviewError
        code={error.code}
        message={error.message}
        onRetry={retry}
        className={className}
      />
    );
  }

  return (
    <div
      className={cn(
        "flex h-full w-full flex-col overflow-hidden rounded-lg border border-gray-200 bg-white shadow-sm",
        className
      )}
    >
      {/* Toolbar */}
      <div className="flex items-center justify-between gap-2 border-b border-gray-200 bg-gray-50 px-3 py-2">
        <div className="flex items-center gap-1">
          <button
            type="button"
            onClick={goPrev}
            disabled={loading || pageNumber <= 1}
            className="inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-600 hover:bg-gray-200 disabled:cursor-not-allowed disabled:opacity-40"
            aria-label="Previous page"
          >
            <ChevronLeft className="h-4 w-4" />
          </button>
          <span className="min-w-[70px] text-center text-sm tabular-nums text-gray-700">
            {loading || numPages === 0 ? "–" : `${pageNumber} / ${numPages}`}
          </span>
          <button
            type="button"
            onClick={goNext}
            disabled={loading || pageNumber >= numPages}
            className="inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-600 hover:bg-gray-200 disabled:cursor-not-allowed disabled:opacity-40"
            aria-label="Next page"
          >
            <ChevronRight className="h-4 w-4" />
          </button>
        </div>
        <div className="flex items-center gap-1">
          <button
            type="button"
            onClick={zoomOut}
            disabled={loading || zoomIndex <= 0}
            className="inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-600 hover:bg-gray-200 disabled:cursor-not-allowed disabled:opacity-40"
            aria-label="Zoom out"
          >
            <ZoomOut className="h-4 w-4" />
          </button>
          <span className="min-w-[48px] text-center text-sm tabular-nums text-gray-700">
            {Math.round(zoom * 100)}%
          </span>
          <button
            type="button"
            onClick={zoomIn}
            disabled={loading || zoomIndex >= ZOOM_LEVELS.length - 1}
            className="inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-600 hover:bg-gray-200 disabled:cursor-not-allowed disabled:opacity-40"
            aria-label="Zoom in"
          >
            <ZoomIn className="h-4 w-4" />
          </button>
        </div>
      </div>

      {/* Viewer */}
      <div className="flex flex-1 items-start justify-center overflow-auto bg-gray-100 p-4">
        {loading ? (
          <div className="flex h-full w-full items-center justify-center text-gray-500">
            <Loader2 className="mr-2 h-5 w-5 animate-spin" />
            Loading preview…
          </div>
        ) : blobUrl ? (
          <Document
            file={blobUrl}
            onLoadSuccess={onDocumentLoadSuccess}
            onLoadError={onDocumentLoadError}
            loading={
              <div className="flex items-center justify-center text-gray-500">
                <Loader2 className="mr-2 h-5 w-5 animate-spin" />
                Rendering…
              </div>
            }
          >
            <Page
              pageNumber={pageNumber}
              scale={zoom}
              renderTextLayer={false}
              renderAnnotationLayer={false}
              className="shadow"
            />
          </Document>
        ) : null}
      </div>
    </div>
  );
}

export default PaperPreviewPane;
