import { ref, reactive } from 'vue'
import { BaseService } from '@/core/base/BaseService'
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
import { finalizeEvent, type EventTemplate } from 'nostr-tools'
import type { Event as NostrEvent } from 'nostr-tools'

export interface Reaction {
  id: string
  eventId: string // The event being reacted to
  pubkey: string // Who reacted
  content: string // The reaction content ('+', '-', emoji)
  created_at: number
}

export interface EventReactions {
  eventId: string
  likes: number
  dislikes: number
  totalReactions: number
  userHasLiked: boolean
  userHasDisliked: boolean
  userReactionId?: string // Track the user's reaction ID for deletion
  reactions: Reaction[]
}

export class ReactionService extends BaseService {
  protected readonly metadata = {
    name: 'ReactionService',
    version: '1.0.0',
    dependencies: []
  }

  protected relayHub: any = null
  protected authService: any = null

  // Reaction state - indexed by event ID
  private _eventReactions = reactive(new Map<string, EventReactions>())
  private _isLoading = ref(false)

  // Track reaction subscription
  private currentSubscription: string | null = null
  private currentUnsubscribe: (() => void) | null = null

  // Track deletion subscription separately
  private deletionUnsubscribe: (() => void) | null = null

  // Track which events we're monitoring
  private monitoredEvents = new Set<string>()

  // Track deleted reactions to hide them
  private deletedReactions = new Set<string>()

  protected async onInitialize(): Promise<void> {
    console.log('ReactionService: Starting initialization...')

    this.relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
    this.authService = injectService(SERVICE_TOKENS.AUTH_SERVICE)

    if (!this.relayHub) {
      throw new Error('RelayHub service not available')
    }

    // Start monitoring deletion events globally
    await this.startDeletionMonitoring()

    console.log('ReactionService: Initialization complete')
  }

  /**
   * Start monitoring deletion events globally
   */
  private async startDeletionMonitoring(): Promise<void> {
    try {
      if (!this.relayHub?.isConnected) {
        await this.relayHub?.connect()
      }

      const subscriptionId = `reaction-deletions-${Date.now()}`

      // Subscribe to ALL deletion events for reactions
      const filter = {
        kinds: [5], // Deletion requests
        '#k': ['7'], // Only for reaction events
        since: Math.floor(Date.now() / 1000) - 86400, // Last 24 hours
        limit: 1000
      }

      console.log('ReactionService: Starting global deletion monitoring')

      const unsubscribe = this.relayHub.subscribe({
        id: subscriptionId,
        filters: [filter],
        onEvent: (event: NostrEvent) => {
          this.handleDeletionEvent(event)
        },
        onEose: () => {
          console.log('ReactionService: Initial deletion events loaded')
        }
      })

      // Store subscription ID if needed for tracking
      this.deletionUnsubscribe = unsubscribe

    } catch (error) {
      console.error('Failed to start deletion monitoring:', error)
    }
  }

  /**
   * Get reactions for a specific event
   */
  getEventReactions(eventId: string): EventReactions {
    if (!this._eventReactions.has(eventId)) {
      this._eventReactions.set(eventId, {
        eventId,
        likes: 0,
        dislikes: 0,
        totalReactions: 0,
        userHasLiked: false,
        userHasDisliked: false,
        reactions: []
      })
    }
    return this._eventReactions.get(eventId)!
  }

