"use client";
import { useEffect, useRef, useState } from "react";
import { Button, Spinner } from "@heroui/react";
import { ChevronLeft, ChevronRight, Minus, Plus, X } from "lucide-react";
import type { PDFDocumentProxy, RenderTask } from "pdfjs-dist";
import SystemAlert from "./SystemAlert";

type Props = { url: string; title: string; onClose: () => void; embedded?: boolean };
export default function PdfViewer({ url, title, onClose, embedded = false }: Props) {
  const dialog = useRef<HTMLDialogElement>(null);
  const area = useRef<HTMLDivElement>(null);
  const canvas = useRef<HTMLCanvasElement>(null);
  const [document, setDocument] = useState<PDFDocumentProxy | null>(null);
  const [page, setPage] = useState(1);
  const [zoom, setZoom] = useState(1);
  const [width, setWidth] = useState(0);
  const [rendering, setRendering] = useState(true);
  const [error, setError] = useState("");
  const [version, setVersion] = useState(0);
  useEffect(() => {
    if (embedded) return;
    const el = dialog.current;
    el?.showModal();
    const previous = window.document.body.style.overflow;
    window.document.body.style.overflow = "hidden";
    return () => {
      el?.close();
      window.document.body.style.overflow = previous;
    };
  }, [embedded]);
  useEffect(() => {
    let active = true;
    let task: ReturnType<typeof import("pdfjs-dist").getDocument> | undefined;
    import("pdfjs-dist")
      .then((pdfjs) => {
        if (!active) return;
        pdfjs.GlobalWorkerOptions.workerSrc = new URL(
          "pdfjs-dist/build/pdf.worker.min.mjs",
          import.meta.url,
        ).toString();
        task = pdfjs.getDocument({
          url,
          withCredentials: new URL(url, location.href).origin === location.origin,
          useSystemFonts: true,
          useWasm: false,
        });
        return task.promise;
      })
      .then((pdf) => {
        if (active && pdf) setDocument(pdf);
      })
      .catch(() => {
        if (active) {
          setError("No fue posible abrir el PDF. Comprueba tu conexión y vuelve a intentarlo.");
          setRendering(false);
        }
      });
    return () => {
      active = false;
      void task?.destroy();
    };
  }, [url, version]);
  useEffect(() => {
    const el = area.current;
    if (!el) return;
    const observer = new ResizeObserver((entries) =>
      setWidth(Math.max(1, entries[0].contentRect.width - 32)),
    );
    observer.observe(el);
    return () => observer.disconnect();
  }, []);
  useEffect(() => {
    const el = canvas.current;
    if (!document || !el || !width) return;
    let active = true;
    let render: RenderTask | undefined;
    document
      .getPage(page)
      .then((pdfPage) => {
        if (!active) return;
        setRendering(true);
        const base = pdfPage.getViewport({ scale: 1 });
        const viewport = pdfPage.getViewport({ scale: Math.min(width / base.width, 1.5) * zoom });
        const ratio = Math.min(window.devicePixelRatio || 1, 2);
        el.width = Math.floor(viewport.width * ratio);
        el.height = Math.floor(viewport.height * ratio);
        el.style.width = `${Math.floor(viewport.width)}px`;
        el.style.height = `${Math.floor(viewport.height)}px`;
        render = pdfPage.render({
          canvas: el,
          viewport,
          transform: ratio !== 1 ? [ratio, 0, 0, ratio, 0, 0] : undefined,
        });
        return render.promise;
      })
      .then(() => {
        if (active) setRendering(false);
      })
      .catch((e) => {
        if (active && e?.name !== "RenderingCancelledException") {
          setError("No fue posible mostrar esta página del documento.");
          setRendering(false);
        }
      });
    return () => {
      active = false;
      render?.cancel();
    };
  }, [document, page, width, zoom]);
  function move(next: number) {
    setRendering(true);
    setPage(next);
    area.current?.scrollTo({ top: 0, left: 0 });
  }
  function retry() {
    setError("");
    setDocument(null);
    setPage(1);
    setRendering(true);
    setVersion((v) => v + 1);
  }
  const content = (
    <div className="flex h-full min-w-0 flex-col">
      <header className="flex shrink-0 flex-wrap items-center justify-between gap-3 border-b border-zinc-200 bg-white p-3 sm:px-5">
        <h2 className="min-w-0 flex-1 truncate text-sm font-semibold" title={title}>
          {title}
        </h2>
        <div className="flex items-center gap-1">
          <Button
            variant="secondary"
            isIconOnly
            aria-label="Reducir zoom"
            isDisabled={zoom <= 0.5}
            onPress={() => setZoom((z) => Math.max(0.5, z - 0.25))}
          >
            <Minus className="size-4" />
          </Button>
          <span className="w-12 text-center text-xs tabular-nums">{Math.round(zoom * 100)}%</span>
          <Button
            variant="secondary"
            isIconOnly
            aria-label="Aumentar zoom"
            isDisabled={zoom >= 2.5}
            onPress={() => setZoom((z) => Math.min(2.5, z + 0.25))}
          >
            <Plus className="size-4" />
          </Button>
          {!embedded && (
            <Button
              variant="secondary"
              isIconOnly
              aria-label="Cerrar documento"
              onPress={onClose}
              className="ml-2"
            >
              <X className="size-5" />
            </Button>
          )}
        </div>
      </header>
      {error && (
        <div className="p-4">
          <SystemAlert
            status="danger"
            title="No se pudo abrir el documento"
            action={
              <Button variant="secondary" onPress={retry}>
                Reintentar
              </Button>
            }
          >
            {error}
          </SystemAlert>
        </div>
      )}
      <div ref={area} className="relative min-h-0 flex-1 overflow-auto overscroll-contain p-4">
        {!error && (
          <canvas
            ref={canvas}
            aria-label={`Página ${page} de ${document?.numPages ?? "documento"}`}
            role="img"
            className={`mx-auto block bg-white shadow-sm ${rendering ? "invisible" : ""}`}
          />
        )}
        {!error && rendering && (
          <div
            role="status"
            aria-label="Cargando documento"
            className="absolute inset-0 flex items-center justify-center"
          >
            <Spinner size="lg" />
          </div>
        )}
      </div>
      <footer className="flex shrink-0 items-center justify-center gap-4 border-t border-zinc-200 bg-white px-4 py-3">
        <Button
          variant="secondary"
          isIconOnly
          aria-label="Página anterior"
          isDisabled={!document || page <= 1 || rendering}
          onPress={() => move(page - 1)}
        >
          <ChevronLeft className="size-4" />
        </Button>
        <span aria-live="polite" className="text-sm tabular-nums">
          {document ? `${page} / ${document.numPages}` : "Documento PDF"}
        </span>
        <Button
          variant="secondary"
          isIconOnly
          aria-label="Página siguiente"
          isDisabled={!document || page >= document.numPages || rendering}
          onPress={() => move(page + 1)}
        >
          <ChevronRight className="size-4" />
        </Button>
      </footer>
    </div>
  );
  if (embedded)
    return (
      <div
        className="h-full min-h-0 w-full bg-zinc-100 text-zinc-900"
        aria-label={`Documento: ${title}`}
      >
        {content}
      </div>
    );
  return (
    <dialog
      ref={dialog}
      aria-label={`Documento: ${title}`}
      className="fixed inset-0 m-0 h-dvh max-h-none w-screen max-w-none border-0 bg-zinc-100 p-0 text-zinc-900 backdrop:bg-zinc-950/60"
      onCancel={(e) => {
        e.preventDefault();
        onClose();
      }}
    >
      {content}
    </dialog>
  );
}
