'use client';

import { useEffect, useMemo, useRef, useState } from 'react';
import { Document, Page, pdfjs } from 'react-pdf';
import 'react-pdf/dist/Page/AnnotationLayer.css';
import 'react-pdf/dist/Page/TextLayer.css';
import { Download, ExternalLink, Loader2, X } from 'lucide-react';
import { toast } from 'sonner';

import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import { triggerBlobDownload } from '@/lib/utils/download';
import { resolvePortIdFromSlug } from '@/lib/api/client';

// Same worker setup as the field-placement step. Required because
// react-pdf needs a parser worker; the CDN worker runs cross-origin
// but we hand it bytes via `{ data }` so it never has to fetch.
pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;

interface Props {
  open: boolean;
  onOpenChange: (next: boolean) => void;
  /** JSON body forwarded to /api/v1/reports/generate. */
  payload: unknown;
  /** Filename used when the rep clicks Download. */
  filename: string;
  /** Dialog header shown above the iframe. */
  title?: string;
}

/**
 * Preview-then-download flow for a generated PDF.
 *
 * Renders the PDF via react-pdf (PDF.js → canvas), which is the only
 * approach that works reliably across browsers. Both `<iframe src=blob>`
 * and `<object data=blob type=application/pdf>` are silently broken in
 * WebKit (iOS/iPadOS Safari refuses to embed PDFs in either) - canvas
 * rendering works there + everywhere else.
 *
 * Lifecycle:
 *   - On open: POST /api/v1/reports/generate, read the response bytes
 *     into a Uint8Array, hand to react-pdf via { data }
 *   - On payload change: re-fetch
 *   - On close: clear the bytes + the blob-url alias kept for the
 *     "Open in new tab" + Download buttons
 */
