'use client';

import { useMemo, useState } from 'react';
import { Document, Page, pdfjs } from 'react-pdf';
import { usePinch } from '@use-gesture/react';
import { ChevronLeft, ChevronRight, Loader2, Minus, Plus } from 'lucide-react';

import { Button } from '@/components/ui/button';
import 'react-pdf/dist/Page/AnnotationLayer.css';
import 'react-pdf/dist/Page/TextLayer.css';

/**
 * In-app PDF viewer.
 *
 * Replaces the `<iframe>` preview which delegated rendering to the
 * browser's built-in PDF viewer. The iframe path works on desktop
 * Chrome/Firefox/Safari but is unreliable on mobile (older Android
 * Chrome refuses to render PDFs inline; iOS Safari opens a new tab).
 * react-pdf renders via pdfjs-dist which works identically everywhere.
 *
 * The pdfjs worker is loaded from a CDN matched to the installed
 * pdfjs-dist version. Bundling the worker locally would inflate the
 * main-route bundle by ~150kb; the CDN avoids that cost on every page
 * that uses pdfjs at the cost of a single first-load fetch per user.
 */

// Match the pdfjs.version that react-pdf has bundled at runtime. If
// pdfjs-dist is bumped, the URL auto-tracks. .mjs (module worker) is
// the current pdfjs distribution; .js fallback handled by pdf.js
// internally for older browsers.
pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;

interface PdfViewerProps {
  url: string;
  /** Optional aria-friendly filename for the document. */
  fileName?: string;
}

export function PdfViewer(props: PdfViewerProps) {
  // Key-based remount: re-mount the inner body whenever the url
  // changes so all local state (page, zoom, error) re-initializes from
  // scratch. Replaces the prior useEffect(reset, [url]) the Compiler
  // flagged as set-state-in-effect.
  return <PdfViewerBody key={props.url} {...props} />;
}

function PdfViewerBody({ url, fileName }: PdfViewerProps) {
  const [numPages, setNumPages] = useState<number | null>(null);
  const [pageNumber, setPageNumber] = useState(1);
  const [scale, setScale] = useState(1);
  const [error, setError] = useState<string | null>(null);

  // Keep options stable across renders so react-pdf doesn't refetch
  // every render - useMemo wins because react-pdf compares by identity.
  const options = useMemo(
    () => ({
      cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/`,
      standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/standard_fonts/`,
    }),
    [],
  );

  // Pinch-zoom on touch devices. usePinch's `offset` already maps
  // gesture distance to a smoothly-changing scalar; we clamp it to
  // the same [0.5, 3] range as the +/- buttons.
  const pinchBindings = usePinch(
    ({ offset: [s] }) => {
      setScale(Math.max(0.5, Math.min(3, s)));
    },
    {
      scaleBounds: { min: 0.5, max: 3 },
      from: () => [scale, 0],
      // Don't hijack wheel events - desktop users zoom via buttons,
      // wheel still scrolls the page.
      eventOptions: { passive: false },
    },
  );

  return (
    <div className="flex h-full flex-col">
      <div className="flex items-center justify-between gap-2 border-b bg-muted/40 px-3 py-2 text-sm">
        <div className="flex items-center gap-1">
          <Button
            type="button"
            variant="ghost"
            size="icon"
            disabled={pageNumber <= 1}
            onClick={() => setPageNumber((p) => Math.max(1, p - 1))}
            aria-label="Previous page"
          >
            <ChevronLeft className="h-4 w-4" />
          </Button>
          <span className="min-w-[80px] text-center tabular-nums">
            {numPages ? `${pageNumber} / ${numPages}` : '-'}
          </span>
          <Button
            type="button"
            variant="ghost"
            size="icon"
            disabled={numPages === null || pageNumber >= numPages}
            onClick={() => setPageNumber((p) => (numPages ? Math.min(numPages, p + 1) : p))}
            aria-label="Next page"
          >
            <ChevronRight className="h-4 w-4" />
          </Button>
        </div>

        <div className="flex items-center gap-1">
          <Button
            type="button"
            variant="ghost"
            size="icon"
            onClick={() => setScale((s) => Math.max(0.5, s - 0.25))}
            aria-label="Zoom out"
          >
            <Minus className="h-4 w-4" />
          </Button>
          <span className="min-w-[48px] text-center tabular-nums">{Math.round(scale * 100)}%</span>
          <Button
            type="button"
            variant="ghost"
            size="icon"
            onClick={() => setScale((s) => Math.min(3, s + 0.25))}
            aria-label="Zoom in"
          >
            <Plus className="h-4 w-4" />
          </Button>
        </div>
      </div>

      <div
        className="flex-1 overflow-auto bg-muted/30 p-4 touch-pan-y"
        // Pinch-zoom on touch devices (tablets, phones). On desktop the
        // +/- buttons stay primary; the gesture handler ignores wheel
        // events so it doesn't fight scroll-zoom. Range matches the
        // button zoom (50%–300%).
        {...pinchBindings()}
      >
        {error ? (
          <div className="flex h-full items-center justify-center text-sm text-destructive">
            {error}
          </div>
        ) : (
          <div className="flex justify-center">
            <Document
              file={url}
              options={options}
              onLoadSuccess={({ numPages: n }) => setNumPages(n)}
              onLoadError={(err) => setError(err.message || 'Failed to load PDF')}
              loading={
                <div className="flex items-center gap-2 text-sm text-muted-foreground">
                  <Loader2 className="h-4 w-4 animate-spin" />
                  Loading PDF…
                </div>
              }
            >
              <Page
                pageNumber={pageNumber}
                scale={scale}
                renderAnnotationLayer
                renderTextLayer
                aria-label={fileName ? `${fileName}, page ${pageNumber}` : `Page ${pageNumber}`}
              />
            </Document>
          </div>
        )}
      </div>
    </div>
  );
}
