"use client"

import { useCallback, useEffect, useRef, useState } from "react"
import { ChevronDown, ChevronUp, Loader2, Printer, ZoomIn, ZoomOut } from "lucide-react"
import {
  getDocument,
  GlobalWorkerOptions,
  type PDFDocumentLoadingTask,
  type PDFDocumentProxy,
} from "pdfjs-dist"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"

GlobalWorkerOptions.workerSrc = new URL(
  "pdfjs-dist/build/pdf.worker.min.mjs",
  import.meta.url,
).toString()

const PAGE_GAP_PX = 24
const MIN_ZOOM = 0.5
const MAX_ZOOM = 2.5
const ZOOM_STEP = 0.15
const RENDER_MARGIN_PX = 320
const HORIZONTAL_PADDING_PX = 48

function fitScaleForPage(pageWidth: number, containerWidth: number): number {
  if (pageWidth <= 0 || containerWidth <= 0) return 1
  return Math.min(2, Math.max(0.25, containerWidth / pageWidth))
}

async function releasePdfDocument(doc: PDFDocumentProxy | null) {
  if (!doc) return
  try {
    await doc.cleanup()
  } catch {
    /* already released */
  }
}

async function releaseLoadingTask(task: PDFDocumentLoadingTask | null) {
  if (!task || task.destroyed) return
  try {
    await task.destroy()
  } catch {
    /* already released */
  }
}

type PdfPreviewViewerProps = {
  blobUrl: string
  name: string
}

function PdfPageCanvas({
  pdf,
  pageNumber,
  containerWidth,
  zoom,
  shouldRender,
}: {
  pdf: PDFDocumentProxy
  pageNumber: number
  containerWidth: number
  zoom: number
  shouldRender: boolean
}) {
  const canvasRef = useRef<HTMLCanvasElement>(null)
  const renderTaskRef = useRef<{ cancel: () => void } | null>(null)

  useEffect(() => {
    if (!shouldRender || containerWidth <= 0 || zoom <= 0) return

    let cancelled = false
    const canvas = canvasRef.current
    if (!canvas) return

    ;(async () => {
      try {
        const page = await pdf.getPage(pageNumber)
        if (cancelled) return

        const base = page.getViewport({ scale: 1 })
        const scale = fitScaleForPage(base.width, containerWidth) * zoom
        const viewport = page.getViewport({ scale })
        const ctx = canvas.getContext("2d")
        if (!ctx) return

        const outputScale = window.devicePixelRatio || 1
        canvas.width = Math.floor(viewport.width * outputScale)
        canvas.height = Math.floor(viewport.height * outputScale)
        canvas.style.width = `${viewport.width}px`
        canvas.style.height = `${viewport.height}px`
        ctx.setTransform(outputScale, 0, 0, outputScale, 0, 0)

        renderTaskRef.current?.cancel()
        const task = page.render({ canvasContext: ctx, viewport, canvas })
        renderTaskRef.current = task
        await task.promise
      } catch {
        /* render cancelled or viewer unmounted */
      }
    })()

    return () => {
      cancelled = true
      renderTaskRef.current?.cancel()
      renderTaskRef.current = null
    }
  }, [pdf, pageNumber, containerWidth, zoom, shouldRender])

  return (
    <canvas
      ref={canvasRef}
      className="block bg-white"
      aria-hidden
    />
  )
}

function printPdfBlob(blobUrl: string, title: string) {
  const frame = document.createElement("iframe")
  frame.style.position = "fixed"
  frame.style.right = "0"
  frame.style.bottom = "0"
  frame.style.width = "0"
  frame.style.height = "0"
  frame.style.border = "0"
  frame.setAttribute("title", title)
  frame.src = blobUrl

  const cleanup = () => {
    frame.remove()
  }

  frame.onload = () => {
    try {
      frame.contentWindow?.focus()
      frame.contentWindow?.print()
    } finally {
      window.setTimeout(cleanup, 1000)
    }
  }

  document.body.appendChild(frame)
}

