import React, { useCallback, useMemo, useRef, useState } from 'react';
import Cropper from 'react-easy-crop';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';

export type MediaItemType = 'image' | 'video';

export interface MediaItem {
  id: string;
  type: MediaItemType;
  url: string;
  file?: File; // lokal, noch nicht hochgeladen
}

export interface MediaUploadProps {
  value: MediaItem[];
  onChange: (items: MediaItem[]) => void;
  maxImages?: number;
  allowVideo?: boolean;
  aspectRatio?: number; // width / height, z.B. 9/16
  label?: string;
  description?: string;
  uploadFile?: (args: { file: File; type: MediaItemType }) => Promise<{ url: string; id?: string }>;
  onRemoveItem?: (item: MediaItem) => Promise<void> | void;
}

interface PendingImage {
  file: File;
  previewUrl: string;
}

const createObjectUrl = (file: File) => URL.createObjectURL(file);

const MediaUpload: React.FC<MediaUploadProps> = ({
  value,
  onChange,
  maxImages = 3,
  allowVideo = true,
  aspectRatio = 9 / 16,
  label = 'Media Upload',
  description,
  uploadFile,
  onRemoveItem,
}) => {
  const inputRef = useRef<HTMLInputElement | null>(null);
  const [cropOpen, setCropOpen] = useState(false);
  const [pendingImage, setPendingImage] = useState<PendingImage | null>(null);
  const [crop, setCrop] = useState({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const [croppedAreaPixels, setCroppedAreaPixels] = useState<{
    width: number;
    height: number;
    x: number;
    y: number;
  } | null>(null);

  const imageCount = useMemo(() => value.filter((m) => m.type === 'image').length, [value]);
  const videoCount = useMemo(() => value.filter((m) => m.type === 'video').length, [value]);

  const handleClickAdd = () => {
    if (!inputRef.current) return;
    inputRef.current.click();
  };

  const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    e.target.value = '';
    if (!file) return;

    if (file.type.startsWith('image/')) {
      if (imageCount >= maxImages) {
        alert(`Maximal ${maxImages} Bilder erlaubt.`);
        return;
      }
      const previewUrl = createObjectUrl(file);
      setPendingImage({ file, previewUrl });
      setCrop({ x: 0, y: 0 });
      setZoom(1);
      setCroppedAreaPixels(null);
      setCropOpen(true);
    } else if (file.type.startsWith('video/')) {
      if (!allowVideo) {
        alert('Video-Upload ist hier nicht erlaubt.');
        return;
      }
      if (videoCount >= 1) {
        alert('Es ist nur ein Video erlaubt.');
        return;
      }
      try {
        if (uploadFile) {
          const result = await uploadFile({ file, type: 'video' });
          const item: MediaItem = {
            id: result.id || `video-${Date.now()}`,
            type: 'video',
            url: result.url,
            file,
          };
          onChange([...value, item]);
        } else {
          const url = createObjectUrl(file);
          const item: MediaItem = {
            id: `video-${Date.now()}`,
            type: 'video',
            url,
            file,
          };
          onChange([...value, item]);
        }
      } catch (err) {
        console.error('Fehler beim Hochladen des Videos', err);
        alert('Fehler beim Hochladen des Videos.');
      }
    } else {
      alert('Nur Bild- oder Videodateien sind erlaubt.');
    }
  };

  const onCropComplete = useCallback((_: any, areaPixels: any) => {
    setCroppedAreaPixels(areaPixels);
  }, []);

  const getCroppedImg = async (imageSrc: string, cropPixels: { x: number; y: number; width: number; height: number }) => {
    const image = new Image();
    image.src = imageSrc;
    await new Promise((resolve, reject) => {
      image.onload = resolve;
      image.onerror = reject;
    });

    const canvas = document.createElement('canvas');
    canvas.width = cropPixels.width;
    canvas.height = cropPixels.height;
    const ctx = canvas.getContext('2d');
    if (!ctx) throw new Error('Canvas context not available');

    ctx.drawImage(
      image,
      cropPixels.x,
      cropPixels.y,
      cropPixels.width,
      cropPixels.height,
      0,
      0,
      cropPixels.width,
      cropPixels.height
    );

    return new Promise<File>((resolve, reject) => {
      canvas.toBlob((blob) => {
        if (!blob) {
          reject(new Error('Canvas is empty'));
          return;
        }
        const file = new File([blob], `image_${Date.now()}.jpg`, { type: 'image/jpeg' });
        resolve(file);
      }, 'image/jpeg', 0.9);
    });
  };

  const handleConfirmCrop = async () => {
    if (!pendingImage || !croppedAreaPixels) {
      setCropOpen(false);
      return;
    }
    try {
      const croppedFile = await getCroppedImg(pendingImage.previewUrl, croppedAreaPixels);

      if (uploadFile) {
        const result = await uploadFile({ file: croppedFile, type: 'image' });
        const item: MediaItem = {
          id: result.id || `image-${Date.now()}`,
          type: 'image',
          url: result.url,
          file: croppedFile,
        };
        onChange([...value, item]);
      } else {
        const url = createObjectUrl(croppedFile);
        const item: MediaItem = {
          id: `image-${Date.now()}`,
          type: 'image',
          url,
          file: croppedFile,
        };
        onChange([...value, item]);
      }
    } catch (e) {
      console.error('Fehler beim Croppen des Bildes', e);
      alert('Fehler beim Croppen des Bildes.');
    } finally {
      setCropOpen(false);
      if (pendingImage.previewUrl) {
        URL.revokeObjectURL(pendingImage.previewUrl);
      }
      setPendingImage(null);
    }
  };

  const handleRemove = async (item: MediaItem) => {
    try {
      if (onRemoveItem) {
        await onRemoveItem(item);
      }
      const items = value.filter((m) => m.id !== item.id);
      onChange(items);
    } catch (e) {
      console.error('Fehler beim Löschen des Mediums', e);
      alert('Fehler beim Löschen des Mediums.');
    }
  };

  return (
    <div className="space-y-2">
      <div className="flex items-center justify-between">
        <div className="space-y-1">
          <Label>{label}</Label>
          {description && (
            <p className="text-xs text-muted-foreground">{description}</p>
          )}
        </div>
        <Button type="button" variant="outline" size="sm" onClick={handleClickAdd}>
          Hinzufügen
        </Button>
      </div>

      <input
        ref={inputRef}
        type="file"
        accept={allowVideo ? 'image/*,video/*' : 'image/*'}
        className="hidden"
        onChange={handleFileChange}
      />

      <div className="grid grid-cols-3 gap-2">
        {value.map((item) => (
          <div
            key={item.id}
            className="relative aspect-[9/16] rounded-md overflow-hidden border bg-muted flex items-center justify-center"
          >
            {item.type === 'image' ? (
              <img src={item.url} alt="Bild" className="w-full h-full object-cover" />
            ) : (
              <video src={item.url} className="w-full h-full object-cover" controls />
            )}
            <button
              type="button"
              onClick={() => void handleRemove(item)}
              className="absolute top-1 right-1 rounded-full bg-black/60 text-white text-xs px-1.5 py-0.5"
            >
              ×
            </button>
          </div>
        ))}
      </div>

      <Dialog open={cropOpen} onOpenChange={setCropOpen}>
        <DialogContent className="max-w-sm w-full">
          <DialogHeader>
            <DialogTitle>Bild zuschneiden</DialogTitle>
          </DialogHeader>
          <div className="relative w-full h-80 bg-black/80 rounded-md overflow-hidden">
            {pendingImage && (
              <Cropper
                image={pendingImage.previewUrl}
                crop={crop}
                zoom={zoom}
                aspect={aspectRatio}
                onCropChange={setCrop}
                onZoomChange={setZoom}
                onCropComplete={onCropComplete}
              />
            )}
          </div>
          <div className="flex items-center justify-end gap-2 pt-2">
            <Button type="button" variant="outline" onClick={() => setCropOpen(false)}>
              Abbrechen
            </Button>
            <Button type="button" onClick={handleConfirmCrop}>
              Übernehmen
            </Button>
          </div>
        </DialogContent>
      </Dialog>
    </div>
  );
};

export default MediaUpload;
