<script lang="ts">
  import { nostrClient } from '../../services/nostr/nostr-client.js';
  import { relayManager } from '../../services/nostr/relay-manager.js';
  import { config } from '../../services/nostr/config.js';
  import FeedPost from './FeedPost.svelte';
  import type { NostrEvent } from '../../types/nostr.js';
  import { onMount } from 'svelte';
  import { KIND, getFeedKinds, getKindInfo } from '../../types/kind-lookup.js';
  import { getRecentFeedEvents, getCachedReactionsForEvents } from '../../services/cache/event-cache.js';
  import { keyboardShortcuts } from '../../services/keyboard-shortcuts.js';
  import { browser } from '$app/environment';
  import { page } from '$app/stores';
  import Pagination from '../../components/ui/Pagination.svelte';
  import { getPaginatedItems, getCurrentPage, ITEMS_PER_PAGE } from '../../utils/pagination.js';
  import { isReply } from '../../utils/event-utils.js';
  import { filterEvents } from '../../services/event-filter.js';

  interface Props {
    singleRelay?: string;
    showOnlyOPs?: boolean;
  }

  let { singleRelay, showOnlyOPs = false }: Props = $props();
  
  // Expose API for parent component via component reference
  // Note: The warning about loadOlderEvents is a false positive - functions don't need to be reactive
  export { loadOlderEvents, loadingMore, hasMoreEvents, waitingRoomEvents, loadWaitingRoomEvents };
  
  // Refresh function for parent component
  async function refresh() {
    if (!isMounted) return;
    allEvents = [];
    oldestTimestamp = null;
    waitingRoomEvents = [];
    await loadFeed();
  }
  export { refresh };

  // Core state
  let allEvents = $state<NostrEvent[]>([]);
  let loading = $state(true);
  let relayError = $state<string | null>(null);
  
  // Waiting room for new events
  let waitingRoomEvents = $state<NostrEvent[]>([]);
  
  // Pagination
  let oldestTimestamp = $state<number | null>(null);
  let loadingMore = $state(false);
  let hasMoreEvents = $state(true);
  
  // Subscription
  let subscriptionId: string | null = $state(null);
  let isMounted = $state(true);
  let initialLoadComplete = $state(false);
  let loadInProgress = $state(false);
  
  // Preloaded referenced events (e, a, q tags) - eventId -> referenced event
  let preloadedReferencedEvents = $state<Map<string, NostrEvent>>(new Map());
  
  // Preloaded reactions for kind 11 events - eventId -> reactions[]
  let preloadedReactionsMap = $state<Map<string, NostrEvent[]>>(new Map());
  
  // Virtual scrolling for performance
  let Virtualizer: any = $state(null);
  let virtualizerLoading = $state(false);
  let virtualizerContainer = $state<HTMLElement | null>(null);
  
  async function loadVirtualizer() {
    if (Virtualizer) return Virtualizer;
    if (virtualizerLoading) return null;
    
    virtualizerLoading = true;
    try {
      const module = await import('@tanstack/svelte-virtual');
      // Ensure we're getting the component, not a class constructor
      if (module && module.Virtualizer && typeof module.Virtualizer === 'function') {
        Virtualizer = module.Virtualizer;
        return Virtualizer;
      }
      return null;
    } catch (error) {
      console.warn('Virtual scrolling initialization failed:', error);
      return null;
    } finally {
      virtualizerLoading = false;
    }
  }
  
  // Filter events based on showOnlyOPs and mute list
  let events = $derived(
    filterEvents(
      showOnlyOPs 
        ? allEvents.filter(event => !isReply(event))
        : allEvents
    )
  );
  
  // Pagination
  let currentPage = $derived(getCurrentPage($page.url.searchParams));
  let paginatedEvents = $derived(
    events.length > ITEMS_PER_PAGE 
      ? getPaginatedItems(events, currentPage, ITEMS_PER_PAGE)
      : events
  );
  
  // Get preloaded referenced event for a post (from e, a, or q tag)
  function getReferencedEventForPost(event: NostrEvent): NostrEvent | null {
    // Check q tag first
    const qTag = event.tags.find(t => t[0] === 'q' && t[1]);
    if (qTag && qTag[1]) {
      return preloadedReferencedEvents.get(qTag[1]) || null;
    }
    
    // Check e tag
    const eTag = event.tags.find(t => t[0] === 'e' && t[1] && t[1] !== event.id);
    if (eTag && eTag[1]) {
      return preloadedReferencedEvents.get(eTag[1]) || null;
    }
    
    // Check a tag - need to match by kind+pubkey+d-tag
    const aTag = event.tags.find(t => t[0] === 'a' && t[1]);
    if (aTag && aTag[1]) {
      const parts = aTag[1].split(':');
      if (parts.length >= 2) {
        const kind = parseInt(parts[0]);
        const pubkey = parts[1];
        const dTag = parts[2] || '';
        
        // Find matching event in preloaded events
        for (const [eventId, refEvent] of preloadedReferencedEvents.entries()) {
          if (refEvent.kind === kind && refEvent.pubkey === pubkey) {
            if (dTag) {
              const eventDTag = refEvent.tags.find(t => t[0] === 'd' && t[1]);
              if (eventDTag && eventDTag[1] === dTag) {
                return refEvent;
              }
            } else {
              // No d-tag, just match kind and pubkey
              return refEvent;
            }
          }
        }
      }
    }
    
    return null;
  }
      
  // Load waiting room events into feed
  function loadWaitingRoomEvents() {
    if (waitingRoomEvents.length === 0) return;
    
    const eventMap = new Map(allEvents.map((e: NostrEvent) => [e.id, e]));
    for (const event of waitingRoomEvents) {
      eventMap.set(event.id, event);
    }
    
    allEvents = Array.from(eventMap.values()).sort((a: NostrEvent, b: NostrEvent) => b.created_at - a.created_at);
    waitingRoomEvents = [];
  }

  // Load older events (pagination)
  // svelte-ignore non_reactive_update
  async function loadOlderEvents() {
    if (loadingMore || !hasMoreEvents) return;
    
    const untilTimestamp = oldestTimestamp ?? Math.floor(Date.now() / 1000);
    if (!untilTimestamp) return;
    
    loadingMore = true;
    try {
      const relays = singleRelay ? [singleRelay] : relayManager.getFeedReadRelays();
      const feedKinds = getFeedKinds().filter(k => k !== KIND.DISCUSSION_THREAD);
      const filters = feedKinds.map(k => ({ 
        kinds: [k], 
        limit: config.feedLimit,
        until: untilTimestamp - 1
      }));
      
      const fetched = await nostrClient.fetchEvents(
        filters,
        relays,
        {
          useCache: 'relay-first',
          cacheResults: true,
          timeout: config.standardTimeout
        }
      );
      
      if (!isMounted) return;
      
      // Filter by kind and mute list
      const filtered = filterEvents(
        fetched.filter(e => 
          e.kind !== KIND.DISCUSSION_THREAD && 
          getKindInfo(e.kind).showInFeed === true
        )
      );
      
      if (filtered.length === 0) {
        hasMoreEvents = false;
        return;
      }
      
      const eventMap = new Map(allEvents.map((e: NostrEvent) => [e.id, e]));
      for (const event of filtered) {
        eventMap.set(event.id, event);
        }
      
      const sorted = Array.from(eventMap.values()).sort((a: NostrEvent, b: NostrEvent) => b.created_at - a.created_at);
      allEvents = sorted;
      oldestTimestamp = Math.min(...sorted.map((e: NostrEvent) => e.created_at));
      
      // Batch fetch referenced events for paginated events too
      await batchFetchReferencedEvents(filtered);
    } catch (error) {
      // Failed to load older events
    } finally {
      loadingMore = false;
    }
  }

  // Initial feed load
  async function loadFeed() {
    if (!isMounted || loadInProgress) return;
    
    loadInProgress = true;
    loading = true;
    relayError = null;
    
    try {
      // Load from cache first (fast - instant display)
      if (!singleRelay) {
        const feedKinds = getFeedKinds().filter(k => k !== KIND.DISCUSSION_THREAD);
        const cached = await getRecentFeedEvents(feedKinds, 60 * 60 * 1000, config.feedLimit); // 1 hour cache
        // Filter by kind and mute list
        const filtered = filterEvents(
          cached.filter(e => 
            e.kind !== KIND.DISCUSSION_THREAD && 
            getKindInfo(e.kind).showInFeed === true
          )
        );
      
        if (filtered.length > 0 && isMounted) {
          const unique = Array.from(new Map(filtered.map((e: NostrEvent) => [e.id, e])).values());
          allEvents = unique.sort((a: NostrEvent, b: NostrEvent) => b.created_at - a.created_at);
          oldestTimestamp = Math.min(...allEvents.map((e: NostrEvent) => e.created_at));
          loading = false; // Show cached content immediately
        }
      }
      
      // Fetch fresh data from relays (can be slow)
      const relays = singleRelay ? [singleRelay] : relayManager.getFeedReadRelays();
      
      if (singleRelay) {
        const relay = await nostrClient.getRelay(singleRelay);
        if (!relay) {
          relayError = `Relay ${singleRelay} is unavailable.`;
          loading = false;
          return;
          }
        }
        
      const feedKinds = getFeedKinds().filter(k => k !== KIND.DISCUSSION_THREAD);
      const filters = feedKinds.map(k => ({ kinds: [k], limit: config.feedLimit }));
      
      // Stream events as they arrive from relays (progressive enhancement)
      // Don't wait for all relays - update UI as each relay responds
      const fetched = await nostrClient.fetchEvents(
        filters,
        relays,
        singleRelay ? {
          useCache: false,
          cacheResults: false,
          timeout: config.singleRelayTimeout
        } : {
          useCache: 'cache-first', // Already shown cache above
          cacheResults: true,
          timeout: config.standardTimeout,
          // Stream events as they arrive from each relay
          onUpdate: (newEvents) => {
            if (!isMounted) return;
            
            // Filter by kind and mute list
            const filtered = filterEvents(
              newEvents.filter(e => 
                e.kind !== KIND.DISCUSSION_THREAD && 
                getKindInfo(e.kind).showInFeed === true
              )
            );
            
            if (filtered.length === 0) return;
            
            // Merge with existing events (deduplicate by ID)
            const eventMap = new Map(allEvents.map((e: NostrEvent) => [e.id, e]));
            for (const event of filtered) {
              eventMap.set(event.id, event);
            }
            
            // Update UI immediately with new events
            const sorted = Array.from(eventMap.values())
              .sort((a: NostrEvent, b: NostrEvent) => b.created_at - a.created_at);
            allEvents = sorted;
            
            if (sorted.length > 0) {
              oldestTimestamp = Math.min(...sorted.map((e: NostrEvent) => e.created_at));
            }
          }
        }
      );
      
      if (!isMounted) return;
      
      // Final merge of any remaining events (for single relay mode or fallback)
      // Filter by kind and mute list
      const filtered = filterEvents(
        fetched.filter(e => 
          e.kind !== KIND.DISCUSSION_THREAD && 
          getKindInfo(e.kind).showInFeed === true
        )
      );
      
      if (filtered.length > 0) {
        const eventMap = new Map(allEvents.map((e: NostrEvent) => [e.id, e]));
        for (const event of filtered) {
          eventMap.set(event.id, event);
        }
        
        const sorted = Array.from(eventMap.values()).sort((a: NostrEvent, b: NostrEvent) => b.created_at - a.created_at);
        allEvents = sorted;
        
        if (sorted.length > 0) {
          oldestTimestamp = Math.min(...sorted.map((e: NostrEvent) => e.created_at));
        }
      }
      
      // Batch fetch referenced events (e, a, q tags) after main events are loaded
      if (allEvents.length > 0) {
        await batchFetchReferencedEvents(allEvents);
        // Batch load reactions for kind 11 events
        await batchLoadReactionsForKind11(allEvents);
      }
    } catch (error) {
      // Failed to load feed
      if (!events.length) {
        relayError = 'Failed to load feed.';
      }
    } finally {
      loading = false;
      initialLoadComplete = true;
      loadInProgress = false;
    }
  }
  
  // Collect and batch fetch all referenced events from e, a, q tags
  async function batchFetchReferencedEvents(events: NostrEvent[]) {
    if (!isMounted || events.length === 0) return;
    
    const eventIds = new Set<string>(); // For e and q tags
    const aTagGroups = new Map<string, { kind: number; pubkey: string; dTag?: string }>(); // For a tags
    
    // Collect all references
    for (const event of events) {
      // Check q tag (quote)
      const qTag = event.tags.find(t => t[0] === 'q' && t[1]);
      if (qTag && qTag[1] && qTag[1] !== event.id) {
        eventIds.add(qTag[1]);
      }
      
      // Check e tag (reply) - skip if it's the event's own ID
      const eTag = event.tags.find(t => t[0] === 'e' && t[1] && t[1] !== event.id);
      if (eTag && eTag[1]) {
        eventIds.add(eTag[1]);
      }
      
      // Check a tag (addressable event)
      const aTag = event.tags.find(t => t[0] === 'a' && t[1]);
      if (aTag && aTag[1]) {
        const parts = aTag[1].split(':');
        if (parts.length >= 2) {
          const kind = parseInt(parts[0]);
          const pubkey = parts[1];
          const dTag = parts[2] || undefined;
          const groupKey = `${kind}:${pubkey}:${dTag || ''}`;
          if (!aTagGroups.has(groupKey)) {
            aTagGroups.set(groupKey, { kind, pubkey, dTag });
          }
        }
      }
    }
    
    // Remove event IDs that are already in the feed (no need to fetch them)
    const feedEventIds = new Set(events.map(e => e.id));
    const eventIdsToFetch = Array.from(eventIds).filter(id => !feedEventIds.has(id));
    
    if (eventIdsToFetch.length === 0 && aTagGroups.size === 0) {
      return; // Nothing to fetch
    }
    
    const relays = singleRelay ? [singleRelay] : relayManager.getFeedReadRelays();
    const fetchedEvents = new Map<string, NostrEvent>();
    
    try {
      // Fetch events by ID (e and q tags) in batches
      if (eventIdsToFetch.length > 0) {
        const batchSize = 100;
        for (let i = 0; i < eventIdsToFetch.length; i += batchSize) {
          const batch = eventIdsToFetch.slice(i, i + batchSize);
          const events = await nostrClient.fetchEvents(
            [{ ids: batch, limit: batch.length }],
            relays,
            {
              useCache: true,
              cacheResults: true,
              priority: 'low', // Low priority - don't block main feed
              timeout: config.standardTimeout
            }
          );
          
          for (const event of events) {
            fetchedEvents.set(event.id, event);
          }
        }
      }
      
      // Fetch addressable events (a tags) - group by kind+pubkey+d-tag
      if (aTagGroups.size > 0) {
        const aTagFilters: any[] = [];
        const filterToATag = new Map<number, string>(); // filter index -> a-tag string
        
        for (const [groupKey, group] of aTagGroups.entries()) {
          const filter: any = {
            kinds: [group.kind],
            authors: [group.pubkey],
            limit: 100
          };
          
          if (group.dTag) {
            filter['#d'] = [group.dTag];
          }
          
          const filterIndex = aTagFilters.length;
          aTagFilters.push(filter);
          filterToATag.set(filterIndex, groupKey);
        }
        
        if (aTagFilters.length > 0) {
          const aTagEvents = await nostrClient.fetchEvents(
            aTagFilters,
            relays,
            {
              useCache: true,
              cacheResults: true,
              priority: 'low',
              timeout: config.standardTimeout
            }
          );
          
          // Map a-tag events back to their a-tag strings
          for (let filterIndex = 0; filterIndex < aTagFilters.length; filterIndex++) {
            const filter = aTagFilters[filterIndex];
            const groupKey = filterToATag.get(filterIndex);
            if (!groupKey) continue;
            
            const [, pubkey, dTag] = groupKey.split(':');
            const kind = filter.kinds[0];
            
            // Find matching events
            const matchingEvents = aTagEvents.filter(e => 
              e.kind === kind && 
              e.pubkey === pubkey &&
              (!dTag || e.tags.find(t => t[0] === 'd' && t[1] === dTag))
            );
            
            for (const event of matchingEvents) {
              // Store by event ID (will be matched by ReferencedEventPreview)
              fetchedEvents.set(event.id, event);
            }
          }
        }
      }
      
      // Update preloaded events map (merge with existing)
      if (fetchedEvents.size > 0 && isMounted) {
        const merged = new Map(preloadedReferencedEvents);
        for (const [id, event] of fetchedEvents.entries()) {
          merged.set(id, event);
        }
        preloadedReferencedEvents = merged;
      }
    } catch (error) {
      // Failed to fetch referenced events (non-critical)
      // Don't block on errors - components will fetch individually if needed
    }
  }

  // Batch load reactions for kind 11 events (optimized cache-first loading)
  async function batchLoadReactionsForKind11(events: NostrEvent[]) {
    if (!isMounted || events.length === 0) return;
    
    // Filter to only kind 11 events
    const kind11Events = events.filter(e => e.kind === KIND.DISCUSSION_THREAD);
    if (kind11Events.length === 0) return;
    
    const eventIds = kind11Events.map(e => e.id);
    const reactionRelays = relayManager.getProfileReadRelays();
    const allReactionsMap = new Map<string, NostrEvent>();
    
    try {
      // Step 1: Load from cache first (instant)
      const cachedReactionsMap = await getCachedReactionsForEvents(eventIds);
      
      // Add cached reactions immediately
      for (const [eventId, reactions] of cachedReactionsMap) {
        for (const reaction of reactions) {
          allReactionsMap.set(reaction.id, reaction);
        }
      }
      
      // Process cached reactions and update preloaded map
      const reactionsByEvent = new Map<string, NostrEvent[]>();
      for (const [eventId, reactions] of cachedReactionsMap) {
        reactionsByEvent.set(eventId, reactions);
      }
      
      // Update preloaded reactions map with cached data
      if (reactionsByEvent.size > 0 && isMounted) {
        const merged = new Map(preloadedReactionsMap);
        for (const [eventId, reactions] of reactionsByEvent.entries()) {
          merged.set(eventId, reactions);
        }
        preloadedReactionsMap = merged;
      }
      
      // Step 2: Fetch from relays in parallel (fast, non-blocking)
      const reactionsFetchPromise = nostrClient.fetchEvents(
        [
          { kinds: [KIND.REACTION], '#e': eventIds, limit: config.feedLimit },
          { kinds: [KIND.REACTION], '#E': eventIds, limit: config.feedLimit }
        ],
        reactionRelays,
        { 
          useCache: 'cache-first',
          cacheResults: true,
          timeout: config.shortTimeout,
          priority: 'low'
        }
      );
      
      // Don't await - let it update in background
      reactionsFetchPromise.then((allReactions) => {
        if (!isMounted) return;
        
        // Group reactions by event ID
        const updatedReactionsByEvent = new Map<string, NostrEvent[]>();
        
        for (const reaction of allReactions) {
          // Find the event ID this reaction references
          const eventIdTag = reaction.tags.find(t => {
            const tagName = t[0];
            return (tagName === 'e' || tagName === 'E') && t[1] && eventIds.includes(t[1]);
          });
          
          if (eventIdTag && eventIdTag[1]) {
            const eventId = eventIdTag[1];
            if (!updatedReactionsByEvent.has(eventId)) {
              updatedReactionsByEvent.set(eventId, []);
            }
            updatedReactionsByEvent.get(eventId)!.push(reaction);
          }
        }
        
        // Update preloaded reactions map
        if (updatedReactionsByEvent.size > 0 && isMounted) {
          const merged = new Map(preloadedReactionsMap);
          for (const [eventId, reactions] of updatedReactionsByEvent.entries()) {
            merged.set(eventId, reactions);
          }
          preloadedReactionsMap = merged;
        }
      }).catch(() => {
        // Silently fail - reactions are non-critical
      });
    } catch (error) {
      // Failed to load reactions (non-critical)
    }
  }

  // Setup subscription (only adds to waiting room)
  function setupSubscription() {
    if (subscriptionId || singleRelay) return;
    
    const relays = relayManager.getFeedReadRelays();
    const feedKinds = getFeedKinds().filter(k => k !== KIND.DISCUSSION_THREAD);
    const filters = feedKinds.map(k => ({ kinds: [k], limit: config.feedLimit }));
    
    subscriptionId = nostrClient.subscribe(
      filters,
      relays,
      (event: NostrEvent) => {
        if (!isMounted || event.kind === KIND.DISCUSSION_THREAD || !initialLoadComplete) return;
        
        // Add to waiting room if not already in feed or waiting room
        const eventIds = new Set([...allEvents.map((e: NostrEvent) => e.id), ...waitingRoomEvents.map((e: NostrEvent) => e.id)]);
        if (!eventIds.has(event.id)) {
          waitingRoomEvents = [...waitingRoomEvents, event].sort((a, b) => b.created_at - a.created_at);
    }
      },
      () => {}
    );
  }

  onMount(() => {
    isMounted = true;
    loadInProgress = false;
    
    // Load virtualizer for better performance with large feeds
    loadVirtualizer();
    
    // Use a small delay to ensure previous page cleanup completes
    const initTimeout = setTimeout(() => {
      if (!isMounted) return;
      
      nostrClient.initialize().then(() => {
        if (isMounted && !loadInProgress) {
          loadFeed().then(() => {
            if (isMounted) {
              setupSubscription();
            }
          });
        }
      });
    }, 50);
    
    // Register j/k navigation shortcuts
    if (browser) {
      const unregisterJ = keyboardShortcuts.register('j', (e) => {
        const activeElement = document.activeElement as HTMLElement | null;
        if (activeElement?.tagName === 'INPUT' || activeElement?.tagName === 'TEXTAREA' || activeElement?.isContentEditable) {
          return; // Don't interfere with typing
        }
        e.preventDefault();
        navigateToNextPost();
        return false;
      });
      
      const unregisterK = keyboardShortcuts.register('k', (e) => {
        const activeElement = document.activeElement as HTMLElement | null;
        if (activeElement?.tagName === 'INPUT' || activeElement?.tagName === 'TEXTAREA' || activeElement?.isContentEditable) {
          return; // Don't interfere with typing
        }
        e.preventDefault();
        navigateToPreviousPost();
        return false;
      });
      
      return () => {
        clearTimeout(initTimeout);
        isMounted = false;
        loadInProgress = false;
        if (subscriptionId) {
          nostrClient.unsubscribe(subscriptionId);
          subscriptionId = null;
        }
        unregisterJ();
        unregisterK();
      };
    }
    
    return () => {
      clearTimeout(initTimeout);
      isMounted = false;
      loadInProgress = false;
      if (subscriptionId) {
        nostrClient.unsubscribe(subscriptionId);
        subscriptionId = null;
      }
    };
  });
  
  function navigateToNextPost() {
    const posts = Array.from(document.querySelectorAll('[data-post-id]'));
    const currentIndex = posts.findIndex(p => p === document.activeElement || p.contains(document.activeElement));
    const nextIndex = currentIndex < posts.length - 1 ? currentIndex + 1 : 0;
    if (posts[nextIndex]) {
      (posts[nextIndex] as HTMLElement).focus();
      posts[nextIndex].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
    }
  }
  
  function navigateToPreviousPost() {
    const posts = Array.from(document.querySelectorAll('[data-post-id]'));
    const currentIndex = posts.findIndex(p => p === document.activeElement || p.contains(document.activeElement));
    const prevIndex = currentIndex > 0 ? currentIndex - 1 : posts.length - 1;
    if (posts[prevIndex]) {
      (posts[prevIndex] as HTMLElement).focus();
      posts[prevIndex].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
    }
  }