  /**
   * Subscribe to reactions for a list of event IDs
   */
  async subscribeToReactions(eventIds: string[]): Promise<void> {
    if (eventIds.length === 0) return

    // Filter out events we're already monitoring
    const newEventIds = eventIds.filter(id => !this.monitoredEvents.has(id))
    if (newEventIds.length === 0) return

    console.log(`ReactionService: Subscribing to reactions for ${newEventIds.length} events`)

    try {
      if (!this.relayHub?.isConnected) {
        await this.relayHub?.connect()
      }

      // Add to monitored set
      newEventIds.forEach(id => this.monitoredEvents.add(id))

      const subscriptionId = `reactions-${Date.now()}`

      // Subscribe to reactions (kind 7) and deletions (kind 5) for these events
      const filters = [
        {
          kinds: [7], // Reactions
          '#e': newEventIds, // Events being reacted to
          limit: 1000
        },
        {
          kinds: [5], // Deletion requests for ALL users
          '#k': ['7'], // Only deletions of reaction events (kind 7)
          limit: 500
        }
      ]

      console.log('ReactionService: Creating reaction subscription', filters)

      const unsubscribe = this.relayHub.subscribe({
        id: subscriptionId,
        filters: filters,
        onEvent: (event: NostrEvent) => {
          if (event.kind === 7) {
            this.handleReactionEvent(event)
          } else if (event.kind === 5) {
            this.handleDeletionEvent(event)
          }
        },
        onEose: () => {
          console.log(`Reaction subscription ${subscriptionId} complete`)
        }
      })

      // Store subscription info (we can have multiple)
      if (!this.currentSubscription) {
        this.currentSubscription = subscriptionId
        this.currentUnsubscribe = unsubscribe
      }

    } catch (error) {
      console.error('Failed to subscribe to reactions:', error)
    }
  }

  /**
   * Handle incoming reaction event
   */
  private handleReactionEvent(event: NostrEvent): void {
    try {
      // Find the event being reacted to
      const eTag = event.tags.find(tag => tag[0] === 'e')
      if (!eTag || !eTag[1]) {
        console.warn('Reaction event missing e tag:', event.id)
        return
      }

      const eventId = eTag[1]
      const content = event.content.trim()

      // Create reaction object
      const reaction: Reaction = {
        id: event.id,
        eventId,
        pubkey: event.pubkey,
        content,
        created_at: event.created_at
      }

      // Update event reactions
      const eventReactions = this.getEventReactions(eventId)

      // Check if this reaction already exists (deduplication) or is deleted
      const existingIndex = eventReactions.reactions.findIndex(r => r.id === reaction.id)
      if (existingIndex >= 0) {
        return // Already have this reaction
      }

      // Check if this reaction has been deleted
      if (this.deletedReactions.has(reaction.id)) {
        return // This reaction was deleted
      }

      // IMPORTANT: Remove any previous reaction from the same user
      // This ensures one reaction per user per event, even if deletion events aren't processed
      const previousReactionIndex = eventReactions.reactions.findIndex(r =>
        r.pubkey === reaction.pubkey &&
        r.content === reaction.content
      )

      if (previousReactionIndex >= 0) {
        // Replace the old reaction with the new one
        console.log(`ReactionService: Replacing previous reaction from ${reaction.pubkey.slice(0, 8)}...`)
        eventReactions.reactions[previousReactionIndex] = reaction
      } else {
        // Add as new reaction
        eventReactions.reactions.push(reaction)
      }

      // Recalculate counts and user state
      this.recalculateEventReactions(eventId)

      console.log(`ReactionService: Added/updated reaction ${content} to event ${eventId.slice(0, 8)}...`)

    } catch (error) {
      console.error('Failed to handle reaction event:', error)
    }
  }

  /**
   * Handle deletion event
   */
  private handleDeletionEvent(event: NostrEvent): void {
    try {
      // Process each deleted event
      const eTags = event.tags.filter(tag => tag[0] === 'e')
      const deletionAuthor = event.pubkey

      for (const eTag of eTags) {
        const deletedEventId = eTag[1]
        if (deletedEventId) {
          // Add to deleted set
          this.deletedReactions.add(deletedEventId)

          // Find and remove the reaction from all event reactions
          for (const [eventId, eventReactions] of this._eventReactions) {
            const reactionIndex = eventReactions.reactions.findIndex(r => r.id === deletedEventId)

            if (reactionIndex >= 0) {
              const reaction = eventReactions.reactions[reactionIndex]

              // IMPORTANT: Only process deletion if it's from the same user who created the reaction
              // This follows NIP-09 spec: "Relays SHOULD delete or stop publishing any referenced events
              // that have an identical `pubkey` as the deletion request"
              if (reaction.pubkey === deletionAuthor) {
                eventReactions.reactions.splice(reactionIndex, 1)
                // Recalculate counts for this event
                this.recalculateEventReactions(eventId)
                console.log(`ReactionService: Removed deleted reaction ${deletedEventId.slice(0, 8)}... from ${deletionAuthor.slice(0, 8)}...`)
              } else {
                console.log(`ReactionService: Ignoring deletion request from ${deletionAuthor.slice(0, 8)}... for reaction by ${reaction.pubkey.slice(0, 8)}...`)
              }
            }
          }
        }
      }
    } catch (error) {
      console.error('Failed to handle deletion event:', error)
    }
  }

