import { useCallback, useEffect, useRef, useState } from 'react';
import { jsPDF } from 'jspdf';
import {
  Upload,
  RotateCcw,
  RotateCw,
  FlipHorizontal,
  FlipVertical,
  Trash2,
  FileText,
  Repeat,
} from 'lucide-react';
import Modal from './Modal';
import Button from './Button';

interface ImageItem {
  id: string;
  dataUrl: string;
  naturalWidth: number;
  naturalHeight: number;
  rotation: 0 | 90 | 180 | 270;
  flipH: boolean;
  flipV: boolean;
  fileName: string;
}

interface JpgToPdfModalProps {
  isOpen: boolean;
  onClose: () => void;
  onComplete: (pdfFile: File) => void;
  fileNameHint?: string;
}

// Pentest 68.2 (INFO): Self-DoS-Schutz – Modal kann sonst den Tab des
// Uploaders selbst zum Absturz bringen. Werte konservativ gewählt:
// 50 Bilder × 25 MB = 1.25 GB ist mehr als jede legitime Vollmacht.
const MAX_IMAGES = 50;
const MAX_IMAGE_BYTES = 25 * 1024 * 1024;

// Smartphone-Fotos haben oft 4000-6000 px Kante. Bei JPEG-Quality
// 0.95 sind das 5-10 MB pro Seite, zwei Bilder = >10 MB PDF.
// 2400 px lange Kante entspricht ~290 DPI auf A4 (Druckqualität) und
// reduziert die Pixelmenge auf 25-36 % vom Original → PDF wird
// drastisch kleiner, sichtbarer Unterschied praktisch null.
const MAX_DIMENSION = 2400;
const EMBED_QUALITY = 0.92;

function makeId() {
  return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
}

function readFileAsDataUrl(file: File): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result as string);
    reader.onerror = () => reject(reader.error);
    reader.readAsDataURL(file);
  });
}

function loadImage(dataUrl: string): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = () => reject(new Error('Bild-Decode fehlgeschlagen'));
    img.src = dataUrl;
  });
}

function downscaleIfNeeded(image: HTMLImageElement): HTMLCanvasElement | null {
  const w = image.naturalWidth;
  const h = image.naturalHeight;
  if (w <= MAX_DIMENSION && h <= MAX_DIMENSION) return null;
  const scale = MAX_DIMENSION / Math.max(w, h);
  const newW = Math.round(w * scale);
  const newH = Math.round(h * scale);
  const canvas = document.createElement('canvas');
  canvas.width = newW;
  canvas.height = newH;
  const ctx = canvas.getContext('2d');
  if (!ctx) return null;
  ctx.imageSmoothingQuality = 'high';
  ctx.drawImage(image, 0, 0, newW, newH);
  return canvas;
}

function renderImageToCanvas(image: HTMLImageElement, item: ImageItem): HTMLCanvasElement {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  if (!ctx) throw new Error('Canvas-Kontext konnte nicht erstellt werden');

  const w = image.naturalWidth;
  const h = image.naturalHeight;
  const rotated = item.rotation === 90 || item.rotation === 270;

  canvas.width = rotated ? h : w;
  canvas.height = rotated ? w : h;

  ctx.save();
  ctx.translate(canvas.width / 2, canvas.height / 2);
  ctx.rotate((item.rotation * Math.PI) / 180);
  ctx.scale(item.flipH ? -1 : 1, item.flipV ? -1 : 1);
  ctx.drawImage(image, -w / 2, -h / 2);
  ctx.restore();

  return canvas;
}

