import React, { useState, useCallback, useMemo, useRef, useEffect, memo } from 'react';
import { useDebounce } from '../hooks/useDebounce';
import {
  Box,
  Flex,
  Text,
  Input,
  InputGroup,
  InputLeftElement,
  Checkbox,
  Button,
  HStack,
  Menu,
  MenuButton,
  MenuList,
  MenuItem,
  MenuDivider,
  IconButton,
  Spinner,

} from '@chakra-ui/react';
import { Search2Icon, ChevronDownIcon } from '@chakra-ui/icons';
import type { Song, PlaylistNode } from '../types/interfaces';
import { formatDuration, formatTotalDuration } from '../utils/formatters';

interface PaginatedSongListProps {
  songs: Song[];
  onAddToPlaylist: (songIds: string[], playlistName: string) => void;
  onRemoveFromPlaylist?: (songIds: string[]) => void;
  playlists: PlaylistNode[];
  onSongSelect: (song: Song) => void;
  selectedSongId: string | null;
  currentPlaylist: string | null;
  loading: boolean;
  hasMore: boolean;
  totalSongs: number;
  onLoadMore: () => void;
  onSearch: (query: string) => void;
  searchQuery: string;
  depth?: number;
}

// Memoized song item component to prevent unnecessary re-renders
const SongItem = memo<{
  song: Song;
  isSelected: boolean;
  isHighlighted: boolean;
  onSelect: (song: Song) => void;
  onToggleSelection: (songId: string) => void;
  showCheckbox: boolean;
}>(({ song, isSelected, isHighlighted, onSelect, onToggleSelection, showCheckbox }) => {
  const handleClick = useCallback(() => {
    onSelect(song);
  }, [onSelect, song]);

  const handleCheckboxClick = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    e.stopPropagation();
    onToggleSelection(song.id);
  }, [onToggleSelection, song.id]);

  return (
    <Flex
      key={song.id}
      p={3}
      borderBottom="1px"
      borderColor="gray.700"
      cursor="pointer"
      bg={isHighlighted ? "blue.900" : "transparent"}
      _hover={{ bg: isHighlighted ? "blue.800" : "gray.700" }}
      onClick={handleClick}
      transition="background-color 0.2s"
    >
      {showCheckbox && (
        <Checkbox
          isChecked={isSelected}
          onChange={handleCheckboxClick}
          mr={3}
          onClick={(e) => e.stopPropagation()}
        />
      )}
      <Box flex={1} minW={0}>
        <Text fontSize="sm" fontWeight="medium" color="white" noOfLines={1}>
          {song.title}
        </Text>
        <Text fontSize="xs" color="gray.400" noOfLines={1}>
          {song.artist}
        </Text>
      </Box>
      <Box textAlign="right" ml={2}>
        <Text fontSize="xs" color="gray.500">
          {formatDuration(song.totalTime || '')}
        </Text>
        <Text fontSize="xs" color="gray.600">
          {song.averageBpm} BPM
        </Text>
      </Box>
    </Flex>
  );
});

SongItem.displayName = 'SongItem';