export function PdfPreviewModal({
  open,
  onOpenChange,
  payload,
  filename,
  title = 'Preview report',
}: Props) {
  const [fileBytes, setFileBytes] = useState<Uint8Array | null>(null);
  const [blob, setBlob] = useState<Blob | null>(null);
  const [url, setUrl] = useState<string | null>(null);
  const [numPages, setNumPages] = useState<number>(0);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const [pageWidth, setPageWidth] = useState<number>(800);

  // Re-fetch the preview whenever the modal opens (or the payload
  // changes while open). Reads the response bytes into a Uint8Array
  // up-front so PDF.js can parse synchronously inside its worker - no
  // cross-origin fetch from the worker context.
  useEffect(() => {
    if (!open) return;
    let cancelled = false;
    let createdUrl: string | null = null;

    async function loadPreview() {
      setLoading(true);
      setError(null);
      setNumPages(0);
      try {
        const headers = new Headers({ 'Content-Type': 'application/json' });
        if (typeof window !== 'undefined') {
          const slug = window.location.pathname.split('/').filter(Boolean)[0];
          if (slug && slug !== 'login' && slug !== 'portal' && slug !== 'api') {
            const portId = await resolvePortIdFromSlug(slug);
            if (portId) headers.set('X-Port-Id', portId);
          }
        }
        const res = await fetch('/api/v1/reports/generate', {
          method: 'POST',
          headers,
          body: JSON.stringify(payload),
        });
        if (!res.ok) {
          const text = await res.text();
          throw new Error(text || `Preview failed (${res.status})`);
        }
        const b = await res.blob();
        if (cancelled) return;
        const buffer = await b.arrayBuffer();
        if (cancelled) return;
        createdUrl = URL.createObjectURL(b);
        setBlob(b);
        setFileBytes(new Uint8Array(buffer));
        setUrl(createdUrl);
      } catch (err) {
        if (cancelled) return;
        setError(err instanceof Error ? err.message : 'Preview failed');
      } finally {
        if (!cancelled) setLoading(false);
      }
    }

    void loadPreview();

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

  // Belt-and-braces revoke on unmount.
  useEffect(
    () => () => {
      if (url) URL.revokeObjectURL(url);
    },
    [url],
  );

  // Track the container width so the rendered pages fit edge-to-edge
  // without horizontal overflow. ResizeObserver covers the case where
  // the dialog re-flows (e.g. an iPad rotation) after first render.
  useEffect(() => {
    const node = containerRef.current;
    if (!node) return;
    const update = () => {
      const w = node.clientWidth;
      if (w > 0) setPageWidth(Math.min(w - 32, 1200)); // 16px padding on each side
    };
    update();
    const ro = new ResizeObserver(update);
    ro.observe(node);
    return () => ro.disconnect();
  }, [open]);

  // Memoise the `{ data }` source - react-pdf reparses every time the
  // `file` prop's reference identity changes, so a fresh object literal
  // per render would restart parsing and flicker the placeholder.
  const pdfFileSource = useMemo(() => (fileBytes ? { data: fileBytes } : null), [fileBytes]);

  function handleDownload() {
    if (!blob) return;
    triggerBlobDownload(blob, filename);
    toast.success('Report downloaded');
    onOpenChange(false);
  }

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-5xl w-[95vw] h-[90vh] flex flex-col">
        <DialogHeader>
          <DialogTitle>{title}</DialogTitle>
          <DialogDescription>
            Inline preview of the rendered PDF. Download to save the file or close to go back and
            tweak the configuration.
          </DialogDescription>
        </DialogHeader>

        <div
          ref={containerRef}
          className="flex-1 min-h-0 overflow-auto rounded-md border bg-muted/20"
        >
          {loading ? (
            <div className="flex h-full items-center justify-center gap-2 text-sm text-muted-foreground">
              <Loader2 className="h-4 w-4 animate-spin" aria-hidden />
              Rendering preview…
            </div>
          ) : error ? (
            <div className="flex h-full flex-col items-center justify-center gap-2 px-4 text-center">
              <div className="text-sm font-medium text-destructive">Preview failed</div>
              <div className="font-mono text-xs text-muted-foreground break-all">{error}</div>
            </div>
          ) : pdfFileSource ? (
            <div className="flex flex-col items-center gap-4 p-4">
              <Document
                file={pdfFileSource}
                onLoadSuccess={({ numPages: n }) => {
                  setNumPages(n);
                  setError(null);
                }}
                onLoadError={(err) => {
                  setError(err instanceof Error ? err.message : 'PDF render error');
                }}
                loading={
                  <div className="flex items-center justify-center gap-2 text-sm text-muted-foreground">
                    <Loader2 className="h-4 w-4 animate-spin" aria-hidden />
                    Parsing PDF…
                  </div>
                }
              >
                {Array.from({ length: numPages }, (_, i) => (
                  <Page
                    key={`page-${i + 1}`}
                    pageNumber={i + 1}
                    width={pageWidth}
                    renderAnnotationLayer={false}
                    renderTextLayer={false}
                    className="mb-4 shadow-md last:mb-0"
                  />
                ))}
              </Document>
              {numPages > 0 ? (
                <p className="pt-1 text-xs text-muted-foreground">
                  {numPages} {numPages === 1 ? 'page' : 'pages'}
                </p>
              ) : null}
            </div>
          ) : null}
        </div>

        <div className="flex items-center justify-end gap-2 pt-3">
          <Button variant="outline" onClick={() => onOpenChange(false)}>
            <X className="mr-1.5 h-4 w-4" aria-hidden />
            Close
          </Button>
          {url ? (
            <Button asChild variant="outline" disabled={loading}>
              <a href={url} target="_blank" rel="noreferrer">
                <ExternalLink className="mr-1.5 h-4 w-4" aria-hidden />
                Open in new tab
              </a>
            </Button>
          ) : null}
          <Button onClick={handleDownload} disabled={!blob || loading}>
            {loading ? (
              <Loader2 className="mr-1.5 h-4 w-4 animate-spin" aria-hidden />
            ) : (
              <Download className="mr-1.5 h-4 w-4" aria-hidden />
            )}
            Download
          </Button>
        </div>
      </DialogContent>
    </Dialog>
  );
}