</script>

<div class="feed-page">
  {#if singleRelay}
    <div class="relay-info">
      <p class="relay-info-text">Showing feed from: <code class="relay-url">{singleRelay}</code></p>
    </div>
  {/if}
  
  {#if loading}
    <div class="loading-state">
      <p class="text-fog-text dark:text-fog-dark-text">Loading feed...</p>
    </div>
  {:else if relayError}
    <div class="error-state">
      <p class="text-fog-text dark:text-fog-dark-text error-message">{relayError}</p>
    </div>
  {:else if events.length === 0}
    <div class="empty-state">
      <p class="text-fog-text dark:text-fog-dark-text">No posts found.</p>
    </div>
  {:else}
    {#if Virtualizer && typeof Virtualizer === 'function' && events.length > 50}
      <!-- Use virtual scrolling for large feeds (50+ items) -->
      <!-- Note: Virtualizer is disabled due to compatibility issues -->
      <!-- Fallback to regular rendering -->
      <div class="feed-posts">
        {#each paginatedEvents as event (event.id)}
          {@const referencedEvent = getReferencedEventForPost(event)}
          {@const preloadedReactions = preloadedReactionsMap.get(event.id) ?? []}
          <FeedPost post={event} preloadedReferencedEvent={referencedEvent} preloadedReactions={preloadedReactions} />
        {/each}
      </div>
    {:else}
      <!-- Fallback to regular rendering for small feeds or when virtualizer not loaded -->
      <div class="feed-posts">
        {#each paginatedEvents as event (event.id)}
          {@const referencedEvent = getReferencedEventForPost(event)}
          {@const preloadedReactions = preloadedReactionsMap.get(event.id) ?? []}
          <FeedPost post={event} preloadedReferencedEvent={referencedEvent} preloadedReactions={preloadedReactions} />
        {/each}
      </div>
    {/if}
    
    {#if events.length > ITEMS_PER_PAGE}
      <Pagination totalItems={events.length} itemsPerPage={ITEMS_PER_PAGE} />
    {:else}
      <div class="load-more-section">
        <button
          onclick={loadOlderEvents}
          disabled={loadingMore || !hasMoreEvents}
          class="see-more-events-btn"
        >
          {loadingMore ? 'Loading...' : 'See more events'}
        </button>
      </div>
    {/if}
    
  {/if}
</div>

<style>
  .feed-page {
    max-width: 100%;
  }

  .loading-state,
  .empty-state,
  .error-state {
    padding: 2rem;
    text-align: center;
  }

  .error-message {
    font-weight: 600;
    color: var(--fog-accent, #64748b);
  }

  :global(.dark) .error-message {
    color: var(--fog-dark-accent, #94a3b8);
  }

  .feed-posts {
    display: flex;
    flex-direction: column;
    gap: 1rem;
  }

  .relay-info {
    margin-bottom: 1.5rem;
    padding: 1rem;
    background: var(--fog-highlight, #f3f4f6);
    border: 1px solid var(--fog-border, #e5e7eb);
    border-radius: 0.375rem;
  }

  :global(.dark) .relay-info {
    background: var(--fog-dark-highlight, #374151);
    border-color: var(--fog-dark-border, #475569);
  }

  .relay-info-text {
    margin: 0;
    font-size: 0.875rem;
    color: var(--fog-text, #1f2937);
  }

  :global(.dark) .relay-info-text {
    color: var(--fog-dark-text, #f9fafb);
  }

  .relay-url {
    font-family: monospace;
    font-size: 0.875rem;
    background: var(--fog-post, #ffffff);
    padding: 0.25rem 0.5rem;
    border-radius: 0.25rem;
    border: 1px solid var(--fog-border, #e5e7eb);
  }

  :global(.dark) .relay-url {
    background: var(--fog-dark-post, #1f2937);
    border-color: var(--fog-dark-border, #374151);
  }

  .see-more-events-btn {
    padding: 0.75rem 1.5rem;
    background: var(--fog-accent, #64748b);
    color: white;
    border: none;
    border-radius: 0.375rem;
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s;
  }

  .see-more-events-btn:hover:not(:disabled) {
    background: var(--fog-text, #475569);
  }

  .see-more-events-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  :global(.dark) .see-more-events-btn {
    background: var(--fog-dark-accent, #94a3b8);
    color: var(--fog-dark-text, #f9fafb);
  }

  :global(.dark) .see-more-events-btn:hover:not(:disabled) {
    background: var(--fog-dark-text, #cbd5e1);
  }

  .load-more-section {
    padding: 2rem;
    text-align: center;
  }
</style>
