/**
 * Paginated DOCX preview (docx-preview) + per-page raster PDF (html2canvas + jsPDF).
 * See docs/PDF_converter.md.
 */

import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
  type ChangeEvent,
  type CSSProperties,
  type DragEvent,
} from 'react';
import {
  convertDocxToPdfBlob,
  type ConvertDocxToPdfLayoutInsights,
  createOffScreenDocxCaptureHost,
  DOCX_PDF_PREVIEW_SHELL_MAX_WIDTH_PX,
} from './convertDocxToPdfBlob';
import { convertUploadedDocxToOfficialPdf } from '@/lib/applicationFormDocxApi';

export type ConverterStatus =
  | 'idle'
  | 'rendering'
  | 'capturing'
  | 'ready'
  | 'error';

export interface DocxToPdfViewerProps {
  file?: File | null;
  hideFilePicker?: boolean;
  hidePreview?: boolean;
  onPdfReady?: (pdfBlob: Blob, sourceFile: File) => void;
  onStatusChange?: (status: ConverterStatus) => void;
  renderScale?: number;
  imageQuality?: number;
  losslessImages?: boolean;
  /**
   * Try server-side LibreOffice conversion (`/api/v1/docx/convert-pdf`) for
   * Word-faithful output, then fall back to browser conversion on failure.
   */
  preferOfficialServerPdf?: boolean;
  className?: string;
  style?: CSSProperties;
}

function formatBytes(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
}