  /**
   * Recalculate reaction counts and user state for an event
   */
  private recalculateEventReactions(eventId: string): void {
    const eventReactions = this.getEventReactions(eventId)
    const userPubkey = this.authService?.user?.value?.pubkey

    // Use Sets to track unique users who liked/disliked
    const likedUsers = new Set<string>()
    const dislikedUsers = new Set<string>()
    let userHasLiked = false
    let userHasDisliked = false
    let userReactionId: string | undefined

    // Group reactions by user, keeping only the most recent
    const latestReactionsByUser = new Map<string, Reaction>()

    for (const reaction of eventReactions.reactions) {
      // Skip deleted reactions
      if (this.deletedReactions.has(reaction.id)) {
        continue
      }

      // Keep only the latest reaction from each user
      const existing = latestReactionsByUser.get(reaction.pubkey)
      if (!existing || reaction.created_at > existing.created_at) {
        latestReactionsByUser.set(reaction.pubkey, reaction)
      }
    }

    // Now count unique reactions
    for (const reaction of latestReactionsByUser.values()) {
      const isLike = reaction.content === '+' || reaction.content === '❤️' || reaction.content === ''
      const isDislike = reaction.content === '-'

      if (isLike) {
        likedUsers.add(reaction.pubkey)
        if (userPubkey && reaction.pubkey === userPubkey) {
          userHasLiked = true
          userReactionId = reaction.id
        }
      } else if (isDislike) {
        dislikedUsers.add(reaction.pubkey)
        if (userPubkey && reaction.pubkey === userPubkey) {
          userHasDisliked = true
          userReactionId = reaction.id
        }
      }
    }

    // Update the reactive state with unique user counts
    eventReactions.likes = likedUsers.size
    eventReactions.dislikes = dislikedUsers.size
    eventReactions.totalReactions = latestReactionsByUser.size
    eventReactions.userHasLiked = userHasLiked
    eventReactions.userHasDisliked = userHasDisliked
    eventReactions.userReactionId = userReactionId
  }

  /**
   * Send a heart reaction (like) to an event
   */
  async likeEvent(eventId: string, eventPubkey: string, eventKind: number): Promise<void> {
    if (!this.authService?.isAuthenticated?.value) {
      throw new Error('Must be authenticated to react')
    }

    if (!this.relayHub?.isConnected) {
      throw new Error('Not connected to relays')
    }

    const userPubkey = this.authService.user.value?.pubkey
    const userPrivkey = this.authService.user.value?.prvkey

    if (!userPubkey || !userPrivkey) {
      throw new Error('User keys not available')
    }

    // Check if user already liked this event
    const eventReactions = this.getEventReactions(eventId)
    if (eventReactions.userHasLiked) {
      throw new Error('Already liked this event')
    }

    try {
      this._isLoading.value = true

      // Create reaction event template according to NIP-25
      const eventTemplate: EventTemplate = {
        kind: 7, // Reaction
        content: '+', // Like reaction
        tags: [
          ['e', eventId, '', eventPubkey], // Event being reacted to
          ['p', eventPubkey], // Author of the event being reacted to
          ['k', eventKind.toString()] // Kind of the event being reacted to
        ],
        created_at: Math.floor(Date.now() / 1000)
      }

      console.log('ReactionService: Creating like reaction:', eventTemplate)

      // Sign the event
      const privkeyBytes = this.hexToUint8Array(userPrivkey)
      const signedEvent = finalizeEvent(eventTemplate, privkeyBytes)

      console.log('ReactionService: Publishing like reaction:', signedEvent)

      // Publish the reaction
      const result = await this.relayHub.publishEvent(signedEvent)

      console.log(`ReactionService: Like published to ${result.success}/${result.total} relays`)

      // Optimistically update local state
      this.handleReactionEvent(signedEvent)

    } catch (error) {
      console.error('Failed to like event:', error)
      throw error
    } finally {
      this._isLoading.value = false
    }
  }