export function PdfPreviewViewer({ blobUrl, name }: PdfPreviewViewerProps) {
  const scrollRef = useRef<HTMLDivElement>(null)
  const pageRefs = useRef<Map<number, HTMLDivElement>>(new Map())
  const loadingTaskRef = useRef<PDFDocumentLoadingTask | null>(null)
  const [pdf, setPdf] = useState<PDFDocumentProxy | null>(null)
  const [numPages, setNumPages] = useState(0)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [currentPage, setCurrentPage] = useState(1)
  const [containerWidth, setContainerWidth] = useState(0)
  const [zoom, setZoom] = useState(1)
  const [visiblePages, setVisiblePages] = useState<Set<number>>(() => new Set([1]))

  useEffect(() => {
    let destroyed = false
    let doc: PDFDocumentProxy | null = null

    loadingTaskRef.current = null
    setLoading(true)
    setError(null)
    setPdf(null)
    setNumPages(0)
    setCurrentPage(1)
    setZoom(1)
    setVisiblePages(new Set([1]))
    pageRefs.current.clear()

    ;(async () => {
      try {
        const response = await fetch(blobUrl)
        if (!response.ok) throw new Error("fetch failed")
        const data = await response.arrayBuffer()
        if (destroyed) return

        const task = getDocument({ data, isEvalSupported: false })
        loadingTaskRef.current = task
        const loaded = await task.promise
        if (destroyed) {
          await releasePdfDocument(loaded)
          return
        }
        doc = loaded
        setPdf(loaded)
        setNumPages(loaded.numPages)
        setLoading(false)
      } catch {
        if (!destroyed) {
          setError("Impossible d’afficher ce PDF.")
          setLoading(false)
        }
      }
    })()

    return () => {
      destroyed = true
      void releaseLoadingTask(loadingTaskRef.current)
      void releasePdfDocument(doc)
    }
  }, [blobUrl])

  const updateContainerWidth = useCallback(() => {
    const el = scrollRef.current
    if (!el) return
    const width = el.clientWidth - HORIZONTAL_PADDING_PX
    if (width > 0) setContainerWidth(width)
  }, [])

  useEffect(() => {
    if (!pdf) return
    updateContainerWidth()
    const el = scrollRef.current
    if (!el) return
    const ro = new ResizeObserver(updateContainerWidth)
    ro.observe(el)
    return () => ro.disconnect()
  }, [pdf, updateContainerWidth])

  const updateCurrentPageFromScroll = useCallback(() => {
    const root = scrollRef.current
    if (!root || pageRefs.current.size === 0) return
    const focusY = root.getBoundingClientRect().top + root.clientHeight * 0.35
    let bestPage = 1
    let bestDist = Number.POSITIVE_INFINITY
    pageRefs.current.forEach((node, page) => {
      const dist = Math.abs(node.getBoundingClientRect().top - focusY)
      if (dist < bestDist) {
        bestDist = dist
        bestPage = page
      }
    })
    setCurrentPage(bestPage)
  }, [])

  useEffect(() => {
    if (!pdf || numPages === 0) return
    const root = scrollRef.current
    if (!root) return

    const observer = new IntersectionObserver(
      (entries) => {
        setVisiblePages((prev) => {
          const merged = new Set(prev)
          for (const entry of entries) {
            const page = Number((entry.target as HTMLElement).dataset.page)
            if (!page) continue
            if (entry.isIntersecting) merged.add(page)
          }
          return merged
        })
        updateCurrentPageFromScroll()
      },
      { root, rootMargin: `${RENDER_MARGIN_PX}px 0px`, threshold: 0 },
    )

    const nodes = [...pageRefs.current.values()]
    nodes.forEach((node) => observer.observe(node))

    root.addEventListener("scroll", updateCurrentPageFromScroll, { passive: true })
    updateCurrentPageFromScroll()

    return () => {
      observer.disconnect()
      root.removeEventListener("scroll", updateCurrentPageFromScroll)
    }
  }, [pdf, numPages, updateCurrentPageFromScroll])

  const scrollToPage = (page: number) => {
    const node = pageRefs.current.get(page)
    node?.scrollIntoView({ behavior: "smooth", block: "start" })
    setCurrentPage(page)
  }

  const setPageRef = (page: number) => (node: HTMLDivElement | null) => {
    if (node) pageRefs.current.set(page, node)
    else pageRefs.current.delete(page)
  }

  if (loading) {
    return (
      <div className="flex h-full flex-col items-center justify-center gap-2 text-zinc-400">
        <Loader2 className="h-10 w-10 animate-spin" aria-hidden />
        <span className="text-sm">Ouverture du PDF…</span>
      </div>
    )
  }

  if (error || !pdf) {
    return <p className="text-center text-sm text-zinc-400">{error ?? "Aperçu indisponible."}</p>
  }

  const zoomPercent = Math.round(zoom * 100)

  return (
    <div className="flex h-full min-h-0 flex-col">
      <div
        ref={scrollRef}
        className="min-h-0 flex-1 overflow-auto overscroll-contain bg-zinc-900/60"
        aria-label={`Aperçu PDF : ${name}`}
      >
        <div
          className={cn(
            "mx-auto flex w-max min-w-full flex-col items-center px-4 py-6",
            zoom <= 1 && "max-w-5xl",
          )}
          style={{ gap: PAGE_GAP_PX }}
        >
          {Array.from({ length: numPages }, (_, i) => {
            const pageNumber = i + 1
            return (
              <div
                key={pageNumber}
                ref={setPageRef(pageNumber)}
                data-page={pageNumber}
                className={cn(
                  "inline-flex w-max max-w-none flex-col scroll-mt-4",
                  "rounded-sm bg-white shadow-[0_8px_32px_rgba(0,0,0,0.45)] ring-1 ring-white/10",
                )}
              >
                <div className="flex w-full shrink-0 items-center justify-between border-b border-zinc-200/80 bg-zinc-50 px-3 py-1.5">
                  <span className="text-xs font-medium tabular-nums text-zinc-500">
                    Page {pageNumber}
                  </span>
                </div>
                <div className="flex shrink-0 justify-center bg-zinc-100 p-2 sm:p-3">
                  <PdfPageCanvas
                    pdf={pdf}
                    pageNumber={pageNumber}
                    containerWidth={containerWidth}
                    zoom={zoom}
                    shouldRender={visiblePages.has(pageNumber) && containerWidth > 0}
                  />
                </div>
              </div>
            )
          })}
        </div>
      </div>

      <div className="flex shrink-0 items-center justify-between gap-3 border-t border-white/10 bg-zinc-950/95 px-4 py-2.5 backdrop-blur-sm">
        <div className="flex items-center gap-1">
          <Button
            type="button"
            variant="ghost"
            size="icon"
            className="h-8 w-8 cursor-pointer text-zinc-400 hover:bg-white/10 hover:text-white"
            onClick={() => printPdfBlob(blobUrl, name)}
            aria-label="Imprimer"
          >
            <Printer className="h-4 w-4" />
          </Button>
          <Button
            type="button"
            variant="ghost"
            size="icon"
            className="h-8 w-8 cursor-pointer text-zinc-400 hover:bg-white/10 hover:text-white"
            onClick={() => setZoom((z) => Math.max(MIN_ZOOM, z - ZOOM_STEP))}
            disabled={zoom <= MIN_ZOOM}
            aria-label="Zoom arrière"
          >
            <ZoomOut className="h-4 w-4" />
          </Button>
          <span className="min-w-[3.5rem] text-center text-xs tabular-nums text-zinc-400">
            {zoomPercent}%
          </span>
          <Button
            type="button"
            variant="ghost"
            size="icon"
            className="h-8 w-8 cursor-pointer text-zinc-400 hover:bg-white/10 hover:text-white"
            onClick={() => setZoom((z) => Math.min(MAX_ZOOM, z + ZOOM_STEP))}
            disabled={zoom >= MAX_ZOOM}
            aria-label="Zoom avant"
          >
            <ZoomIn className="h-4 w-4" />
          </Button>
        </div>

        <p className="text-sm font-medium tabular-nums text-zinc-200">
          Page {currentPage} / {numPages}
        </p>

        <div className="flex items-center gap-1">
          <Button
            type="button"
            variant="ghost"
            size="icon"
            className="h-8 w-8 cursor-pointer text-zinc-400 hover:bg-white/10 hover:text-white"
            onClick={() => scrollToPage(Math.max(1, currentPage - 1))}
            disabled={currentPage <= 1}
            aria-label="Page précédente"
          >
            <ChevronUp className="h-4 w-4" />
          </Button>
          <Button
            type="button"
            variant="ghost"
            size="icon"
            className="h-8 w-8 cursor-pointer text-zinc-400 hover:bg-white/10 hover:text-white"
            onClick={() => scrollToPage(Math.min(numPages, currentPage + 1))}
            disabled={currentPage >= numPages}
            aria-label="Page suivante"
          >
            <ChevronDown className="h-4 w-4" />
          </Button>
        </div>
      </div>
    </div>
  )
}