export default function DocxToPdfViewer({
  file: externalFile,
  hideFilePicker = false,
  hidePreview = false,
  onPdfReady,
  onStatusChange,
  renderScale = 2,
  imageQuality = 0.95,
  losslessImages = false,
  preferOfficialServerPdf = true,
  className,
  style,
}: DocxToPdfViewerProps) {
  const [file, setFile] = useState<File | null>(externalFile ?? null);
  const [status, setStatusInternal] = useState<ConverterStatus>('idle');
  const [pageProgress, setPageProgress] = useState({ current: 0, total: 0 });
  const [error, setError] = useState<string | null>(null);
  const [isDragging, setIsDragging] = useState(false);
  const [serverFixJustifiedSoftBreaks, setServerFixJustifiedSoftBreaks] = useState(true);
  const [serverFixTableRowHeights, setServerFixTableRowHeights] = useState(false);
  const [pdfSource, setPdfSource] = useState<'server' | 'browser' | null>(null);
  const [layoutInsights, setLayoutInsights] = useState<ConvertDocxToPdfLayoutInsights | null>(null);

  /** Only the latest {@link convert} run may commit blob/status — avoids stale PDF after a newer docx is on screen. */
  const convertGenerationRef = useRef(0);
  const [pdfBlob, setPdfBlob] = useState<Blob | null>(null);
  const [pdfReady, setPdfReady] = useState(false);
  const [pdfPreviewUrl, setPdfPreviewUrl] = useState<string | null>(null);

  const setStatus = useCallback(
    (next: ConverterStatus) => {
      setStatusInternal(next);
      onStatusChange?.(next);
    },
    [onStatusChange],
  );

  useEffect(() => {
    if (externalFile !== undefined) setFile(externalFile);
  }, [externalFile]);

  useEffect(() => {
    if (!pdfBlob) {
      setPdfPreviewUrl(null);
      return;
    }
    const nextUrl = URL.createObjectURL(pdfBlob);
    setPdfPreviewUrl(nextUrl);
    return () => URL.revokeObjectURL(nextUrl);
  }, [pdfBlob]);

  const convert = useCallback(
    async (source: File) => {
      const tempHost = createOffScreenDocxCaptureHost();
      const container: HTMLElement = tempHost;

      const gen = ++convertGenerationRef.current;

      setError(null);
      setPdfBlob(null);
      setPdfReady(false);
      setPdfSource(null);
      setLayoutInsights(null);
      setPageProgress({ current: 0, total: 0 });

      try {
        const serverPdfPromise = preferOfficialServerPdf
          ? convertUploadedDocxToOfficialPdf(source, {
              relaxJustifiedSoftBreaks: serverFixJustifiedSoftBreaks,
              stripTableRowHeights: serverFixTableRowHeights,
            })
              .then((buffer) => new Blob([buffer], { type: 'application/pdf' }))
              .catch(() => null)
          : Promise.resolve<Blob | null>(null);

        const browserBlob = await convertDocxToPdfBlob(source, container, {
          renderScale,
          imageQuality,
          losslessImages,
          onPhaseRendering: () => {
            if (gen !== convertGenerationRef.current) return;
            setStatus('rendering');
          },
          onPhaseCapturing: (pageCount) => {
            if (gen !== convertGenerationRef.current) return;
            setStatus('capturing');
            setPageProgress({ current: 0, total: pageCount });
          },
          onCaptureProgress: (current, total) => {
            if (gen !== convertGenerationRef.current) return;
            setPageProgress({ current, total });
          },
          onLayoutAnalysed: (insights) => {
            if (gen !== convertGenerationRef.current) return;
            setLayoutInsights(insights);
          },
        });
        const serverBlob = await serverPdfPromise;
        const blob = serverBlob ?? browserBlob;

        if (gen !== convertGenerationRef.current) return;

        setPdfBlob(blob);
        setPdfSource(serverBlob ? 'server' : 'browser');
        setPdfReady(true);
        setStatus('ready');
        onPdfReady?.(blob, source);
      } catch (e) {
        if (gen !== convertGenerationRef.current) return;
        const msg = e instanceof Error ? e.message : String(e);
        setError(msg);
        setStatus('error');
      } finally {
        if (tempHost?.parentNode) {
          tempHost.parentNode.removeChild(tempHost);
        }
      }
    },
    [
      renderScale,
      imageQuality,
      losslessImages,
      preferOfficialServerPdf,
      serverFixJustifiedSoftBreaks,
      serverFixTableRowHeights,
      onPdfReady,
      setStatus,
    ],
  );

  useEffect(() => {
    if (file) void convert(file);
  }, [file, convert]);

  const handlePick = (e: ChangeEvent<HTMLInputElement>) => {
    const picked = e.target.files?.[0];
    if (picked) setFile(picked);
  };

  const handleDrop = (e: DragEvent<HTMLLabelElement>) => {
    e.preventDefault();
    setIsDragging(false);
    const dropped = e.dataTransfer.files?.[0];
    if (dropped && /\.docx$/i.test(dropped.name)) setFile(dropped);
  };

  const handleExportPdf = useCallback(() => {
    if (!pdfBlob) return;

    const baseName =
      file?.name.replace(/\.[^.]+$/, '')?.trim() || 'document';
    const safeBaseName = baseName.replace(/[<>:"/\\|?*\x00-\x1F]/g, '_');
    const pdfName = `${safeBaseName || 'document'}.pdf`;

    const url = URL.createObjectURL(pdfBlob);
    const link = document.createElement('a');
    link.href = url;
    link.download = pdfName;
    link.rel = 'noopener';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    window.setTimeout(() => URL.revokeObjectURL(url), 4000);
  }, [pdfBlob, file]);

  const sourceLabel =
    pdfSource === 'server'
      ? 'Official PDF (server LibreOffice)'
      : pdfSource === 'browser'
        ? 'Fallback PDF (browser capture)'
        : null;

  const exportPdfButtonStyle: CSSProperties = {
    padding: '0.5rem 1rem',
    borderRadius: 6,
    border: '1px solid #1e293b',
    background: '#1e293b',
    color: 'white',
    cursor: 'pointer',
    fontSize: 14,
  };

  const statusLabel = useMemo(() => {
    switch (status) {
      case 'rendering':
        return 'Rendering document…';
      case 'capturing':
        return pageProgress.total > 0
          ? `Capturing page ${pageProgress.current} of ${pageProgress.total}…`
          : 'Capturing pages…';
      case 'ready':
        return 'Ready.';
      case 'error':
        return `Error: ${error}`;
      default:
        return 'Waiting for a .docx file.';
    }
  }, [status, pageProgress, error]);

  return (
    <div className={className} style={style}>
      {!hideFilePicker && (
        <label
          onDragOver={(e) => {
            e.preventDefault();
            setIsDragging(true);
          }}
          onDragLeave={() => setIsDragging(false)}
          onDrop={handleDrop}
          style={{
            display: 'block',
            padding: '1.25rem',
            border: `2px dashed ${isDragging ? '#3b82f6' : '#cbd5e1'}`,
            borderRadius: 8,
            background: isDragging ? '#eff6ff' : '#f8fafc',
            cursor: 'pointer',
            textAlign: 'center',
          }}
        >
          <input
            type="file"
            accept=".docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
            onChange={handlePick}
            style={{ display: 'none' }}
          />
          <div style={{ fontWeight: 500 }}>
            {file ? file.name : 'Drop a .docx file here, or click to browse'}
          </div>
          {file && (
            <div style={{ fontSize: 12, color: '#64748b', marginTop: 4 }}>
              {formatBytes(file.size)}
            </div>
          )}
        </label>
      )}

      {status !== 'idle' && (
        <div
          role="status"
          aria-live="polite"
          style={{
            marginTop: 12,
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'space-between',
            gap: 12,
            fontSize: 14,
            color: status === 'error' ? '#b91c1c' : '#475569',
          }}
        >
          <span>{statusLabel}</span>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            {sourceLabel ? (
              <span
                style={{
                  fontSize: 12,
                  color: '#334155',
                  background: '#e2e8f0',
                  border: '1px solid #cbd5e1',
                  borderRadius: 999,
                  padding: '2px 8px',
                  whiteSpace: 'nowrap',
                }}
              >
                {sourceLabel}
              </span>
            ) : null}
            {pdfReady && hidePreview ? (
              <button
                type="button"
                onClick={handleExportPdf}
                style={exportPdfButtonStyle}
                aria-label="Download converted PDF"
              >
                Download PDF
              </button>
            ) : null}
          </div>
        </div>
      )}

      {!hidePreview ? (
        <>
          {pdfReady && pdfPreviewUrl ? (
            <div
              style={{
                marginTop: 16,
                maxWidth: DOCX_PDF_PREVIEW_SHELL_MAX_WIDTH_PX,
                marginLeft: 'auto',
                marginRight: 'auto',
                borderRadius: 6,
                overflow: 'hidden',
                border: '1px solid #cbd5e1',
                background: '#fff',
              }}
            >
              <div
                style={{
                  padding: '10px 12px',
                  fontSize: 13,
                  color: '#334155',
                  background: '#f8fafc',
                  borderBottom: '1px solid #cbd5e1',
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'space-between',
                  gap: 12,
                  flexWrap: 'wrap',
                }}
              >
                <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
                  <span>PDF preview (final output)</span>
                  <span style={{ color: '#64748b' }}>
                    {preferOfficialServerPdf
                      ? 'Configured to prefer server conversion'
                      : 'Browser conversion mode'}
                  </span>
                </div>
                {preferOfficialServerPdf ? (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
                    <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13 }}>
                      <input
                        type="checkbox"
                        checked={serverFixJustifiedSoftBreaks}
                        onChange={(e) => setServerFixJustifiedSoftBreaks(e.target.checked)}
                      />
                      Fix stretched text lines
                    </label>
                    <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13 }}>
                      <input
                        type="checkbox"
                        checked={serverFixTableRowHeights}
                        onChange={(e) => setServerFixTableRowHeights(e.target.checked)}
                      />
                      Relax fixed table row heights
                    </label>
                  </div>
                ) : null}
                <button
                  type="button"
                  onClick={handleExportPdf}
                  style={exportPdfButtonStyle}
                  aria-label="Export converted PDF"
                >
                  Export PDF
                </button>
              </div>
              {pdfSource === 'browser' && layoutInsights?.hasFloatingShapeCandidates ? (
                <div
                  role="note"
                  style={{
                    padding: '10px 12px',
                    fontSize: 12,
                    lineHeight: 1.5,
                    color: '#1f2937',
                    background: '#fef9c3',
                    borderBottom: '1px solid #fde68a',
                  }}
                >
                  This document appears to contain floating shapes (signature lines / checkboxes).
                  Please spot-check alignment in the DOCX preview before using browser PDF as final.
                  For pixel-critical output, prefer Official PDF (server LibreOffice).
                </div>
              ) : null}
              {pdfSource === 'browser' && layoutInsights?.appliedTimesFallbackOverride ? (
                <div
                  role="note"
                  style={{
                    padding: '10px 12px',
                    fontSize: 12,
                    lineHeight: 1.5,
                    color: '#334155',
                    background: '#eff6ff',
                    borderBottom: '1px solid #bfdbfe',
                  }}
                >
                  Times New Roman was unavailable in this environment, so a scoped fallback font
                  override was applied. Verify checkbox glyphs still render correctly.
                </div>
              ) : null}
              <iframe
                title="Generated PDF preview"
                src={pdfPreviewUrl}
                style={{ width: '100%', height: '70vh', border: 0 }}
              />
            </div>
          ) : null}
        </>
      ) : null}
    </div>
  );
}
