import { ref, computed } from 'vue'
import { BaseService } from '@/core/base/BaseService'
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
import { eventBus } from '@/core/event-bus'
import type { Event as NostrEvent, Filter } from 'nostr-tools'

export interface FeedPost {
  id: string
  pubkey: string
  content: string
  created_at: number
  kind: number
  tags: string[][]
  mentions: string[]
  isReply: boolean
  replyTo?: string // Direct parent ID
  rootId?: string // Thread root ID
  replies?: FeedPost[] // Child replies
  depth?: number // Depth in reply tree (0 for root posts)
}

export interface ContentFilter {
  id: string
  label: string
  kinds: number[]
  description: string
  requiresAuth?: boolean
  filterByAuthor?: 'admin' | 'exclude-admin' | 'none'
  tags?: string[] // NIP-12 tags to filter by
  keywords?: string[] // Content keywords to search for
}

export interface FeedConfig {
  feedType: 'announcements' | 'general' | 'mentions' | 'events' | 'all' | 'custom'
  maxPosts?: number
  adminPubkeys?: string[]
  contentFilters?: ContentFilter[]
}

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

  protected relayHub: any = null
  protected visibilityService: any = null

  // Event ID tracking for deduplication
  private seenEventIds = new Set<string>()

  // Feed state
  private _posts = ref<FeedPost[]>([])
  private _isLoading = ref(false)
  private _error = ref<string | null>(null)

  // Current subscription state
  private currentSubscription: string | null = null
  private currentUnsubscribe: (() => void) | null = null
  private currentConfig: FeedConfig | null = null

  // Public reactive state
  public readonly posts = computed(() => this._posts.value)
  public readonly isLoading = computed(() => this._isLoading.value)
  public readonly error = computed(() => this._error.value)

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

    this.relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
    this.visibilityService = injectService(SERVICE_TOKENS.VISIBILITY_SERVICE)

    console.log('FeedService: RelayHub injected:', !!this.relayHub)
    console.log('FeedService: VisibilityService injected:', !!this.visibilityService)

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

    // Register with visibility service for proper connection management
    if (this.visibilityService) {
      this.visibilityService.registerService(
        'FeedService',
        this.onResume.bind(this),
        this.onPause.bind(this)
      )
    }

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

  /**
   * Subscribe to feed with deduplication
   */
  async subscribeFeed(config: FeedConfig): Promise<void> {
    // If already subscribed with same config, don't resubscribe
    if (this.currentSubscription && this.currentConfig &&
        JSON.stringify(this.currentConfig) === JSON.stringify(config)) {
      return
    }

    // Unsubscribe from previous feed if exists
    await this.unsubscribeFeed()

    this.currentConfig = config
    this._isLoading.value = true
    this._error.value = null

    try {
      // Check if RelayHub is connected
      if (!this.relayHub) {
        throw new Error('RelayHub not available')
      }

      if (!this.relayHub.isConnected) {
        console.log('RelayHub not connected, attempting to connect...')
        await this.relayHub.connect()
      }

      if (!this.relayHub.isConnected) {
        throw new Error('Unable to connect to relays')
      }

      // Create subscription ID
      const subscriptionId = `feed-service-${config.feedType}-${Date.now()}`

      // Create filters based on feed type and content filters
      const filters: Filter[] = []

      if (config.feedType === 'custom' && config.contentFilters) {
        // Use custom content filters
        // Using custom content filters
        for (const contentFilter of config.contentFilters) {
          const filter: Filter = {
            kinds: contentFilter.kinds,
            limit: Math.floor((config.maxPosts || 50) / config.contentFilters.length)
          }

          // Apply author filtering if specified
          if (contentFilter.filterByAuthor === 'admin') {
            if (config.adminPubkeys?.length) {
              filter.authors = config.adminPubkeys
              // Using admin authors for filtering
            } else {
              // No admin pubkeys configured - include all authors for admin filters
              // No admin pubkeys configured - include all authors
            }
          } else if (contentFilter.filterByAuthor === 'exclude-admin' && config.adminPubkeys?.length) {
            // Note: Nostr doesn't support negative filters natively,
            // we'll filter these out in post-processing
            // Will exclude admin in post-processing
          }

          // Apply tag filtering if specified (NIP-12)
          if (contentFilter.tags && contentFilter.tags.length > 0) {
            filter['#t'] = contentFilter.tags
          }

          filters.push(filter)
        }
      } else {
        // Use legacy feed types
        const filter: Filter = {
          kinds: [1], // Text notes by default
          limit: config.maxPosts || 50
        }

        // Handle legacy feed types
        if (config.feedType === 'announcements') {
          if (config.adminPubkeys && config.adminPubkeys.length > 0) {
            filter.authors = config.adminPubkeys
          } else {
            // No admin pubkeys configured - fall back to all text posts
            console.log('No admin pubkeys configured for announcements feed, showing all posts')
            // filter.authors remains undefined, so all authors are included
          }
        }

        filters.push(filter)
      }

      console.log(`Creating feed subscription for ${config.feedType} with filters:`, filters)

      // Subscribe to events with deduplication
      const unsubscribe = this.relayHub.subscribe({
        id: subscriptionId,
        filters: filters,
        onEvent: (event: NostrEvent) => {
          this.handleNewEvent(event, config)
        },
        onEose: () => {
          console.log(`Feed subscription ${subscriptionId} end of stored events`)
          console.log('FeedService: Setting isLoading to false')
          this._isLoading.value = false
          console.log('FeedService: isLoading is now:', this._isLoading.value)
        },
        onClose: () => {
          console.log(`Feed subscription ${subscriptionId} closed`)
        }
      })

      // Store the subscription info for later cleanup
      this.currentSubscription = subscriptionId
      this.currentUnsubscribe = unsubscribe

      // Set a timeout to stop loading if no EOSE is received
      setTimeout(() => {
        console.log(`Feed subscription ${subscriptionId} timeout check: isLoading=${this._isLoading.value}, currentSub=${this.currentSubscription}`)
        if (this._isLoading.value && this.currentSubscription === subscriptionId) {
          console.log(`Feed subscription ${subscriptionId} timeout, stopping loading`)
          this._isLoading.value = false
        }
      }, 5000) // 5 second timeout (reduced for testing)

    } catch (err) {
      console.error('Failed to subscribe to feed:', err)
      this._error.value = err instanceof Error ? err.message : 'Failed to subscribe to feed'
      this._isLoading.value = false
    }
  }

  /**
   * Handle new event with robust deduplication
   */
  private handleNewEvent(event: NostrEvent, config: FeedConfig): void {
    // Skip if event already seen
    if (this.seenEventIds.has(event.id)) {
      return
    }

    // Add to seen events
    this.seenEventIds.add(event.id)

    // Check if event should be included based on feed type
    if (!this.shouldIncludeEvent(event, config)) {
      return
    }

    // Extract reply information according to NIP-10
    let rootId: string | undefined
    let replyTo: string | undefined
    let isReply = false

    // Look for marked e-tags first (preferred method)
    const markedRootTag = event.tags?.find((tag: string[]) => tag[0] === 'e' && tag[3] === 'root')
    const markedReplyTag = event.tags?.find((tag: string[]) => tag[0] === 'e' && tag[3] === 'reply')

    if (markedRootTag || markedReplyTag) {
      // Using marked tags (NIP-10 preferred method)
      rootId = markedRootTag?.[1]
      replyTo = markedReplyTag?.[1] || markedRootTag?.[1] // Direct reply to root if no reply tag
      isReply = true
    } else {
      // Fallback to positional tags (deprecated but still in use)
      const eTags = event.tags?.filter((tag: string[]) => tag[0] === 'e') || []
      if (eTags.length === 1) {
        // Single e-tag means this is a direct reply
        replyTo = eTags[0][1]
        rootId = eTags[0][1]
        isReply = true
      } else if (eTags.length >= 2) {
        // Multiple e-tags: first is root, last is direct reply
        rootId = eTags[0][1]
        replyTo = eTags[eTags.length - 1][1]
        isReply = true
      }
    }

    // Transform to FeedPost
    const post: FeedPost = {
      id: event.id,
      pubkey: event.pubkey,
      content: event.content,
      created_at: event.created_at,
      kind: event.kind,
      tags: event.tags || [],
      mentions: event.tags?.filter((tag: string[]) => tag[0] === 'p').map((tag: string[]) => tag[1]) || [],
      isReply,
      replyTo,
      rootId,
      replies: [],
      depth: 0
    }

    // Add to posts (newest first)
    this._posts.value = [post, ...this._posts.value]

    // Limit array size and clean up seen IDs
    const maxPosts = config.maxPosts || 100
    if (this._posts.value.length > maxPosts) {
      const removedPosts = this._posts.value.slice(maxPosts)
      this._posts.value = this._posts.value.slice(0, maxPosts)

      // Clean up seen IDs for removed posts
      removedPosts.forEach(post => {
        this.seenEventIds.delete(post.id)
      })
    }

    // Emit event for other modules
    eventBus.emit('nostr-feed:new-post', {
      event,
      feedType: config.feedType
    }, 'nostr-feed')
  }

  /**
   * Check if event should be included in feed
   */
  private shouldIncludeEvent(event: NostrEvent, config: FeedConfig): boolean {
    // Never include reactions (kind 7) or deletions (kind 5) in the main feed
    // These should only be processed by the ReactionService
    if (event.kind === 7 || event.kind === 5) {
      return false
    }


    const isAdminPost = config.adminPubkeys?.includes(event.pubkey) || false

    // For custom content filters, check if event matches any active filter
    if (config.feedType === 'custom' && config.contentFilters) {
      console.log('FeedService: Using custom filters, count:', config.contentFilters.length)
      const result = config.contentFilters.some(filter => {
        console.log('FeedService: Checking filter:', filter.id, 'kinds:', filter.kinds, 'filterByAuthor:', filter.filterByAuthor)

        // Check if event kind matches
        if (!filter.kinds.includes(event.kind)) {
          console.log('FeedService: Kind mismatch, event kind:', event.kind, 'filter kinds:', filter.kinds)
          return false
        }

        // Apply author filtering
        if (filter.filterByAuthor === 'admin') {
          console.log('FeedService: Admin filter, isAdminPost:', isAdminPost)
          if (!isAdminPost) return false
        } else if (filter.filterByAuthor === 'exclude-admin') {
          console.log('FeedService: Exclude admin filter, isAdminPost:', isAdminPost)
          if (isAdminPost) return false
        }

        // Apply keyword filtering if specified
        if (filter.keywords && filter.keywords.length > 0) {
          const content = event.content.toLowerCase()
          const hasMatchingKeyword = filter.keywords.some(keyword =>
            content.includes(keyword.toLowerCase())
          )
          if (!hasMatchingKeyword) {
            console.log('FeedService: No matching keywords found')
            return false
          }
        }

        // Apply tag filtering if specified (check if event has any matching tags)
        if (filter.tags && filter.tags.length > 0) {
          const eventTags = event.tags?.filter(tag => tag[0] === 't').map(tag => tag[1]) || []
          const hasMatchingTag = filter.tags.some(filterTag =>
            eventTags.includes(filterTag)
          )
          if (!hasMatchingTag) {
            console.log('FeedService: No matching tags found')
            return false
          }
        }

        console.log('FeedService: Filter passed all checks')
        return true
      })
      console.log('FeedService: Custom filter result:', result)
      return result
    }

    // Legacy feed type handling
    switch (config.feedType) {
      case 'announcements':
        return isAdminPost
      case 'general':
        return !isAdminPost
      case 'events':
        // Events feed could show all posts for now, or implement event-specific filtering
        return true
      case 'mentions':
        // TODO: Implement mention detection if needed
        return true
      case 'all':
      default:
        return true
    }
  }

  /**
   * Unsubscribe from current feed
   */
  async unsubscribeFeed(): Promise<void> {
    if (this.currentUnsubscribe) {
      this.currentUnsubscribe()
      this.currentSubscription = null
      this.currentUnsubscribe = null
      this.currentConfig = null
    }
  }

  /**
   * Refresh feed (clear and reload)
   */
  async refreshFeed(): Promise<void> {
    if (!this.currentConfig) return

    // Clear existing state
    this._posts.value = []
    this.seenEventIds.clear()

    // Resubscribe
    await this.subscribeFeed(this.currentConfig)
  }

  /**
   * Build threaded reply structure from flat posts
   */
  buildThreadedPosts(posts: FeedPost[]): FeedPost[] {
    // Create a map for quick lookup
    const postMap = new Map<string, FeedPost>()
    posts.forEach(post => {
      postMap.set(post.id, { ...post, replies: [], depth: 0 })
    })

    // Build the tree structure
    const rootPosts: FeedPost[] = []

    posts.forEach(post => {
      const currentPost = postMap.get(post.id)!

      if (post.isReply && post.replyTo) {
        // This is a reply, attach it to its parent if parent exists
        const parentPost = postMap.get(post.replyTo)
        if (parentPost) {
          currentPost.depth = (parentPost.depth || 0) + 1
          parentPost.replies = parentPost.replies || []
          parentPost.replies.push(currentPost)
          // Sort replies by timestamp (oldest first for better reading flow)
          parentPost.replies.sort((a, b) => a.created_at - b.created_at)
        } else {
          // Parent not found, treat as root post
          rootPosts.push(currentPost)
        }
      } else {
        // This is a root post
        rootPosts.push(currentPost)
      }
    })

    // Sort root posts by newest first
    rootPosts.sort((a, b) => b.created_at - a.created_at)

    return rootPosts
  }

  /**
   * Get filtered posts for specific feed type
   */
  getFilteredPosts(config: FeedConfig): FeedPost[] {
    console.log('FeedService: getFilteredPosts called, total posts:', this._posts.value.length, 'config:', config.feedType)
    const filtered = this._posts.value
      .filter(post => {
        const shouldInclude = this.shouldIncludeEvent({
          id: post.id,
          pubkey: post.pubkey,
          content: post.content,
          created_at: post.created_at,
          kind: post.kind,
          tags: post.tags
        } as NostrEvent, config)
        if (!shouldInclude) {
          console.log('FeedService: Post filtered out in getFilteredPosts:', post.id)
        }
        return shouldInclude
      })
      .sort((a, b) => b.created_at - a.created_at)
      .slice(0, config.maxPosts || 100)

    console.log('FeedService: getFilteredPosts returning', filtered.length, 'posts')
    return filtered
  }

  /**
   * Visibility service callbacks
   */
  private async onResume(): Promise<void> {
    console.log('FeedService: App resumed, checking connections')

    // Check if we need to reconnect
    if (this.currentConfig && this.relayHub) {
      const isConnected = this.relayHub.isConnected.value
      console.log('FeedService: RelayHub connection status:', isConnected)
      if (!isConnected) {
        console.log('FeedService: Reconnecting after resume')
        await this.subscribeFeed(this.currentConfig)
      }
    }
  }

  private onPause(): void {
    console.log('FeedService: App paused, maintaining state')
    // Don't clear state, just log for debugging
  }

  /**
   * Cleanup
   */
  protected async onDestroy(): Promise<void> {
    await this.unsubscribeFeed()
    this.seenEventIds.clear()
    this._posts.value = []
  }
}