  /**
   * Remove a like from an event (unlike) using NIP-09 deletion events
   */
  async unlikeEvent(eventId: string): Promise<void> {
    if (!this.authService?.isAuthenticated?.value) {
      throw new Error('Must be authenticated to remove reaction')
    }

    if (!this.relayHub?.isConnected) {
      throw new Error('Not connected to relays')
    }

    const userPubkey = this.authService.user.value?.pubkey
    const userPrivkey = this.authService.user.value?.prvkey

    if (!userPubkey || !userPrivkey) {
      throw new Error('User keys not available')
    }

    // Get the user's reaction ID to delete
    const eventReactions = this.getEventReactions(eventId)
    if (!eventReactions.userHasLiked || !eventReactions.userReactionId) {
      throw new Error('No reaction to remove')
    }

    try {
      this._isLoading.value = true

      // Create deletion event according to NIP-09
      const eventTemplate: EventTemplate = {
        kind: 5, // Deletion request
        content: '', // Empty content or reason
        tags: [
          ['e', eventReactions.userReactionId], // The reaction event to delete
          ['k', '7'] // Kind of event being deleted (reaction)
        ],
        created_at: Math.floor(Date.now() / 1000)
      }

      console.log('ReactionService: Creating deletion event for reaction:', eventReactions.userReactionId)

      // Sign the event
      const privkeyBytes = this.hexToUint8Array(userPrivkey)
      const signedEvent = finalizeEvent(eventTemplate, privkeyBytes)

      console.log('ReactionService: Publishing deletion event:', signedEvent)

      // Publish the deletion
      const result = await this.relayHub.publishEvent(signedEvent)

      console.log(`ReactionService: Deletion published to ${result.success}/${result.total} relays`)

      // Optimistically update local state
      this.handleDeletionEvent(signedEvent)

    } catch (error) {
      console.error('Failed to unlike event:', error)
      throw error
    } finally {
      this._isLoading.value = false
    }
  }

  /**
   * Toggle like on an event - like if not liked, unlike if already liked
   */
  async toggleLikeEvent(eventId: string, eventPubkey: string, eventKind: number): Promise<void> {
    const eventReactions = this.getEventReactions(eventId)

    if (eventReactions.userHasLiked) {
      // Unlike the event
      await this.unlikeEvent(eventId)
    } else {
      // Like the event
      await this.likeEvent(eventId, eventPubkey, eventKind)
    }
  }

  /**
   * Helper function to convert hex string to Uint8Array
   */
  private hexToUint8Array(hex: string): Uint8Array {
    const bytes = new Uint8Array(hex.length / 2)
    for (let i = 0; i < hex.length; i += 2) {
      bytes[i / 2] = parseInt(hex.substr(i, 2), 16)
    }
    return bytes
  }

  /**
   * Get all event reactions
   */
  get eventReactions(): Map<string, EventReactions> {
    return this._eventReactions
  }

  /**
   * Check if currently loading
   */
  get isLoading(): boolean {
    return this._isLoading.value
  }

  /**
   * Cleanup
   */
  protected async onDestroy(): Promise<void> {
    if (this.currentUnsubscribe) {
      this.currentUnsubscribe()
    }
    if (this.deletionUnsubscribe) {
      this.deletionUnsubscribe()
    }
    this._eventReactions.clear()
    this.monitoredEvents.clear()
    this.deletedReactions.clear()
  }
}