'use client'

import { useState, useEffect, useRef, useCallback, memo, useMemo } from 'react';
import { Note } from '@/lib/types';
import { NoteCard } from './note-card';
import { NoteEditor } from './note-editor';
import { updateFullOrderWithoutRevalidation } from '@/app/actions/notes';
import { useResizeObserver } from '@/hooks/use-resize-observer';
import { useNotebookDrag } from '@/context/notebook-drag-context';
import { useLanguage } from '@/lib/i18n';
import { DEFAULT_LAYOUT, calculateColumns, calculateItemWidth, isMobileViewport } from '@/config/masonry-layout';
import './masonry-grid.css';

interface MasonryGridProps {
  notes: Note[];
  onEdit?: (note: Note, readOnly?: boolean) => void;
}

interface MasonryItemProps {
  note: Note;
  onEdit: (note: Note, readOnly?: boolean) => void;
  onResize: () => void;
  onDragStart?: (noteId: string) => void;
  onDragEnd?: () => void;
}

const MasonryItem = memo(function MasonryItem({ note, onEdit, onResize, onDragStart, onDragEnd }: MasonryItemProps) {
  const resizeRef = useResizeObserver(onResize);

  return (
    <div
      className="masonry-item absolute py-1"
      data-id={note.id}
      data-size={note.size}
      data-draggable="true"
      ref={resizeRef as any}
    >
      <div className="masonry-item-content relative">
        <NoteCard
          note={note}
          onEdit={onEdit}
          onDragStart={onDragStart}
          onDragEnd={onDragEnd}
        />
      </div>
    </div>
  );
}, (prev, next) => {
  // Custom comparison to avoid re-render on function prop changes if note data is same
  return prev.note.id === next.note.id; // Removed isDragging comparison
});

