<script lang="ts">
  import { sessionManager } from '../../services/auth/session-manager.js';
  import { signAndPublish } from '../../services/nostr/auth-handler.js';
  import { nostrClient } from '../../services/nostr/nostr-client.js';
  import { relayManager } from '../../services/nostr/relay-manager.js';
  import { config } from '../../services/nostr/config.js';
  import { onMount } from 'svelte';
  import type { NostrEvent } from '../../types/nostr.js';
  import { KIND } from '../../types/kind-lookup.js';
  import VoteCount from '../../components/content/VoteCount.svelte';

  interface Props {
    event: NostrEvent;
    preloadedReactions?: NostrEvent[]; // Optional pre-loaded reactions to avoid duplicate fetches
  }

  let { event, preloadedReactions }: Props = $props();

  let userReaction = $state<string | null>(null);
  let userReactionEventId = $state<string | null>(null); // Track the event ID of user's reaction
  let loading = $state(true);
  let allReactionsMap = $state<Map<string, NostrEvent>>(new Map()); // Persist reactions map for real-time updates
  
  let loadingReactions = $state(false);
  let lastEventId = $state<string | null>(null);
  let isMounted = $state(false);
  let processingUpdate = $state(false);
  let updateDebounceTimer: ReturnType<typeof setTimeout> | null = null;
  let initialLoadComplete = $state(false); // Track when initial load is done

  // Count upvotes and downvotes
  let upvotes = $derived.by(() => {
    let count = 0;
    for (const reaction of allReactionsMap.values()) {
      const content = reaction.content.trim();
      if (content === '+' || content === '⬆️' || content === '↑') {
        count++;
      }
    }
    return count;
  });

  let downvotes = $derived.by(() => {
    let count = 0;
    for (const reaction of allReactionsMap.values()) {
      const content = reaction.content.trim();
      if (content === '-' || content === '⬇️' || content === '↓') {
        count++;
      }
    }
    return count;
  });

  // Only show votes as calculated after initial load completes
  let votesCalculated = $derived(initialLoadComplete && !loading);

  onMount(() => {
    // Set lastEventId immediately to prevent $effect from running during mount
    if (event.id) {
      lastEventId = event.id;
    }
    isMounted = true;
    
    nostrClient.initialize().then(async () => {
      if (event.id) {
        // Use pre-loaded reactions if available, otherwise fetch
        if (preloadedReactions && preloadedReactions.length > 0) {
          const filtered = await filterDeletedReactions(preloadedReactions);
          
          // Update the map to only contain non-deleted reactions
          // Reassign map to trigger reactivity in Svelte 5
          const filteredMap = new Map<string, NostrEvent>();
          for (const reaction of filtered) {
            filteredMap.set(reaction.id, reaction);
          }
          allReactionsMap = filteredMap;
          
          processReactions(filtered);
          initialLoadComplete = true; // Mark initial load as complete
        } else {
          loadReactions();
        }
      }
    });
  });

  // Reload reactions when event changes (but prevent duplicate loads and initial mount)
  $effect(() => {
    // Only run after mount and when event.id actually changes
    if (!isMounted || !event.id || event.id === lastEventId || loadingReactions) {
      return;
    }
    
    lastEventId = event.id;
    // Clear previous reactions map when event changes
    allReactionsMap = new Map(); // Reassign to trigger reactivity
    initialLoadComplete = false; // Reset when event changes
    
    // Use pre-loaded reactions if available, otherwise fetch
    if (preloadedReactions && preloadedReactions.length > 0) {
      const newMap = new Map<string, NostrEvent>();
      for (const r of preloadedReactions) {
        newMap.set(r.id, r);
      }
      allReactionsMap = newMap; // Reassign to trigger reactivity
      filterDeletedReactions(preloadedReactions).then(filtered => {
        // Update the map to only contain non-deleted reactions
        // Reassign map to trigger reactivity in Svelte 5
        const filteredMap = new Map<string, NostrEvent>();
        for (const reaction of filtered) {
          filteredMap.set(reaction.id, reaction);
        }
        allReactionsMap = filteredMap;
        
        processReactions(filtered);
        initialLoadComplete = true; // Mark initial load as complete
      });
    } else {
      loadReactions();
    }
  });

  // Handle real-time updates - process reactions when new ones arrive
  // Debounced to prevent excessive processing
  async function handleReactionUpdate(updated: NostrEvent[]) {
    // Only process real-time updates after initial load is complete
    if (!initialLoadComplete) {
      return;
    }
    
    // Prevent concurrent processing
    if (processingUpdate) {
      return;
    }
    
    // Add new reactions to the map
    let hasNewReactions = false;
    const newMap = new Map(allReactionsMap);
    for (const r of updated) {
      if (!newMap.has(r.id)) {
        newMap.set(r.id, r);
        hasNewReactions = true;
      }
    }
    if (hasNewReactions) {
      allReactionsMap = newMap; // Reassign to trigger reactivity
    }
    
    // Only process if we have new reactions
    if (!hasNewReactions) {
      return;
    }
    
    // Clear existing debounce timer
    if (updateDebounceTimer) {
      clearTimeout(updateDebounceTimer);
    }
    
    // Debounce processing to batch multiple rapid updates
    updateDebounceTimer = setTimeout(async () => {
      if (processingUpdate) return;
      processingUpdate = true;
      
      try {
        // Process all accumulated reactions and update the map to remove deleted ones
        const allReactions = Array.from(allReactionsMap.values());
        const filtered = await filterDeletedReactions(allReactions);
        
        // Update the map to only contain non-deleted reactions
        // Reassign map to trigger reactivity in Svelte 5
        const filteredMap = new Map<string, NostrEvent>();
        for (const reaction of filtered) {
          filteredMap.set(reaction.id, reaction);
        }
        allReactionsMap = filteredMap;
        
        // Process reactions to update userReaction state
        processReactions(filtered);
      } finally {
        processingUpdate = false;
      }
    }, 300); // 300ms debounce
  }

  async function loadReactions() {
    // Prevent concurrent loads for the same event
    if (loadingReactions) {
      return;
    }
    loadingReactions = true;
    loading = true;
    try {
      // Use getProfileReadRelays() to include defaultRelays + profileRelays + user inbox + localRelays
      // This ensures we get all reactions from the complete relay set, matching DiscussionList behavior
      const reactionRelays = relayManager.getProfileReadRelays();
      
      // Clear and rebuild reactions map for this event
      allReactionsMap = new Map(); // Reassign to trigger reactivity
      
      // Use low priority for reactions - they're background data, comments should load first
      const reactionsWithLowerE = await nostrClient.fetchEvents(
        [{ kinds: [KIND.REACTION], '#e': [event.id], limit: config.feedLimit }],
        reactionRelays,
        { useCache: true, cacheResults: true, onUpdate: handleReactionUpdate, timeout: config.mediumTimeout, priority: 'low' }
      );
      const reactionsWithUpperE = await nostrClient.fetchEvents(
        [{ kinds: [KIND.REACTION], '#E': [event.id], limit: config.feedLimit }],
        reactionRelays,
        { useCache: true, cacheResults: true, onUpdate: handleReactionUpdate, timeout: config.mediumTimeout, priority: 'low' }
      );
      
      // Combine and deduplicate by reaction ID
      const newMap = new Map<string, NostrEvent>();
      for (const r of reactionsWithLowerE) {
        newMap.set(r.id, r);
      }
      for (const r of reactionsWithUpperE) {
        newMap.set(r.id, r);
      }
      allReactionsMap = newMap; // Reassign to trigger reactivity
      const reactionEvents = Array.from(allReactionsMap.values());
      
      // Filter out deleted reactions (kind 5)
      const filteredReactions = await filterDeletedReactions(reactionEvents);
      
      // Update the map to only contain non-deleted reactions
      // Reassign map to trigger reactivity in Svelte 5
      const filteredMap = new Map<string, NostrEvent>();
      for (const reaction of filteredReactions) {
        filteredMap.set(reaction.id, reaction);
      }
      allReactionsMap = filteredMap;
      
      processReactions(filteredReactions);
      initialLoadComplete = true; // Mark initial load as complete after processing
    } catch (error) {
      console.error('[DiscussionVoteButtons] Error loading reactions:', error);
      initialLoadComplete = true; // Mark as complete even on error to avoid stuck loading state
    } finally {
      loading = false;
      loadingReactions = false;
    }
  }

  async function filterDeletedReactions(reactions: NostrEvent[]): Promise<NostrEvent[]> {
    if (reactions.length === 0) return reactions;
    
    // Optimize: Instead of fetching all deletion events for all users,
    // fetch deletion events that reference the specific reaction IDs we have
    // This is much more efficient and limits memory usage
    const reactionRelays = relayManager.getProfileReadRelays();
    const reactionIds = reactions.map(r => r.id);
    
    // Limit to first 100 reactions to avoid massive queries
    const limitedReactionIds = reactionIds.slice(0, 100);
    
    // Fetch deletion events that reference these specific reaction IDs
    // This is much more efficient than fetching all deletion events from all users
    // Use low priority for deletion events - background data
    const deletionEvents = await nostrClient.fetchEvents(
      [{ kinds: [KIND.EVENT_DELETION], '#e': limitedReactionIds, limit: config.feedLimit }],
      reactionRelays,
      { useCache: true, timeout: config.mediumTimeout, priority: 'low' }
    );
    
    // Build a set of deleted reaction event IDs (more efficient - just a Set)
    const deletedReactionIds = new Set<string>();
    for (const deletionEvent of deletionEvents) {
      // Kind 5 events have 'e' tags pointing to deleted events
      for (const tag of deletionEvent.tags) {
        if (tag[0] === 'e' && tag[1]) {
          deletedReactionIds.add(tag[1]);
        }
      }
    }
    
    // Filter out deleted reactions - much simpler now
    const filtered = reactions.filter(reaction => {
      const isDeleted = deletedReactionIds.has(reaction.id);
      return !isDeleted;
    });
    
    return filtered;
  }

  async function processReactions(reactionEvents: NostrEvent[]) {
    // Prevent duplicate processing - check if we're already processing the same set
    if (processingUpdate) {
      return;
    }
    const currentUser = sessionManager.getCurrentPubkey();

    for (const reactionEvent of reactionEvents) {
      let content = reactionEvent.content.trim();
      
      // Normalize reactions: only + and - allowed
      // Backward compatibility: ⬆️/↑ = +, ⬇️/↓ = -
      if (content === '⬆️' || content === '↑') {
        content = '+';
      } else if (content === '⬇️' || content === '↓') {
        content = '-';
      } else if (content !== '+' && content !== '-') {
        // Skip invalid reactions for voting
        continue;
      }

      if (currentUser && reactionEvent.pubkey === currentUser) {
        userReaction = content;
        userReactionEventId = reactionEvent.id;
      }
    }
  }

  async function toggleReaction(content: string) {
    if (!sessionManager.isLoggedIn()) {
      alert('Please log in to vote');
      return;
    }

    // Only allow + and - (upvote/downvote)
    if (content !== '+' && content !== '-') {
      return;
    }

    // If clicking the same reaction, delete it
    if (userReaction === content) {
      // Remove reaction by publishing a kind 5 deletion event
      if (userReactionEventId) {
        const reactionIdToDelete = userReactionEventId; // Store before clearing
        try {
          const deletionEvent: Omit<NostrEvent, 'id' | 'sig'> = {
            kind: KIND.EVENT_DELETION,
            pubkey: sessionManager.getCurrentPubkey()!,
            created_at: Math.floor(Date.now() / 1000),
            tags: [['e', reactionIdToDelete]],
            content: ''
          };

          const config = nostrClient.getConfig();
          await signAndPublish(deletionEvent, [...config.defaultRelays]);
          
          // Remove from map immediately so counts update
          allReactionsMap.delete(reactionIdToDelete);
          
          // Update local state immediately
          userReaction = null;
          userReactionEventId = null;
        } catch (error) {
          console.error('Error deleting reaction:', error);
          alert('Error deleting reaction');
        }
      } else {
        // Fallback: just update UI if we don't have the event ID
        userReaction = null;
        userReactionEventId = null;
      }
      return;
    }

    // If user has the opposite vote, delete it first
    if (userReaction && userReaction !== content) {
      // Delete the existing vote first
      if (userReactionEventId) {
        const oldReactionId = userReactionEventId; // Store before clearing
        try {
          const deletionEvent: Omit<NostrEvent, 'id' | 'sig'> = {
            kind: KIND.EVENT_DELETION,
            pubkey: sessionManager.getCurrentPubkey()!,
            created_at: Math.floor(Date.now() / 1000),
            tags: [['e', oldReactionId]],
            content: ''
          };

          const config = nostrClient.getConfig();
          await signAndPublish(deletionEvent, [...config.defaultRelays]);
          
          // Remove from map immediately so counts update
          // Reassign map to trigger reactivity in Svelte 5
          const newMap = new Map(allReactionsMap);
          newMap.delete(oldReactionId);
          allReactionsMap = newMap;
        } catch (error) {
          console.error('Error deleting old reaction:', error);
        }
      }
      // Clear the old reaction state
      userReaction = null;
      userReactionEventId = null;
    }

    try {
      const tags: string[][] = [
        ['e', event.id],
        ['p', event.pubkey],
        ['k', event.kind.toString()]
      ];

      const reactionEvent: Omit<NostrEvent, 'id' | 'sig'> = {
        kind: KIND.REACTION,
        pubkey: sessionManager.getCurrentPubkey()!,
        created_at: Math.floor(Date.now() / 1000),
        tags,
        content
      };

      const config = nostrClient.getConfig();
      
      // Sign the event first to get the ID
      const signedEvent = await sessionManager.signEvent(reactionEvent);
      
      // Publish the signed event
      await nostrClient.publish(signedEvent, { relays: [...config.defaultRelays] });

      // Update local state immediately for instant UI feedback
      userReaction = content;
      userReactionEventId = signedEvent.id;
      
      // Add the new reaction to the map so counts update immediately
      // Reassign map to trigger reactivity in Svelte 5
      const newMap = new Map(allReactionsMap);
      newMap.set(signedEvent.id, signedEvent);
      allReactionsMap = newMap;
    } catch (error) {
      console.error('Error publishing reaction:', error);
    }
  }

  const isLoggedIn = $derived(sessionManager.isLoggedIn());
  
  // Get user's current vote (normalize to + or -)
  let userVote = $derived<string | null>(userReaction === '+' || userReaction === '-' ? userReaction : null);
</script>

<div class="discussion-vote-buttons flex gap-2 items-center">
  <!-- Vote counts with clickable emojis -->
  <VoteCount 
    upvotes={upvotes} 
    downvotes={downvotes} 
    votesCalculated={votesCalculated} 
    size="xs"
    userVote={userVote as '+' | '-' | null}
    onVote={toggleReaction}
    isLoggedIn={isLoggedIn}
  />
</div>

<style>
      .discussion-vote-buttons {
        margin-top: 0.5rem;
      }
</style>