export default function JpgToPdfModal({
  isOpen,
  onClose,
  onComplete,
  fileNameHint,
}: JpgToPdfModalProps) {
  const [images, setImages] = useState<ImageItem[]>([]);
  const [dragSrcIdx, setDragSrcIdx] = useState<number | null>(null);
  const [isOverModal, setIsOverModal] = useState(false);
  const [isBuilding, setIsBuilding] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (!isOpen) {
      setImages([]);
      setDragSrcIdx(null);
      setIsOverModal(false);
      setIsBuilding(false);
      setError(null);
    }
  }, [isOpen]);

  const addFiles = useCallback(async (files: FileList | File[]) => {
    const list = Array.from(files).filter((f) => f.type.startsWith('image/'));
    if (list.length === 0) {
      setError('Nur Bilddateien erlaubt (JPG/PNG).');
      return;
    }
    setError(null);
    const added: ImageItem[] = [];
    for (const file of list) {
      // 68.2: Self-DoS-Schutz – harte Schranken pro Bild und gesamt.
      if (file.size > MAX_IMAGE_BYTES) {
        setError(`Bild zu groß (max. ${Math.round(MAX_IMAGE_BYTES / 1024 / 1024)} MB): ${file.name || 'unbenannt'}`);
        continue;
      }
      if (images.length + added.length >= MAX_IMAGES) {
        setError(`Maximal ${MAX_IMAGES} Bilder pro PDF erlaubt.`);
        break;
      }
      try {
        const rawDataUrl = await readFileAsDataUrl(file);
        const img = await loadImage(rawDataUrl);
        // Beim Hinzufügen direkt auf MAX_DIMENSION runterskalieren, damit
        // die Vorschau, das Rendern in der PDF und die finale Dateigröße
        // alle auf vernünftigen Pixelmaßen arbeiten.
        const downscaled = downscaleIfNeeded(img);
        const dataUrl = downscaled
          ? downscaled.toDataURL('image/jpeg', EMBED_QUALITY)
          : rawDataUrl;
        const finalW = downscaled ? downscaled.width : img.naturalWidth;
        const finalH = downscaled ? downscaled.height : img.naturalHeight;
        added.push({
          id: makeId(),
          dataUrl,
          naturalWidth: finalW,
          naturalHeight: finalH,
          rotation: 0,
          flipH: false,
          flipV: false,
          fileName: file.name || 'clipboard.png',
        });
      } catch {
        setError(`Bild konnte nicht geladen werden: ${file.name || 'unbenannt'}`);
      }
    }
    if (added.length > 0) {
      setImages((prev) => [...prev, ...added]);
    }
  }, [images.length]);

  useEffect(() => {
    if (!isOpen) return;
    const handler = (e: ClipboardEvent) => {
      const items = e.clipboardData?.items;
      if (!items) return;
      const files: File[] = [];
      for (let i = 0; i < items.length; i++) {
        const it = items[i];
        if (it.kind === 'file' && it.type.startsWith('image/')) {
          const f = it.getAsFile();
          if (f) files.push(f);
        }
      }
      if (files.length > 0) {
        e.preventDefault();
        addFiles(files);
      }
    };
    document.addEventListener('paste', handler);
    return () => document.removeEventListener('paste', handler);
  }, [isOpen, addFiles]);

  const rotate = (id: string, delta: 90 | -90 | 180) => {
    setImages((prev) =>
      prev.map((it) =>
        it.id === id
          ? {
              ...it,
              rotation: ((((it.rotation + delta) % 360) + 360) % 360) as ImageItem['rotation'],
            }
          : it
      )
    );
  };

  const flip = (id: string, axis: 'h' | 'v') => {
    setImages((prev) =>
      prev.map((it) =>
        it.id === id
          ? { ...it, ...(axis === 'h' ? { flipH: !it.flipH } : { flipV: !it.flipV }) }
          : it
      )
    );
  };

  const remove = (id: string) => {
    setImages((prev) => prev.filter((it) => it.id !== id));
  };

  const handleDragStart = (idx: number) => (e: React.DragEvent) => {
    setDragSrcIdx(idx);
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/plain', String(idx));
  };

  const handleItemDragOver = (e: React.DragEvent) => {
    if (dragSrcIdx === null) return;
    e.preventDefault();
    e.dataTransfer.dropEffect = 'move';
  };

  const handleDropOnItem = (idx: number) => (e: React.DragEvent) => {
    if (dragSrcIdx === null) return;
    e.preventDefault();
    e.stopPropagation();
    if (dragSrcIdx === idx) {
      setDragSrcIdx(null);
      return;
    }
    setImages((prev) => {
      const next = [...prev];
      const [moved] = next.splice(dragSrcIdx, 1);
      next.splice(idx, 0, moved);
      return next;
    });
    setDragSrcIdx(null);
  };

  const handleModalDragOver = (e: React.DragEvent) => {
    if (e.dataTransfer.types.includes('Files')) {
      e.preventDefault();
      setIsOverModal(true);
    }
  };
  const handleModalDragLeave = (e: React.DragEvent) => {
    if (e.target === e.currentTarget) setIsOverModal(false);
  };
  const handleModalDrop = (e: React.DragEvent) => {
    if (e.dataTransfer.files.length > 0) {
      e.preventDefault();
      setIsOverModal(false);
      addFiles(e.dataTransfer.files);
    }
  };

  const buildPdf = async () => {
    if (images.length === 0) return;
    setIsBuilding(true);
    setError(null);
    try {
      const a4w = 210;
      const a4h = 297;
      let pdf: jsPDF | null = null;

      for (let i = 0; i < images.length; i++) {
        const item = images[i];
        const untouched = item.rotation === 0 && !item.flipH && !item.flipV;

        // Fix 1: ungedrehte/ungespiegelte Bilder bekommen ihre Original-Bytes
        // direkt in die PDF eingebettet – kein Canvas-Re-Encode, kein
        // Quality-Aufblasen. Ein 2-MB-JPEG bleibt 2 MB statt 8-15 MB zu werden.
        // Fix 2: wenn doch transformiert wird (Rotation/Flip), Canvas mit
        // Quality 0.95 statt 1.0 – visuell identisch für Foto-Inhalte, aber
        // 50-70 % kleiner.
        let imageData: string;
        let imageFormat: 'JPEG' | 'PNG';
        let srcW: number;
        let srcH: number;

        if (untouched) {
          imageData = item.dataUrl;
          imageFormat = item.dataUrl.startsWith('data:image/png') ? 'PNG' : 'JPEG';
          srcW = item.naturalWidth;
          srcH = item.naturalHeight;
        } else {
          const img = await loadImage(item.dataUrl);
          const canvas = renderImageToCanvas(img, item);
          imageData = canvas.toDataURL('image/jpeg', EMBED_QUALITY);
          imageFormat = 'JPEG';
          srcW = canvas.width;
          srcH = canvas.height;
        }

        const orientation: 'portrait' | 'landscape' =
          srcW > srcH ? 'landscape' : 'portrait';
        const pageW = orientation === 'landscape' ? a4h : a4w;
        const pageH = orientation === 'landscape' ? a4w : a4h;

        if (!pdf) {
          pdf = new jsPDF({ orientation, unit: 'mm', format: 'a4' });
        } else {
          pdf.addPage('a4', orientation);
        }

        const margin = 5;
        const maxW = pageW - 2 * margin;
        const maxH = pageH - 2 * margin;
        const ratio = Math.min(maxW / srcW, maxH / srcH);
        const w = srcW * ratio;
        const h = srcH * ratio;
        const x = (pageW - w) / 2;
        const y = (pageH - h) / 2;

        pdf.addImage(imageData, imageFormat, x, y, w, h, undefined, 'SLOW');
      }

      const blob = pdf!.output('blob');
      const base = (fileNameHint || 'bilder').replace(/[^\w.-]+/g, '_').slice(0, 80) || 'bilder';
      const file = new File([blob], `${base}.pdf`, { type: 'application/pdf' });
      onComplete(file);
      onClose();
    } catch (e) {
      console.error('PDF-Erstellung fehlgeschlagen:', e);
      setError('PDF konnte nicht erstellt werden.');
    } finally {
      setIsBuilding(false);
    }
  };

  return (
    <Modal isOpen={isOpen} onClose={onClose} title="JPGs zu PDF" size="xl">
      <div
        onDragOver={handleModalDragOver}
        onDragLeave={handleModalDragLeave}
        onDrop={handleModalDrop}
        className={`space-y-4 ${isOverModal ? 'ring-2 ring-blue-400 rounded-lg' : ''}`}
      >
        <div className="flex flex-wrap items-center justify-between gap-3">
          <p className="text-sm text-gray-600">
            Bilder per Klick wählen, hier reinziehen, oder mit{' '}
            <kbd className="px-1.5 py-0.5 border rounded text-xs">Strg</kbd>+
            <kbd className="px-1.5 py-0.5 border rounded text-xs">V</kbd> aus der Zwischenablage
            einfügen.
          </p>
          <Button
            variant="secondary"
            size="sm"
            onClick={() => fileInputRef.current?.click()}
            disabled={isBuilding}
          >
            <Upload className="w-4 h-4 mr-1" /> Bilder hinzufügen
          </Button>
          <input
            ref={fileInputRef}
            type="file"
            accept="image/*"
            multiple
            className="hidden"
            onChange={(e) => {
              if (e.target.files && e.target.files.length > 0) addFiles(e.target.files);
              e.target.value = '';
            }}
          />
        </div>

        {error && (
          <div className="text-sm text-red-600 bg-red-50 border border-red-200 rounded px-3 py-2">
            {error}
          </div>
        )}

        {images.length === 0 ? (
          <div
            onClick={() => fileInputRef.current?.click()}
            className="border-2 border-dashed rounded-lg p-12 text-center cursor-pointer border-gray-300 hover:border-gray-400 hover:bg-gray-50"
          >
            <Upload className="w-8 h-8 text-gray-400 mx-auto mb-2" />
            <p className="text-sm text-gray-600">
              Bilder hier hineinziehen oder zum Auswählen klicken
            </p>
            <p className="text-xs text-gray-400 mt-1">
              JPG / PNG · Reihenfolge per Drag &amp; Drop, einzeln drehen/spiegeln
            </p>
          </div>
        ) : (
          <>
            <div className="text-xs text-gray-500">
              {images.length} {images.length === 1 ? 'Bild' : 'Bilder'} · Reihenfolge per
              Drag&amp;Drop, einzeln drehen/spiegeln. Jedes Bild = eine Seite.
            </div>
            <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3 max-h-[60vh] overflow-y-auto p-1">
              {images.map((item, idx) => {
                const transforms = [
                  `rotate(${item.rotation}deg)`,
                  `scaleX(${item.flipH ? -1 : 1})`,
                  `scaleY(${item.flipV ? -1 : 1})`,
                ].join(' ');
                return (
                  <div
                    key={item.id}
                    draggable
                    onDragStart={handleDragStart(idx)}
                    onDragOver={handleItemDragOver}
                    onDrop={handleDropOnItem(idx)}
                    className={`border rounded-lg p-2 bg-white shadow-sm cursor-move ${
                      dragSrcIdx === idx ? 'opacity-40' : ''
                    }`}
                    title="Zum Sortieren ziehen"
                  >
                    <div className="flex items-center justify-between mb-1.5 text-xs">
                      <span className="font-semibold text-gray-700">#{idx + 1}</span>
                      <button
                        type="button"
                        onClick={() => remove(item.id)}
                        className="text-red-500 hover:text-red-700"
                        title="Entfernen"
                      >
                        <Trash2 className="w-3.5 h-3.5" />
                      </button>
                    </div>
                    <div className="aspect-square bg-gray-100 rounded flex items-center justify-center overflow-hidden">
                      <img
                        src={item.dataUrl}
                        alt={item.fileName}
                        style={{ transform: transforms }}
                        className="max-w-full max-h-full object-contain transition-transform"
                        draggable={false}
                      />
                    </div>
                    <div className="mt-1.5 flex justify-center gap-0.5">
                      <button
                        type="button"
                        onClick={() => rotate(item.id, -90)}
                        className="p-1 hover:bg-gray-100 rounded"
                        title="90° gegen Uhrzeigersinn"
                      >
                        <RotateCcw className="w-3.5 h-3.5" />
                      </button>
                      <button
                        type="button"
                        onClick={() => rotate(item.id, 90)}
                        className="p-1 hover:bg-gray-100 rounded"
                        title="90° im Uhrzeigersinn"
                      >
                        <RotateCw className="w-3.5 h-3.5" />
                      </button>
                      <button
                        type="button"
                        onClick={() => rotate(item.id, 180)}
                        className="p-1 hover:bg-gray-100 rounded"
                        title="180° drehen"
                      >
                        <Repeat className="w-3.5 h-3.5" />
                      </button>
                      <button
                        type="button"
                        onClick={() => flip(item.id, 'h')}
                        className={`p-1 rounded ${
                          item.flipH ? 'bg-blue-100 text-blue-700' : 'hover:bg-gray-100'
                        }`}
                        title="Horizontal spiegeln"
                      >
                        <FlipHorizontal className="w-3.5 h-3.5" />
                      </button>
                      <button
                        type="button"
                        onClick={() => flip(item.id, 'v')}
                        className={`p-1 rounded ${
                          item.flipV ? 'bg-blue-100 text-blue-700' : 'hover:bg-gray-100'
                        }`}
                        title="Vertikal spiegeln"
                      >
                        <FlipVertical className="w-3.5 h-3.5" />
                      </button>
                    </div>
                  </div>
                );
              })}
            </div>
          </>
        )}

        <div className="flex justify-end gap-2 pt-2 border-t">
          <Button variant="ghost" onClick={onClose} disabled={isBuilding}>
            Abbrechen
          </Button>
          <Button onClick={buildPdf} disabled={images.length === 0 || isBuilding}>
            <FileText className="w-4 h-4 mr-1" />
            {isBuilding ? 'Erstelle PDF...' : 'PDF erstellen & hochladen'}
          </Button>
        </div>
      </div>
    </Modal>
  );
}
