import React, { useEffect, useState, useRef, useCallback } from 'react';
import { observer } from 'mobx-react-lite';
import type { StoreType } from 'polotno/model/store';
import { getCrop } from 'polotno/utils/image';

// redux
import { useDispatch } from 'react-redux';
import { AppDispatch } from '../../../../redux/store';

// Utils
import { getPublicApiKey, getType } from '../../../../utils/helper';
import { allowedImageTypes } from '../../../../utils/constants';

// Actions
import { success } from '../../../../redux/actions/snackbarActions';

// Components
import DialogV2 from '../../../../components/GenericUIBlocks/Dialog/V2';

// Icons
// @ts-ignore
import Upload from '../../../../assets/images/templates/upload-image.svg';
// @ts-ignore
import Trash from '../../../../assets/images/templates/trash-upload.svg';
// @ts-ignore
import Download from '../../../../assets/images/templates/download.svg';
// @ts-ignore
import ConfirmCloseIcon from '../../../../assets/images/modal-icons/confirm-close-icon';

// Styles
import './styles.scss';

interface BrandingImage {
  id: string | number;
  url: string;
  type: string;
  name?: string;
  meta?: any;
}

interface CustomUploadsV2Props {
  store: StoreType;
  onGetBrandingImages?: (payload: any) => Promise<any>;
  onDeleteBrandingImage?: (id: string | number) => Promise<void>;
  onUploadBrandingImage?: (payload: any) => Promise<any>;
}

const cancelDialogStylesV2 = {
  maxWidth: '567px',
  minHeight: 'auto',
  padding: '40px',
};