export const PaginatedSongList: React.FC<PaginatedSongListProps> = memo(({
  songs,
  onAddToPlaylist,
  onRemoveFromPlaylist,
  playlists,
  onSongSelect,
  selectedSongId,
  currentPlaylist,
  loading,
  hasMore,
  totalSongs,
  onLoadMore,
  onSearch,
  searchQuery,
  depth = 0
}) => {
  const [selectedSongs, setSelectedSongs] = useState<Set<string>>(new Set());
  const [localSearchQuery, setLocalSearchQuery] = useState(searchQuery);
  const observerRef = useRef<IntersectionObserver | null>(null);
  const loadingRef = useRef<HTMLDivElement>(null);
  const scrollContainerRef = useRef<HTMLDivElement>(null);
  const isTriggeringRef = useRef(false);
  const timeoutRef = useRef<NodeJS.Timeout | null>(null);
  
  // Store current values in refs to avoid stale closures
  const hasMoreRef = useRef(hasMore);
  const loadingRef_state = useRef(loading);
  const onLoadMoreRef = useRef(onLoadMore);
  
  // Update refs when props change
  useEffect(() => {
    hasMoreRef.current = hasMore;
    loadingRef_state.current = loading;
    onLoadMoreRef.current = onLoadMore;
  }, [hasMore, loading, onLoadMore]);
  
  // Debounce search to prevent excessive API calls
  const debouncedSearchQuery = useDebounce(localSearchQuery, 300);

  // Memoized helper function to get all playlists (excluding folders) from the playlist tree
  const getAllPlaylists = useCallback((nodes: PlaylistNode[]): PlaylistNode[] => {
    let result: PlaylistNode[] = [];
    for (const node of nodes) {
      if (node.type === 'playlist') {
        result.push(node);
      } else if (node.type === 'folder' && node.children) {
        result = result.concat(getAllPlaylists(node.children));
      }
    }
    return result;
  }, []);

  // Memoized flattened list of all playlists
  const allPlaylists = useMemo(() => getAllPlaylists(playlists), [playlists, getAllPlaylists]);

  const toggleSelection = useCallback((songId: string) => {
    setSelectedSongs(prev => {
      const newSelection = new Set(prev);
      if (newSelection.has(songId)) {
        newSelection.delete(songId);
      } else {
        newSelection.add(songId);
      }
      return newSelection;
    });
  }, []);

  const toggleSelectAll = useCallback(() => {
    setSelectedSongs(prev => 
      prev.size === songs.length ? new Set() : new Set(songs.map(s => s.id))
    );
  }, [songs]);

  const handleBulkAddToPlaylist = useCallback((playlistName: string) => {
    if (selectedSongs.size > 0) {
      onAddToPlaylist(Array.from(selectedSongs), playlistName);
      setSelectedSongs(new Set()); // Clear selection after action
    }
  }, [selectedSongs, onAddToPlaylist]);

  const handleBulkRemoveFromPlaylist = useCallback(() => {
    if (selectedSongs.size > 0 && onRemoveFromPlaylist) {
      onRemoveFromPlaylist(Array.from(selectedSongs));
      setSelectedSongs(new Set()); // Clear selection after action
    }
  }, [selectedSongs, onRemoveFromPlaylist]);

  // Memoized song selection handler
  const handleSongSelect = useCallback((song: Song) => {
    onSongSelect(song);
  }, [onSongSelect]);

  // Memoized search handler with debouncing
  const handleSearch = useCallback((query: string) => {
    setLocalSearchQuery(query);
    onSearch(query);
  }, [onSearch]);

  // Memoized song items to prevent unnecessary re-renders
  const songItems = useMemo(() => {
    return songs.map(song => (
      <SongItem
        key={song.id}
        song={song}
        isSelected={selectedSongs.has(song.id)}
        isHighlighted={selectedSongId === song.id}
        onSelect={handleSongSelect}
        onToggleSelection={toggleSelection}
        showCheckbox={selectedSongs.size > 0 || depth === 0}
      />
    ));
  }, [songs, selectedSongs, selectedSongId, handleSongSelect, toggleSelection, depth]);

  // Memoized total duration calculation
  const totalDuration = useMemo(() => {
    const totalSeconds = songs.reduce((total, song) => {
      if (!song.totalTime) return total;
      const seconds = Math.floor(Number(song.totalTime) / (song.totalTime.length > 4 ? 1000 : 1));
      return total + seconds;
    }, 0);
    return formatTotalDuration(totalSeconds);
  }, [songs]);

  // Memoized playlist options for bulk actions
  const playlistOptions = useMemo(() => {
    return allPlaylists.map(playlist => (
      <MenuItem key={playlist.id} onClick={() => handleBulkAddToPlaylist(playlist.name)}>
        {playlist.name}
      </MenuItem>
    ));
  }, [allPlaylists, handleBulkAddToPlaylist]);

  // Handle debounced search
  useEffect(() => {
    if (debouncedSearchQuery !== searchQuery) {
      onSearch(debouncedSearchQuery);
    }
  }, [debouncedSearchQuery, searchQuery, onSearch]);

  // Intersection Observer for infinite scroll
  useEffect(() => {
    if (loadingRef.current) {
      observerRef.current = new IntersectionObserver(
        (entries) => {
          // Use current values from refs to avoid stale closure
          if (entries[0].isIntersecting && hasMoreRef.current && !loadingRef_state.current && !isTriggeringRef.current) {
            isTriggeringRef.current = true;
            onLoadMoreRef.current();
            // Reset the flag after a short delay to prevent multiple triggers
            timeoutRef.current = setTimeout(() => {
              isTriggeringRef.current = false;
            }, 1000);
          }
        },
        { 
          threshold: 0.1,
          rootMargin: '100px' // Start loading when 100px away from the bottom
        }
      );

      observerRef.current.observe(loadingRef.current);
    }

    return () => {
      if (observerRef.current) {
        observerRef.current.disconnect();
      }
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, []); // Remove dependencies to prevent recreation

  return (
    <Flex direction="column" height="100%">
      {/* Sticky Header */}
      <Box
        position="sticky"
        top={0}
        bg="gray.900"
        zIndex={1}
        pb={4}
      >
        {/* Search Bar */}
        <InputGroup mb={4}>
          <InputLeftElement pointerEvents="none">
            <Search2Icon color="gray.300" />
          </InputLeftElement>
          <Input
            placeholder="Search songs by title, artist, album, or genre..."
            value={localSearchQuery}
            onChange={(e: React.ChangeEvent<HTMLInputElement>) => setLocalSearchQuery(e.target.value)}
            bg="gray.800"
            borderColor="gray.600"
            _hover={{ borderColor: "gray.500" }}
            _focus={{ borderColor: "blue.300", boxShadow: "0 0 0 1px var(--chakra-colors-blue-300)" }}
          />
        </InputGroup>

        {/* Bulk Actions Toolbar */}
        <Flex justify="space-between" align="center" p={2} bg="gray.800" borderRadius="md">
          <HStack spacing={4}>
            <Checkbox
              isChecked={selectedSongs.size === songs.length && songs.length > 0}
              isIndeterminate={selectedSongs.size > 0 && selectedSongs.size < songs.length}
              onChange={toggleSelectAll}
              colorScheme="blue"
              sx={{
                '& > span:first-of-type': {
                  opacity: 1,
                  border: '2px solid',
                  borderColor: 'gray.500'
                }
              }}
            >
              {selectedSongs.size === 0 
                ? "Select All" 
                : `Selected ${selectedSongs.size} song${selectedSongs.size === 1 ? '' : 's'}`}
            </Checkbox>
            <Text color="gray.400" fontSize="sm">
              {songs.length} of {totalSongs} songs • {totalDuration}
              {hasMore && songs.length > 0 && (
                <Text as="span" color="blue.400" ml={2}>
                  • Scroll for more
                </Text>
              )}
            </Text>
          </HStack>
          
          {selectedSongs.size > 0 && (
            <Menu>
              <MenuButton
                as={Button}
                rightIcon={<ChevronDownIcon />}
                size="sm"
                colorScheme="blue"
              >
                Actions
              </MenuButton>
              <MenuList>
                {allPlaylists.map((playlist) => (
                  <MenuItem
                    key={playlist.id}
                    onClick={() => {
                      handleBulkAddToPlaylist(playlist.name);
                    }}
                  >
                    Add to {playlist.name}
                  </MenuItem>
                ))}
                {currentPlaylist !== "All Songs" && onRemoveFromPlaylist && (
                  <>
                    <MenuDivider />
                    <MenuItem
                      color="red.300"
                      onClick={() => {
                        handleBulkRemoveFromPlaylist();
                      }}
                    >
                      Remove from {currentPlaylist}
                    </MenuItem>
                  </>
                )}
              </MenuList>
            </Menu>
          )}
        </Flex>
      </Box>

      {/* Scrollable Song List */}
      <Box 
        ref={scrollContainerRef}
        flex={1} 
        overflowY="auto" 
        mt={2} 
        id="song-list-container"
      >
        <Flex direction="column" gap={2}>
          {songItems}
          
          {/* Loading indicator for infinite scroll */}
          {loading && (
            <Flex justify="center" align="center" p={6} key="loading-spinner" gap={3}>
              <Spinner size="md" color="blue.400" />
              <Text color="gray.400" fontSize="sm">
                Loading more songs...
              </Text>
            </Flex>
          )}
          
          {/* Intersection observer target */}
          <div ref={loadingRef} style={{ height: '20px' }} key="intersection-target" />
          
          {/* Loading more indicator (subtle) */}
          {!loading && hasMore && songs.length > 0 && (
            <Flex justify="center" p={3} key="loading-more-indicator">
              <Text color="gray.600" fontSize="xs">
                Scroll for more songs
              </Text>
            </Flex>
          )}
          
          {/* End of results message */}
          {!hasMore && songs.length > 0 && (
            <Flex justify="center" p={4} key="end-message">
              <Text color="gray.500" fontSize="sm">
                No more songs to load
              </Text>
            </Flex>
          )}
          
          {/* No results message */}
          {!loading && songs.length === 0 && (
            <Flex justify="center" p={8} key="no-results">
              <Text color="gray.500">
                {searchQuery ? 'No songs found matching your search' : 'No songs available'}
              </Text>
            </Flex>
          )}
        </Flex>
      </Box>
    </Flex>
  );
}); 