'use client';
import {
  Dropzone,
  DropZoneArea,
  DropzoneDescription,
  DropzoneFileListItem,
  DropzoneMessage,
  DropzoneRemoveFile,
  DropzoneTrigger,
  useDropzone
} from '@/components/ui/dropzone';
import { useGlobalContext } from '@/context/Provider';
import apis, { BUCKET_ROOT } from '@/utils/apis';
import { Trash2Icon, ImagePlus, Crop, ZoomIn, ZoomOut } from 'lucide-react';
import { useEffect, useState, useCallback, useRef } from 'react';
import Cropper from 'react-easy-crop';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';

// Helper to create a cropped image from canvas
async function getCroppedImg(imageSrc: string, pixelCrop: { x: number; y: number; width: number; height: number }): Promise<File> {
  const image = new Image();
  image.crossOrigin = 'anonymous';
  await new Promise<void>((resolve, reject) => {
    image.onload = () => resolve();
    image.onerror = reject;
    image.src = imageSrc;
  });

  const canvas = document.createElement('canvas');
  canvas.width = pixelCrop.width;
  canvas.height = pixelCrop.height;
  const ctx = canvas.getContext('2d')!;

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

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

// Helper to read file as data URL
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.readAsDataURL(file);
  });
}

export function MultiImages({ addImages, images, maxFiles = 5 }: { addImages: any, images?: any, maxFiles?: number }) {
  const { Toast } = useGlobalContext();
  const [uploadedImages, setUploadedImages] = useState<any[]>([]);
  const uploadedImagesRef = useRef<any[]>([]);

  // Crop state
  const [cropModalOpen, setCropModalOpen] = useState(false);
  const [imageToCrop, setImageToCrop] = useState<string | null>(null);
  const [currentFileName, setCurrentFileName] = useState<string>('');
  const [crop, setCrop] = useState({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const [croppedAreaPixels, setCroppedAreaPixels] = useState<any>(null);
  const [isCropping, setIsCropping] = useState(false);

  // Queue state for handling multiple files
  const [queueInfo, setQueueInfo] = useState({ current: 0, total: 0 });
  const pendingResolve = useRef<((file: File | null) => void) | null>(null);

  // Queue for sequential processing
  const fileQueueRef = useRef<File[]>([]);
  const isProcessingRef = useRef(false);
  const queueResolversRef = useRef<Map<string, (result: any) => void>>(new Map());
  const lastToastTimeRef = useRef<number>(0);

  const showLimitToastOnce = useCallback(() => {
    const now = Date.now();
    if (now - lastToastTimeRef.current > 1500) {
      lastToastTimeRef.current = now;
      Toast.error(`Maximum limit of ${maxFiles} images reached.`);
    }
  }, [maxFiles, Toast]);

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

  const resetCropState = () => {
    setCropModalOpen(false);
    setImageToCrop(null);
    setCurrentFileName('');
    setCrop({ x: 0, y: 0 });
    setZoom(1);
    setCroppedAreaPixels(null);
    pendingResolve.current = null;
  };

  const handleCropConfirm = async () => {
    if (!imageToCrop || !croppedAreaPixels) return;
    setIsCropping(true);
    try {
      const croppedFile = await getCroppedImg(imageToCrop, croppedAreaPixels);
      const resolver = pendingResolve.current;
      resetCropState();
      setIsCropping(false);
      resolver?.(croppedFile);
    } catch (e) {
      Toast.error('Failed to crop image');
      const resolver = pendingResolve.current;
      resetCropState();
      setIsCropping(false);
      resolver?.(null);
    }
  };

  const handleSkipCrop = () => {
    // Upload original file without cropping — need to get the original file
    // We stored it via the queue, so we resolve with null to signal "use original"
    const resolver = pendingResolve.current;
    resetCropState();
    resolver?.(null); // null = signal to use original file
  };

  // Show crop modal for a single file and return a promise that resolves with the cropped file
  const showCropForFile = (file: File, currentIndex: number, totalFiles: number): Promise<File> => {
    return new Promise(async (resolve) => {
      try {
        const dataUrl = await readFileAsDataURL(file);
        setImageToCrop(dataUrl);
        setCurrentFileName(file.name);
        setQueueInfo({ current: currentIndex, total: totalFiles });
        setCrop({ x: 0, y: 0 });
        setZoom(1);
        setCroppedAreaPixels(null);
        setCropModalOpen(true);

        pendingResolve.current = (croppedFile: File | null) => {
          // If croppedFile is null, use the original file
          resolve(croppedFile || file);
        };
      } catch {
        // If reading fails, use original file
        resolve(file);
      }
    });
  };

  // Process the queue of files one by one
  const processFileQueue = async () => {
    if (isProcessingRef.current) return;
    isProcessingRef.current = true;

    let currentBatchIndex = 0;
    const initialBatchTotal = fileQueueRef.current.length;

    while (fileQueueRef.current.length > 0) {
      if (uploadedImagesRef.current.length >= maxFiles) {
        showLimitToastOnce();
        const remaining = fileQueueRef.current.splice(0, fileQueueRef.current.length);
        remaining.forEach((file) => {
          const fileKey = file.name + file.size + file.lastModified;
          const resolver = queueResolversRef.current.get(fileKey);
          if (resolver) {
            resolver(null);
            queueResolversRef.current.delete(fileKey);
          }
        });
        break;
      }

      currentBatchIndex++;
      const file = fileQueueRef.current.shift()!;
      const fileKey = file.name + file.size + file.lastModified;

      const croppedFile = await showCropForFile(file, currentBatchIndex, Math.max(initialBatchTotal, currentBatchIndex));

      // Now upload the cropped/original file
      const uploadedImage = await handleUploadImage(croppedFile);
      if (uploadedImage) {
        setUploadedImages((prevImages) => {
          const validPrevImages = Array.isArray(prevImages) ? prevImages : [];
          const newImages = [...validPrevImages, uploadedImage];
          uploadedImagesRef.current = newImages;
          addImages(newImages);
          return newImages;
        });
      }

      // Resolve the dropzone's promise for this file
      const resolver = queueResolversRef.current.get(fileKey);
      if (resolver) {
        resolver(uploadedImage ? uploadedImage : null);
        queueResolversRef.current.delete(fileKey);
      }
    }

    isProcessingRef.current = false;
  };

  const dropzone = useDropzone<any, string>({
    onDropFile: async (file: File) => {
      return new Promise((resolve) => {
        const currentTotalCount = uploadedImagesRef.current.length + fileQueueRef.current.length;
        if (currentTotalCount >= maxFiles) {
          showLimitToastOnce();
          resolve({ status: "error", error: `Maximum limit of ${maxFiles} images reached.` });
          return;
        }

        // Add file to queue
        fileQueueRef.current.push(file);
        const fileKey = file.name + file.size + file.lastModified;
        queueResolversRef.current.set(fileKey, (result: any) => {
          if (result) {
            resolve({ status: "success", result: result.url });
          } else {
            resolve({ status: "error", error: "Upload failed" });
          }
        });

        // Start processing (will be no-op if already processing)
        // Use setTimeout to batch files that are added in the same tick
        setTimeout(() => processFileQueue(), 50);
      });
    },
    onRootError: (error) => {
      if (error) {
        Toast.error(error);
      }
    },
    validation: {
      accept: { "image/*": [".png", ".jpg", ".jpeg"] },
      maxSize: 10 * 1024 * 1024, // 10MB limit
    }
  });

  useEffect(() => {
    const imgs = Array.isArray(images) ? images : [];
    setUploadedImages(imgs);
    uploadedImagesRef.current = imgs;
  }, [images]);

  const handleUploadImage = async (file: File) => {
    try {
      const apiRes = await apis.Common.uploadFile("file", file);
      Toast.success("Image uploaded successfully!");

      return {
        id: apiRes?.key || file.name, // ✅ use key for payload
        url: apiRes?.key,             // key used in payload
        name: file.name,
        fullUrl: apiRes?.image_url    // only for preview
      };

    } catch (error) {
      Toast.error("Failed to upload image.");
      return null;
    }
  };

  const handleRemoveImage = (id: string) => {
    if (!id) {
      // Safety: if id is undefined/null, don't remove anything
      return;
    }
    const updatedImages = uploadedImages?.filter(img => img.id !== id);
    setUploadedImages(updatedImages);
    uploadedImagesRef.current = updatedImages;
    addImages(updatedImages);
    dropzone.clearFiles();
  };
  return (
    <div className="not-prose flex flex-col gap-4">
      <Dropzone {...dropzone}>
        <div>
          <div className="flex justify-between">
            <DropzoneDescription>
              Select at least 3 images (up to a maximum of {maxFiles}).
            </DropzoneDescription>
            <DropzoneMessage />
          </div>
          <DropZoneArea>
            {uploadedImages?.length > 0 ? (
              <div className='w-full flex flex-row justify-center items-center p-3 flex-wrap gap-2'>
                {uploadedImages.map((file: any, index: number) => (
                  <DropzoneFileListItem
                    className="w-[30%] sm:w-[23%] overflow-hidden rounded-md bg-secondary p-0 shadow-sm"
                    key={file.id || file.url || index}
                    file={file}
                  >
                    <div className="flex relative w-full flex-row h-[110px]">
                      <img
                        src={file.fullUrl ? file.fullUrl : BUCKET_ROOT + file.url}
                        alt={file.name}
                        className="aspect-video w-full h-full object-cover rounded-lg"
                      />
                      <DropzoneRemoveFile
                        className="absolute top-1.5 right-1.5 rounded-full !bg-white text-[var(--primary-brand)] hover:!bg-[var(--primary-brand)] hover:text-white border border-gray-100 shadow-sm transition-all duration-200 h-7 w-7 flex items-center justify-center"
                        onClick={() => handleRemoveImage(file.id)}
                      >
                        <Trash2Icon className="size-3.5" />
                      </DropzoneRemoveFile>
                    </div>
                  </DropzoneFileListItem>
                ))}
                {uploadedImages.length < maxFiles && (
                  <DropzoneTrigger className="w-[30%] sm:w-[23%] h-[110px] flex flex-col items-center justify-center bg-transparent content-center text-sm border-dotted border-[#CCCCCC] border-[2px] hover:border-[var(--primary-brand)] hover:bg-[var(--orange-100)]/30 hover:text-[var(--primary-brand)] transition-all duration-200 rounded-lg">
                    <div className="flex flex-col items-center gap-1">
                      <ImagePlus className="w-5 h-5 text-gray-500 group-hover:text-[var(--primary-brand)] transition-colors" />
                      <p className="font-semibold text-xs">Upload photo</p>
                    </div>
                  </DropzoneTrigger>
                )}
              </div>
            ) : (
              <DropzoneTrigger className="flex flex-col items-center gap-2 bg-transparent py-5 px-4 text-center text-sm hover:!bg-transparent">
                <div>
                  <p className="font-semibold bg-[var(--orange-100)] text-[var(--primary-brand)] hover:bg-[var(--primary-brand)] hover:text-white transition-all duration-200 px-5 py-2 rounded-[100px] cursor-pointer shadow-sm text-sm inline-block">
                    + Upload photo
                  </p>
                  <p className="text-[12px] text-[#999999] mt-1.5">Supported formats: .png, .jpg</p>
                </div>
              </DropzoneTrigger>
            )}

          </DropZoneArea>
        </div>


      </Dropzone>

      {/* Crop Modal */}
      <Dialog open={cropModalOpen} onOpenChange={(open) => {
        if (!open) handleSkipCrop();
      }}>
        <DialogContent className="sm:max-w-xl max-h-[90vh] flex flex-col p-0 rounded-2xl border-none shadow-2xl overflow-hidden">
          <DialogHeader className="px-6 pt-6 pb-2">
            <DialogTitle className="text-lg font-bold text-[#1C1D22] flex items-center gap-2">
              <Crop className="w-5 h-5 text-[var(--primary-brand)]" />
              <span>Crop Image</span>
              {queueInfo.total > 1 && (
                <span className="ml-auto text-sm font-medium text-gray-400">
                  {queueInfo.current} of {queueInfo.total}
                </span>
              )}
            </DialogTitle>
            {currentFileName && (
              <p className="text-xs text-gray-400 mt-1 truncate">{currentFileName}</p>
            )}
          </DialogHeader>

          <div className="relative w-full h-[350px] bg-gray-900">
            {imageToCrop && (
              <Cropper
                image={imageToCrop}
                crop={crop}
                zoom={zoom}
                aspect={4 / 3}
                onCropChange={setCrop}
                onZoomChange={setZoom}
                onCropComplete={onCropComplete}
              />
            )}
          </div>

          {/* Zoom Controls */}
          <div className="flex items-center justify-center gap-4 px-6 py-3 bg-gray-50">
            <button
              type="button"
              onClick={() => setZoom(z => Math.max(1, z - 0.1))}
              className="w-8 h-8 flex items-center justify-center rounded-full border border-gray-200 hover:bg-gray-100 transition-colors"
            >
              <ZoomOut className="w-4 h-4 text-gray-600" />
            </button>
            <input
              type="range"
              min={1}
              max={3}
              step={0.1}
              value={zoom}
              onChange={(e) => setZoom(Number(e.target.value))}
              className="w-48 h-1.5 accent-[var(--primary-brand)] cursor-pointer"
            />
            <button
              type="button"
              onClick={() => setZoom(z => Math.min(3, z + 0.1))}
              className="w-8 h-8 flex items-center justify-center rounded-full border border-gray-200 hover:bg-gray-100 transition-colors"
            >
              <ZoomIn className="w-4 h-4 text-gray-600" />
            </button>
            <span className="text-xs text-gray-500 font-medium min-w-[40px] text-center">{Math.round(zoom * 100)}%</span>
          </div>

          <DialogFooter className="flex flex-row justify-end gap-3 px-6 pb-6 pt-2">
            <Button
              type="button"
              variant="outline"
              className="h-10 px-6 rounded-xl border-gray-200 font-semibold"
              onClick={handleSkipCrop}
            >
              Skip Crop
            </Button>
            <Button
              type="button"
              variant="theme"
              className="h-10 px-6 rounded-xl font-semibold"
              onClick={handleCropConfirm}
              disabled={isCropping}
            >
              {isCropping ? 'Cropping...' : 'Crop & Upload'}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