export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
  const { t } = useLanguage();
  const [editingNote, setEditingNote] = useState<{ note: Note; readOnly?: boolean } | null>(null);
  const { startDrag, endDrag, draggedNoteId } = useNotebookDrag();

  const lastDragEndTime = useRef<number>(0);

  const handleEdit = useCallback((note: Note, readOnly?: boolean) => {
    // Prevent opening note if it was just dragged (within 200ms)
    if (Date.now() - lastDragEndTime.current < 200) {
      return;
    }

    if (onEdit) {
      onEdit(note, readOnly);
    } else {
      setEditingNote({ note, readOnly });
    }
  }, [onEdit]);

  const pinnedGridRef = useRef<HTMLDivElement>(null);
  const othersGridRef = useRef<HTMLDivElement>(null);
  const pinnedMuuri = useRef<any>(null);
  const othersMuuri = useRef<any>(null);

  // Memoize filtered notes (order comes from array)
  const pinnedNotes = useMemo(
    () => notes.filter(n => n.isPinned),
    [notes]
  );
  const othersNotes = useMemo(
    () => notes.filter(n => !n.isPinned),
    [notes]
  );

  const handleDragEnd = useCallback(async (grid: any) => {
    // Record drag end time to prevent accidental clicks
    lastDragEndTime.current = Date.now();

    if (!grid) return;

    const items = grid.getItems();
    const ids = items
      .map((item: any) => item.getElement()?.getAttribute('data-id'))
      .filter((id: any): id is string => !!id);

    try {
      // Save order to database WITHOUT triggering a full page refresh
      // Muuri has already updated the visual layout
      await updateFullOrderWithoutRevalidation(ids);
    } catch (error) {
      console.error('Failed to persist order:', error);
    }
  }, []);

  const layoutTimeoutRef = useRef<NodeJS.Timeout>();

  const refreshLayout = useCallback((_?: any) => {
    if (layoutTimeoutRef.current) {
      clearTimeout(layoutTimeoutRef.current);
    }

    layoutTimeoutRef.current = setTimeout(() => {
      requestAnimationFrame(() => {
        if (pinnedMuuri.current) {
          pinnedMuuri.current.refreshItems().layout();
        }
        if (othersMuuri.current) {
          othersMuuri.current.refreshItems().layout();
        }
      });
    }, 100); // 100ms debounce
  }, []);

  // Ref for container to use with ResizeObserver
  const containerRef = useRef<HTMLDivElement>(null);

  // Centralized function to apply item dimensions based on container width
  const applyItemDimensions = useCallback((grid: any, containerWidth: number) => {
    if (!grid) return;

    const columns = calculateColumns(containerWidth);
    const itemWidth = calculateItemWidth(containerWidth, columns);

    const items = grid.getItems();
    items.forEach((item: any) => {
      const el = item.getElement();
      if (el) {
        el.style.width = `${itemWidth}px`;
        // Height is auto - determined by content (Google Keep style)
      }
    });
  }, []);

  useEffect(() => {
    let isMounted = true;
    let muuriInitialized = false;

    const initMuuri = async () => {
      // Prevent duplicate initialization
      if (muuriInitialized) return;
      muuriInitialized = true;

      // Import web-animations-js polyfill
      await import('web-animations-js');
      // Dynamic import of Muuri to avoid SSR window error
      const MuuriClass = (await import('muuri')).default;

      if (!isMounted) return;

      // Detect if we are on a touch device (mobile behavior)
      const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
      const isMobileWidth = window.innerWidth < 768;
      const isMobile = isTouchDevice || isMobileWidth;

      // Get container width for responsive calculation
      const containerWidth = window.innerWidth - 32; // Subtract padding
      const columns = calculateColumns(containerWidth);
      const itemWidth = calculateItemWidth(containerWidth, columns);

      console.log(`[Masonry] Container width: ${containerWidth}px, Columns: ${columns}, Item width: ${itemWidth}px`);

      // Calculate item dimensions based on note size
      const getItemDimensions = (note: Note) => {
        const baseWidth = itemWidth;
        let baseHeight = 200; // Default medium height

        switch (note.size) {
          case 'small':
            baseHeight = 150;
            break;
          case 'medium':
            baseHeight = 200;
            break;
          case 'large':
            baseHeight = 300;
            break;
          default:
            baseHeight = 200;
        }

        return { width: baseWidth, height: baseHeight };
      };

      const layoutOptions = {
        dragEnabled: true,
        // Use drag handle for mobile devices to allow smooth scrolling
        // On desktop, whole card is draggable (no handle needed)
        dragHandle: isMobile ? '.muuri-drag-handle' : undefined,
        // dragContainer: document.body, // REMOVED: Keep item in grid to prevent React conflict
        dragStartPredicate: {
          distance: 10,
          delay: 0,
        },
        dragPlaceholder: {
          enabled: true,
          createElement: (item: any) => {
            const el = item.getElement().cloneNode(true);
            el.style.opacity = '0.4';
            el.style.transform = 'scale(1.05)';
            el.style.boxShadow = '0 20px 40px rgba(0, 0, 0, 0.3)';
            el.classList.add('muuri-item-placeholder');
            return el;
          },
        },
        dragAutoScroll: {
          targets: [window],
          speed: (item: any, target: any, intersection: any) => {
            return intersection * 30; // Faster auto-scroll for better UX
          },
          threshold: 50, // Start auto-scroll earlier (50px from edge)
          smoothStop: true, // Smooth deceleration
        },
        // IMPROVED: Configuration for drag release handling
        dragRelease: {
          duration: 300,
          easing: 'cubic-bezier(0.25, 1, 0.5, 1)',
          useDragContainer: false, // REMOVED: Keep item in grid
        },
        dragCssProps: {
          touchAction: 'none',
          userSelect: 'none',
          userDrag: 'none',
          tapHighlightColor: 'rgba(0, 0, 0, 0)',
          touchCallout: 'none',
          contentZooming: 'none',
        },
        // CRITICAL: Grid layout configuration for fixed width items
        layoutDuration: 30, // Much faster layout for responsiveness
        layoutEasing: 'ease-out',
        // Use grid layout for better control over item placement
        layout: {
          // Enable true masonry layout - items can be different heights
          fillGaps: true,
          horizontal: false,
          alignRight: false,
          alignBottom: false,
          rounding: false,
          // Set fixed width and let height be determined by content
          // This creates a Google Keep-like masonry layout
          itemPositioning: {
            onLayout: true,
            onResize: true,
            onInit: true,
          },
        },
        dragSort: true,
        dragSortInterval: 50,
        // Enable drag and drop with proper drag handling
        dragSortHeuristics: {
          sortInterval: 0, // Zero interval for immediate sorting
          minDragDistance: 5,
          minBounceBackAngle: 1,
        },
        // Grid configuration for responsive columns
        visibleStyles: {
          opacity: '1',
          transform: 'scale(1)',
        },
        hiddenStyles: {
          opacity: '0',
          transform: 'scale(0.5)',
        },
      };

      // Initialize pinned grid
      if (pinnedGridRef.current && !pinnedMuuri.current) {
        // Set container width explicitly
        pinnedGridRef.current.style.width = '100%';
        pinnedGridRef.current.style.position = 'relative';

        // Get all items in the pinned grid and set their dimensions
        const pinnedItems = Array.from(pinnedGridRef.current.children);
        pinnedItems.forEach((item) => {
          const noteId = item.getAttribute('data-id');
          const note = pinnedNotes.find(n => n.id === noteId);
          if (note) {
            const dims = getItemDimensions(note);
            (item as HTMLElement).style.width = `${dims.width}px`;
            // Don't set height - let content determine it like Google Keep
          }
        });

        pinnedMuuri.current = new MuuriClass(pinnedGridRef.current, layoutOptions)
          .on('dragEnd', () => handleDragEnd(pinnedMuuri.current))
          .on('dragStart', () => {
            // Optional: visual feedback or state update
          });

        // Initial layout
        requestAnimationFrame(() => {
          pinnedMuuri.current?.refreshItems().layout();
        });
      }

      // Initialize others grid
      if (othersGridRef.current && !othersMuuri.current) {
        // Set container width explicitly
        othersGridRef.current.style.width = '100%';
        othersGridRef.current.style.position = 'relative';

        // Get all items in the others grid and set their dimensions
        const othersItems = Array.from(othersGridRef.current.children);
        othersItems.forEach((item) => {
          const noteId = item.getAttribute('data-id');
          const note = othersNotes.find(n => n.id === noteId);
          if (note) {
            const dims = getItemDimensions(note);
            (item as HTMLElement).style.width = `${dims.width}px`;
            // Don't set height - let content determine it like Google Keep
          }
        });

        othersMuuri.current = new MuuriClass(othersGridRef.current, layoutOptions)
          .on('dragEnd', () => handleDragEnd(othersMuuri.current));

        // Initial layout
        requestAnimationFrame(() => {
          othersMuuri.current?.refreshItems().layout();
        });
      }
    };

    initMuuri();

    return () => {
      isMounted = false;
      pinnedMuuri.current?.destroy();
      othersMuuri.current?.destroy();
      pinnedMuuri.current = null;
      othersMuuri.current = null;
    };
    // Only run once on mount
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Synchronize items when notes change (e.g. searching, adding, removing)
  useEffect(() => {
    const syncGridItems = (
      grid: any,
      gridRef: React.RefObject<HTMLDivElement | null>,
      notesArray: Note[]
    ) => {
      if (!grid || !gridRef.current) return;

      // Get container width for dimension calculation
      const containerWidth = containerRef.current?.getBoundingClientRect().width || window.innerWidth - 32;
      const columns = calculateColumns(containerWidth);
      const itemWidth = calculateItemWidth(containerWidth, columns);

      // Get current DOM elements and Muuri items
      const domElements = Array.from(gridRef.current.children) as HTMLElement[];
      const muuriItems = grid.getItems();
      const muuriElements = muuriItems.map((item: any) => item.getElement());

      // Find new elements to add (in DOM but not in Muuri)
      const newElements = domElements.filter(el => !muuriElements.includes(el));

      // Find removed items (in Muuri but not in DOM)
      const removedItems = muuriItems.filter((item: any) =>
        !domElements.includes(item.getElement())
      );

      // Remove old items from Muuri
      if (removedItems.length > 0) {
        console.log(`[Masonry Sync] Removing ${removedItems.length} items`);
        grid.remove(removedItems, { layout: false });
      }

      // Add new items to Muuri with correct width
      if (newElements.length > 0) {
        console.log(`[Masonry Sync] Adding ${newElements.length} new items`);
        newElements.forEach(el => {
          el.style.width = `${itemWidth}px`;
        });
        grid.add(newElements, { layout: false });
      }

      // Update all existing item widths
      domElements.forEach(el => {
        el.style.width = `${itemWidth}px`;
      });

      // Refresh and layout - CRITICAL: Always refresh items to catch content size changes
      // Use requestAnimationFrame to ensure DOM has painted 
      requestAnimationFrame(() => {
        grid.refreshItems().layout();
      });
    };

    // Use setTimeout to ensure React has finished rendering DOM elements
    const timeoutId = setTimeout(() => {
      syncGridItems(pinnedMuuri.current, pinnedGridRef, pinnedNotes);
      syncGridItems(othersMuuri.current, othersGridRef, othersNotes);
    }, 50); // Increased timeout slightly to ensure DOM stability

    return () => clearTimeout(timeoutId);
  }, [pinnedNotes, othersNotes]);

  // Handle container resize with ResizeObserver for responsive layout
  useEffect(() => {
    if (!containerRef.current) return;

    let resizeTimeout: NodeJS.Timeout;

    const handleResize = (entries: ResizeObserverEntry[]) => {
      clearTimeout(resizeTimeout);
      resizeTimeout = setTimeout(() => {
        const containerWidth = entries[0]?.contentRect.width || window.innerWidth - 32;
        const columns = calculateColumns(containerWidth);

        console.log(`[Masonry Resize] Width: ${containerWidth}px, Columns: ${columns}`);

        // Apply dimensions to both grids using centralized function
        applyItemDimensions(pinnedMuuri.current, containerWidth);
        applyItemDimensions(othersMuuri.current, containerWidth);

        // Refresh both grids with new layout
        requestAnimationFrame(() => {
          if (pinnedMuuri.current) {
            pinnedMuuri.current.refreshItems().layout();
          }
          if (othersMuuri.current) {
            othersMuuri.current.refreshItems().layout();
          }
        });
      }, 150);
    };

    const observer = new ResizeObserver(handleResize);
    observer.observe(containerRef.current);

    // Initial layout calculation
    if (containerRef.current) {
      const initialWidth = containerRef.current.getBoundingClientRect().width || window.innerWidth - 32;
      applyItemDimensions(pinnedMuuri.current, initialWidth);
      applyItemDimensions(othersMuuri.current, initialWidth);
      requestAnimationFrame(() => {
        pinnedMuuri.current?.refreshItems().layout();
        othersMuuri.current?.refreshItems().layout();
      });
    }

    return () => {
      clearTimeout(resizeTimeout);
      observer.disconnect();
    };
  }, [applyItemDimensions]);

  return (
    <div ref={containerRef} className="masonry-container">
      {pinnedNotes.length > 0 && (
        <div className="mb-8">
          <h2 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3 px-2">{t('notes.pinned')}</h2>
          <div ref={pinnedGridRef} className="relative min-h-[100px]">
            {pinnedNotes.map(note => (
              <MasonryItem
                key={note.id}
                note={note}
                onEdit={handleEdit}
                onResize={refreshLayout}
                onDragStart={startDrag}
                onDragEnd={endDrag}
              />
            ))}
          </div>
        </div>
      )}

      {othersNotes.length > 0 && (
        <div>
          {pinnedNotes.length > 0 && (
            <h2 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3 px-2">{t('notes.others')}</h2>
          )}
          <div ref={othersGridRef} className="relative min-h-[100px]">
            {othersNotes.map(note => (
              <MasonryItem
                key={note.id}
                note={note}
                onEdit={handleEdit}
                onResize={refreshLayout}
                onDragStart={startDrag}
                onDragEnd={endDrag}
              />
            ))}
          </div>
        </div>
      )}

      {editingNote && (
        <NoteEditor
          note={editingNote.note}
          readOnly={editingNote.readOnly}
          onClose={() => setEditingNote(null)}
        />
      )}

      <style jsx global>{`
        .masonry-container {
          width: 100%;
        }

        .masonry-item {
          display: block;
          position: absolute;
          z-index: 1;
          box-sizing: border-box;
        }
        .masonry-item.muuri-item-dragging {
          z-index: 3;
          opacity: 0.8;
        }
        .masonry-item.muuri-item-releasing {
          z-index: 2;
        }
        .masonry-item.muuri-item-hidden {
          z-index: 0;
        }
        .masonry-item-content {
          position: relative;
          width: 100%;
          height: 100%;
          box-sizing: border-box;
        }
        /* Ensure proper box-sizing for all elements in the grid */
        .masonry-item *,
        .masonry-item-content * {
          box-sizing: border-box;
        }
      `}</style>
    </div>
  );
}