export const CustomUploadsV2 = observer(({
  store,
  onGetBrandingImages,
  onDeleteBrandingImage,
  onUploadBrandingImage,
}: CustomUploadsV2Props) => {
  const [images, setImages] = useState<BrandingImage[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [isUploading, setIsUploading] = useState(false);
  const [isLoadingMore, setIsLoadingMore] = useState(false);
  const [currentPage, setCurrentPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const [activeDropdown, setActiveDropdown] = useState<string | number | null>(null);
  const [imageCache, setImageCache] = useState<Map<string, string>>(new Map());
  const [openDeleteImage, setOpenDeleteImage] = useState(false);
  const [selectedImage, setSelectedImage] = useState<BrandingImage | null>(null);

  const fileInputRef = useRef<HTMLInputElement>(null);
  const dropdownRefs = useRef<Map<string | number, HTMLDivElement>>(new Map());
  const imagesGridRef = useRef<HTMLDivElement>(null);
  const loadingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const isLoadingRef = useRef(false);
  const dispatch: AppDispatch = useDispatch();

  const loadBrandingImages = useCallback(async (page: number = 1, append: boolean = false, loading = true) => {
    if (!onGetBrandingImages) return;

    // Prevent multiple simultaneous calls
    if (isLoadingRef.current) return;
    isLoadingRef.current = true;

    if (page === 1 && loading) {
      setIsLoading(true);
    } else {
      setIsLoadingMore(true);
    }

    try {
      const payload = {
        page,
        pageSize: 15
      };

      const brandingImages = await onGetBrandingImages(payload);

      if (brandingImages && Array.isArray(brandingImages.rows)) {
        const normalized = brandingImages.rows.map((img: any) => ({
          id: img.id,
          url: img.fileUrl,
          type: img.type.toLowerCase(), // Convert to lowercase for consistency
          name: img.name,
          meta: img.meta
        })) as BrandingImage[];

        if (append && page > 1) {
          // Merge with existing images, avoiding duplicates
          setImages(prevImages => {
            const existingIds = new Set(prevImages.map(img => img.id));
            const newImages = normalized.filter(img => !existingIds.has(img.id));
            return [...prevImages, ...newImages];
          });
        } else {
          // Replace images for initial load
          setImages(normalized);
        }

        // Update pagination state using API response pagination info
        const hasMorePages = brandingImages.currentPage < brandingImages.lastPage;
        setHasMore(hasMorePages);
        setCurrentPage(brandingImages.currentPage);
      } else {
        if (!append) {
          setImages([]);
        }
        setHasMore(false);
      }
    } catch (error) {
      console.error('Failed to load branding images:', error);
    } finally {
      setIsLoading(false);
      setIsLoadingMore(false);
      isLoadingRef.current = false;
    }
  }, [onGetBrandingImages]);

  // Handle loading more images on scroll
  const loadMoreImages = useCallback(async () => {
    if (!hasMore || isLoadingMore || isLoading) return;
    await loadBrandingImages(currentPage + 1, true);
  }, [hasMore, isLoadingMore, isLoading, currentPage, loadBrandingImages]);

  // Debounced scroll handler
  const handleScroll = useCallback(() => {
    if (!imagesGridRef.current || !hasMore || isLoadingMore || isLoading) return;

    const { scrollTop, scrollHeight, clientHeight } = imagesGridRef.current;
    const scrollPercentage = (scrollTop + clientHeight) / scrollHeight;

    // Load more when scrolled 80% of the way down
    if (scrollPercentage > 0.75) {
      // Clear existing timeout
      if (loadingTimeoutRef.current) {
        clearTimeout(loadingTimeoutRef.current);
      }

      // Debounce the API call by 300ms
      loadingTimeoutRef.current = setTimeout(() => {
        loadMoreImages();
      }, 300);
    }
  }, [hasMore, isLoadingMore, isLoading, loadMoreImages]);

  const handleOpenDeleteImage = () => {
    setOpenDeleteImage(true);
    setActiveDropdown(null);
  };

  const handleCloseDeleteImage = () => {
    setOpenDeleteImage(false);
  };

  // Handle file validation
  const validateFile = (file: File): boolean => {
    if (!file || !allowedImageTypes.includes(file.type)) {
      console.log('Only image files with extensions JPEG, PNG, or SVG are allowed.');
      return false;
    }
    if (file.size >= 5 * 1024 * 1024) { // 5MB limit
      console.log('File size must be under 5MB.');
      return false;
    }
    return true;
  };

  const getImageDimensions = (file: File): Promise<{ width: number; height: number }> => {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
  
      reader.onload = (e) => {
        const img = new Image();
        img.onload = () => {
          resolve({ width: img.width, height: img.height });
        };
        img.onerror = reject;
        img.src = e.target?.result as string;
      };
  
      reader.onerror = reject;
      reader.readAsDataURL(file);
    });
  };

  // Handle file upload
  const handleFileUpload = async (files: FileList) => {
    if (!files.length) return;

    setIsUploading(true);
    try {
      for (const file of Array.from(files)) {
        if (!validateFile(file)) {
          continue;
        }

        if (onUploadBrandingImage) {
          const formData = new FormData();
          let { width, height } = await getImageDimensions(file);
          const metaData = {
            width, height
          }
          formData.append('image', file);
          formData.append('meta_data', JSON.stringify(metaData));
          await onUploadBrandingImage(formData);
          // Reset pagination and reload from first page
          setCurrentPage(1);
          setHasMore(true);
          await loadBrandingImages(1, false, false);
        } else {
          const localUrl = URL.createObjectURL(file);
          const type = getType(file);

          const tempImage: BrandingImage = {
            id: `temp-${Date.now()}-${Math.random()}`,
            url: localUrl,
            type,
            name: file.name,
          };

          setImages((prev) => [...prev, tempImage]);

          // Cache the image URL
          setImageCache(
            (prev) => new Map(prev.set(tempImage.url, tempImage.url))
          );
        }
      }
    } catch (error) {
      console.error('Upload failed:', error);
    } finally {
      setIsUploading(false);
      if (fileInputRef.current) {
        fileInputRef.current.value = '';
      }
    }
  };

  // Handle delete image
  const handleDeleteImage = async (imageId: string | number) => {
    if (!onDeleteBrandingImage) return;

    try {
      await onDeleteBrandingImage(imageId);
      setImages(prev => prev.filter(img => img.id !== imageId));
      setActiveDropdown(null);

      // Clean up cache
      const imageToDelete = images.find(img => img.id === imageId);
      if (imageToDelete?.url.startsWith('blob:')) {
        URL.revokeObjectURL(imageToDelete.url);
      }
      setImageCache(prev => {
        const newCache = new Map(prev);
        newCache.delete(imageToDelete?.url || '');
        return newCache;
      });
    } catch (error) {
      console.error('Failed to delete image:', error);
    }
  };

  // Handle download image
  const handleDownloadImage = async (image: BrandingImage) => {
    try {
      const response = await fetch(image.url, {
        method: 'GET',
        headers: {
          Authorization: `Bearer ${getPublicApiKey()}`,
        },
      });

      if (!response.ok) throw new Error('Failed to download image');

      // Convert response to Blob
      const blob = await response.blob();

      // Create a temporary object URL for download
      const url = window.URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.download = image.name || `branding-image-${image.id}.png`;
      document.body.appendChild(link);
      link.click();

      // Clean up
      document.body.removeChild(link);
      window.URL.revokeObjectURL(url);
      setActiveDropdown(null);
    } catch (error) {
      console.error('Download failed:', error);
    }

  };

  // Handle image selection for canvas
  const handleImageSelect = async (image: BrandingImage, pos?: { x: number; y: number }, element?: any) => {
    const imageUrl = image.url;
    const type = image.type;

    const imageMetaData = typeof image?.meta?.data === 'string' ? JSON.parse(image?.meta?.data) : image?.meta?.data || {};
    let width = imageMetaData?.width || 100;
    let height = imageMetaData?.height || 100;

    if (element && element.type === 'svg' && element.contentEditable && type === 'image') {
      element.set({ maskSrc: imageUrl });
      return;
    }

    if (element && element.type === 'image' && element.contentEditable && type === 'image') {
      const crop = getCrop(element, { width, height });
      element.set({ src: imageUrl, ...crop });
      return;
    }

    const scale = Math.min(store.width / width, store.height / height, 1);
    width = width * scale;
    height = height * scale;

    const x = (pos?.x || store.width / 2) - width / 2;
    const y = (pos?.y || store.height / 2) - height / 2;

    store.activePage?.addElement({
      type: type === 'svg' ? type : 'image',
      //@ts-ignore
      src: imageUrl,
      x,
      y,
      width,
      height,
    });
  };

  // Handle dropdown toggle
  const toggleDropdown = (imageId: string | number, event: React.MouseEvent) => {
    event.stopPropagation();
    setActiveDropdown(activeDropdown === imageId ? null : imageId);
  };

  // Close dropdown when clicking outside
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (activeDropdown) {
        const dropdownElement = dropdownRefs.current.get(activeDropdown);
        if (dropdownElement && !dropdownElement.contains(event.target as Node)) {
          setActiveDropdown(null);
        }
      }
    };

    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, [activeDropdown]);

  // Load images on mount
  useEffect(() => {
    loadBrandingImages(1, false);
  }, []);

  // Setup scroll event listener
  useEffect(() => {
    const gridElement = imagesGridRef.current;
    if (gridElement) {
      gridElement.addEventListener('scroll', handleScroll);
      return () => {
        gridElement.removeEventListener('scroll', handleScroll);
        // Clean up timeout on unmount
        if (loadingTimeoutRef.current) {
          clearTimeout(loadingTimeoutRef.current);
        }
      };
    }
  }, [hasMore, isLoadingMore, isLoading, currentPage, handleScroll]);

  // Cleanup blob URLs on unmount
  useEffect(() => {
    return () => {
      images.forEach(image => {
        if (image.url.startsWith('blob:')) {
          URL.revokeObjectURL(image.url);
        }
      });
    };
  }, []);

  return (
    <div className="custom-uploads-v2">

      {/* Upload Section */}
      <div className="upload-section">
        <input
          ref={fileInputRef}
          type="file"
          accept=".jpg,.jpeg,.png,.svg"
          multiple
          className="file-input"
          onChange={(e) => e.target.files && handleFileUpload(e.target.files)}
        />

        <button
          className={`upload-button ${isUploading ? 'uploading' : ''}`}
          onClick={() => fileInputRef.current?.click()}
          disabled={isUploading}
        >
          <img src={Upload} alt="upload" />
          <span className="button-text">
            {isUploading ? 'Uploading...' : 'Upload Image'}
          </span>
        </button>
      </div>

      {/* Header */}
      <div className="upload-header">
        <div className="upload-constraints">
          <div className="format-text">
            Accepted File Formats: JPEG, PNG, SVG
          </div>
          <div className="size-text">
            Max Size: 5MB
          </div>
          <div className="quality-note">
            Use high-res images for best print quality.
          </div>
        </div>
      </div>

      {/* Images Grid */}
      <div className="images-grid" ref={imagesGridRef}>
        {isLoading ? (
          <div className="loading-state">Loading images...</div>
        ) : images.length === 0 ? (
          <div className="empty-state">No images uploaded yet</div>
        ) : (
          <>
            {images.map((image) => (
              <div key={image.id} className="image-item">
                <div
                  className="image-wrapper"
                  onClick={() => handleImageSelect(image)}
                >
                  <img
                    src={image.url}
                    alt={image.name || 'Branding image'}
                    className="image-preview"
                    loading="lazy"
                  />
                  <div
                    className="image-menu"
                    onClick={(e) => toggleDropdown(image.id, e)}
                  >
                    <div className="menu-dots">
                      <span></span>
                      <span></span>
                      <span></span>
                    </div>
                  </div>
                </div>

                {/* Dropdown Menu */}
                {activeDropdown === image.id && (
                  <div
                    ref={(el) => {
                      if (el) dropdownRefs.current.set(image.id, el);
                    }}
                    className="dropdown-menu"
                  >
                    {onDeleteBrandingImage && <button
                      className="dropdown-item"
                      onClick={() => {
                        setSelectedImage(image);
                        handleOpenDeleteImage();
                      }}
                      disabled={!onDeleteBrandingImage}
                    >
                      <img src={Trash} alt="trash" />
                      Delete
                    </button>}
                    <button
                      className="dropdown-item"
                      onClick={() => handleDownloadImage(image)}
                    >
                      <img src={Download} alt="download" />
                      Download
                    </button>
                  </div>
                )}
              </div>
            ))
            }

            {/* Loading indicator for pagination */}
            {isLoadingMore && (
              <div className="loading-more">
                <div className="loading-state">Loading more images...</div>
              </div>
            )}
          </>
        )}

        <DialogV2
          icon={<ConfirmCloseIcon fill='var(--primary-color)' />}
          customStyles={cancelDialogStylesV2}
          open={openDeleteImage}
          handleClose={handleCloseDeleteImage}
          title='Delete Image'
          subHeading=''
          description='Are you sure you want to delete this image?'
          onSubmit={() => {
            handleDeleteImage(selectedImage?.id as string | number);
            handleCloseDeleteImage();
          }}
          onCancel={handleCloseDeleteImage}
          cancelText='No'
          submitText='Yes'
          isGallery={false}
        />
      </div>
    </div>
  );
});

export default CustomUploadsV2;
