import { ref, computed } from 'vue'
import { eventBus } from '@/core/event-bus'
import { BaseService } from '@/core/base/BaseService'
import { nip04, finalizeEvent, type Event, type EventTemplate } from 'nostr-tools'
import type { ChatMessage, ChatPeer, UnreadMessageData, ChatConfig } from '../types'
import { getAuthToken } from '@/lib/config/lnbits'
import { config } from '@/lib/config'
export class ChatService extends BaseService {
  // Service metadata
  protected readonly metadata = {
    name: 'ChatService',
    version: '1.0.0',
    dependencies: ['RelayHub', 'AuthService', 'VisibilityService', 'StorageService']
  }
  // Service-specific state
  private messages = ref<Map<string, ChatMessage[]>>(new Map())
  private peers = ref<Map<string, ChatPeer>>(new Map())
  private config: ChatConfig
  private subscriptionUnsubscriber?: () => void
  private marketMessageHandler?: (event: any) => Promise<void>
  private visibilityUnsubscribe?: () => void
  private isFullyInitialized = false
  private authCheckInterval?: ReturnType<typeof setInterval>
  constructor(config: ChatConfig) {
    super()
    this.config = config
    this.loadPeersFromStorage()
  }
  // Register market message handler for forwarding market-related DMs
  setMarketMessageHandler(handler: (event: any) => Promise<void>) {
    this.marketMessageHandler = handler
  }
  /**
   * Service-specific initialization (called by BaseService)
   */
  protected async onInitialize(): Promise<void> {
    this.debug('Chat service onInitialize called')
    // Check both injected auth service AND global auth composable
    // Removed dual auth import
    const hasAuthService = this.authService?.user?.value?.pubkey
    this.debug('Auth detection:', {
      hasAuthService: !!hasAuthService,
      authServicePubkey: hasAuthService ? hasAuthService.substring(0, 10) : 'none',
    })
    if (!this.authService?.user?.value) {
      this.debug('User not authenticated yet, deferring full initialization with periodic check')
      // Listen for auth events to complete initialization when user logs in
      const unsubscribe = eventBus.on('auth:login', async () => {
        this.debug('Auth login detected, completing chat initialization...')
        unsubscribe()
        if (this.authCheckInterval) {
          clearInterval(this.authCheckInterval)
          this.authCheckInterval = undefined
        }
        // Re-inject dependencies and complete initialization
        await this.waitForDependencies()
        await this.completeInitialization()
      })
      // Also check periodically in case we missed the auth event
      this.authCheckInterval = setInterval(async () => {
        // Removed dual auth import
        if (this.authService?.user?.value?.pubkey) {
          this.debug('Auth detected via periodic check, completing initialization')
          if (this.authCheckInterval) {
            clearInterval(this.authCheckInterval)
            this.authCheckInterval = undefined
          }
          unsubscribe()
          await this.waitForDependencies()
          await this.completeInitialization()
        }
      }, 2000) // Check every 2 seconds
      return
    }
    await this.completeInitialization()
  }
  /**
   * Complete the initialization once all dependencies are available
   */
  private async completeInitialization(): Promise<void> {
    if (this.isFullyInitialized) {
      this.debug('Chat service already fully initialized, skipping')
      return
    }
    this.debug('Completing chat service initialization...')
    // Load peers from storage first
    this.loadPeersFromStorage()
    // Load peers from API
    await this.loadPeersFromAPI().catch(error => {
      console.warn('Failed to load peers from API:', error)
    })
    // Initialize message handling (subscription + history loading)
    await this.initializeMessageHandling()
    // Register with visibility service
    this.registerWithVisibilityService()
    this.isFullyInitialized = true
    this.debug('Chat service fully initialized and ready!')
  }
  
  // Initialize message handling (subscription + history loading)
  async initializeMessageHandling(): Promise<void> {
    // Set up real-time subscription
    await this.setupMessageSubscription()
    // Load message history for known peers
    await this.loadMessageHistory()
  }
  // Computed properties
  get allPeers() {
    return computed(() => Array.from(this.peers.value.values()))
  }
  get totalUnreadCount() {
    return computed(() => {
      return Array.from(this.peers.value.values())
        .reduce((total, peer) => total + peer.unreadCount, 0)
    })
  }
  get isReady() {
    return this.isInitialized
  }
  // Get messages for a specific peer
  getMessages(peerPubkey: string): ChatMessage[] {
    return this.messages.value.get(peerPubkey) || []
  }
  // Get peer by pubkey
  getPeer(pubkey: string): ChatPeer | undefined {
    return this.peers.value.get(pubkey)
  }
  // Add or update a peer
  addPeer(pubkey: string, name?: string): ChatPeer {
    let peer = this.peers.value.get(pubkey)
    if (!peer) {
      peer = {
        pubkey,
        name: name || `User ${pubkey.slice(0, 8)}`,
        unreadCount: 0,
        lastSeen: Date.now()
      }
      this.peers.value.set(pubkey, peer)
      this.savePeersToStorage()
      eventBus.emit('chat:peer-added', { peer }, 'chat-service')
    } else if (name && name !== peer.name) {
      peer.name = name
      this.savePeersToStorage()
    }
    return peer
  }
  // Add a message
  addMessage(peerPubkey: string, message: ChatMessage): void {
    if (!this.messages.value.has(peerPubkey)) {
      this.messages.value.set(peerPubkey, [])
    }
    const peerMessages = this.messages.value.get(peerPubkey)!
    // Avoid duplicates
    if (!peerMessages.some(m => m.id === message.id)) {
      peerMessages.push(message)
      // Sort by timestamp
      peerMessages.sort((a, b) => a.created_at - b.created_at)
      // Limit message count
      if (peerMessages.length > this.config.maxMessages) {
        peerMessages.splice(0, peerMessages.length - this.config.maxMessages)
      }
      // Update peer info
      const peer = this.addPeer(peerPubkey)
      peer.lastMessage = message
      peer.lastSeen = Date.now()
      // Update unread count if message is not sent by us
      if (!message.sent) {
        this.updateUnreadCount(peerPubkey, message)
      }
      // Emit events
      const eventType = message.sent ? 'chat:message-sent' : 'chat:message-received'
      eventBus.emit(eventType, { message, peerPubkey }, 'chat-service')
    }
  }
  // Mark messages as read for a peer
  markAsRead(peerPubkey: string): void {
    const peer = this.peers.value.get(peerPubkey)
    if (peer && peer.unreadCount > 0) {
      peer.unreadCount = 0
      // Save unread state
      const unreadData: UnreadMessageData = {
        lastReadTimestamp: Date.now(),
        unreadCount: 0,
        processedMessageIds: new Set()
      }
      this.saveUnreadData(peerPubkey, unreadData)
      eventBus.emit('chat:unread-count-changed', {
        peerPubkey,
        count: 0,
        totalUnread: this.totalUnreadCount.value
      }, 'chat-service')
    }
  }
  // Refresh peers from API
  async refreshPeers(): Promise<void> {
    // Check if we should trigger full initialization
    // Removed dual auth import
    const hasAuth = this.authService?.user?.value?.pubkey
    if (!this.isFullyInitialized && hasAuth) {
      console.log('💬 Refresh peers triggered full initialization')
      await this.completeInitialization()
    }
    return this.loadPeersFromAPI()
  }
  // Check if services are available for messaging
  private async checkServicesAvailable(): Promise<{ relayHub: any; authService: any; userPubkey: string; userPrivkey: string } | null> {
    // Check both injected auth service AND global auth composable
    // Removed dual auth import
    const userPubkey = this.authService?.user?.value?.pubkey
    const userPrivkey = this.authService?.user?.value?.prvkey
    if (!this.relayHub || (!this.authService?.user?.value)) {
      return null
    }
    if (!this.relayHub.isConnected) {
      return null
    }
    return { 
      relayHub: this.relayHub, 
      authService: this.authService,
      userPubkey: userPubkey!,
      userPrivkey: userPrivkey!
    }
  }
  // Send a message
  async sendMessage(peerPubkey: string, content: string): Promise<void> {
    try {
      const services = await this.checkServicesAvailable()
      if (!services) {
        throw new Error('Chat services not ready. Please wait for connection to establish.')
      }
      const { relayHub, userPrivkey, userPubkey } = services
      // Encrypt the message using NIP-04
      const encryptedContent = await nip04.encrypt(userPrivkey, peerPubkey, content)
      // Create Nostr event for the encrypted message (kind 4 = encrypted direct message)
      const eventTemplate: EventTemplate = {
        kind: 4,
        created_at: Math.floor(Date.now() / 1000),
        tags: [['p', peerPubkey]],
        content: encryptedContent
      }
      // Finalize the event with signature  
      const privkeyBytes = this.hexToUint8Array(userPrivkey)
      const signedEvent = finalizeEvent(eventTemplate, privkeyBytes)
      // Create local message for immediate display
      const message: ChatMessage = {
        id: signedEvent.id,
        content,
        created_at: signedEvent.created_at,
        sent: true,
        pubkey: userPubkey
      }
      // Add to local messages immediately
      this.addMessage(peerPubkey, message)
      // Publish to Nostr relays
      const result = await relayHub.publishEvent(signedEvent)
      console.log('Message published to relays:', { success: result.success, total: result.total })
    } catch (error) {
      console.error('Failed to send message:', error)
      throw error
    }
  }
  // Private methods
  
  /**
   * Convert hex string to Uint8Array (browser-compatible)
   */
  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
  }

  private updateUnreadCount(peerPubkey: string, message: ChatMessage): void {
    const unreadData = this.getUnreadData(peerPubkey)
    if (!unreadData.processedMessageIds.has(message.id)) {
      unreadData.processedMessageIds.add(message.id)
      unreadData.unreadCount++
      const peer = this.peers.value.get(peerPubkey)
      if (peer) {
        peer.unreadCount = unreadData.unreadCount
        this.savePeersToStorage()
      }
      this.saveUnreadData(peerPubkey, unreadData)
      eventBus.emit('chat:unread-count-changed', {
        peerPubkey,
        count: unreadData.unreadCount,
        totalUnread: this.totalUnreadCount.value
      }, 'chat-service')
    }
  }
  private getUnreadData(peerPubkey: string): UnreadMessageData {
    const data = this.storageService.getUserData(`chat-unread-messages-${peerPubkey}`, {
      lastReadTimestamp: 0,
      unreadCount: 0,
      processedMessageIds: []
    })
    return {
      ...data,
      processedMessageIds: new Set(data.processedMessageIds || [])
    }
  }
  private saveUnreadData(peerPubkey: string, data: UnreadMessageData): void {
    const serializable = {
      ...data,
      processedMessageIds: Array.from(data.processedMessageIds)
    }
    this.storageService.setUserData(`chat-unread-messages-${peerPubkey}`, serializable)
  }
  // Load peers from API
  async loadPeersFromAPI(): Promise<void> {
    try {
      const authToken = getAuthToken()
      if (!authToken) {
        console.warn('💬 No authentication token found for loading peers from API')
        throw new Error('No authentication token found')
      }
      const API_BASE_URL = config.api.baseUrl || 'http://localhost:5006'
      console.log('💬 Loading peers from API:', `${API_BASE_URL}/api/v1/auth/nostr/pubkeys`)
      const response = await fetch(`${API_BASE_URL}/api/v1/auth/nostr/pubkeys`, {
        headers: {
          'Authorization': `Bearer ${authToken}`,
          'Content-Type': 'application/json'
        }
      })
      if (!response.ok) {
        const errorText = await response.text()
        console.error('💬 API response error:', response.status, errorText)
        throw new Error(`Failed to load peers: ${response.status} - ${errorText}`)
      }
      const data = await response.json()
      console.log('💬 API returned', data?.length || 0, 'peers')
      if (!Array.isArray(data)) {
        console.warn('💬 Invalid API response format - expected array, got:', typeof data)
        return
      }
      // Don't clear existing peers - merge instead
      data.forEach((peer: any) => {
        if (!peer.pubkey) {
          console.warn('💬 Skipping peer without pubkey:', peer)
          return
        }
        const chatPeer: ChatPeer = {
          pubkey: peer.pubkey,
          name: peer.username || `User ${peer.pubkey.slice(0, 8)}`,
          unreadCount: 0,
          lastSeen: Date.now()
        }
        this.peers.value.set(peer.pubkey, chatPeer)
      })
      // Save to storage
      this.savePeersToStorage()
      console.log(`✅ Loaded ${data.length} peers from API, total peers now: ${this.peers.value.size}`)
    } catch (error) {
      console.error('❌ Failed to load peers from API:', error)
      // Don't re-throw - peers from storage are still available
    }
  }
  private loadPeersFromStorage(): void {
    // Skip loading peers in constructor as StorageService may not be available yet
    // This will be called later during initialization when dependencies are ready
    if (!this.isInitialized.value || !this.storageService) {
      this.debug('Skipping peer loading from storage - not initialized or storage unavailable')
      return
    }
    try {
      const peersArray = this.storageService.getUserData('chat-peers', []) as ChatPeer[]
      console.log('💬 Loading', peersArray.length, 'peers from storage')
      peersArray.forEach(peer => {
        this.peers.value.set(peer.pubkey, peer)
      })
    } catch (error) {
      console.warn('💬 Failed to load peers from storage:', error)
    }
  }
  private savePeersToStorage(): void {
    const peersArray = Array.from(this.peers.value.values())
    this.storageService.setUserData('chat-peers', peersArray)
  }
  // Load message history for known peers
  private async loadMessageHistory(): Promise<void> {
    try {
      // Check both injected auth service AND global auth composable
      // Removed dual auth import
      // const hasAuthService = this.authService?.user?.value?.pubkey
      const userPubkey = this.authService?.user?.value?.pubkey
      const userPrivkey = this.authService?.user?.value?.prvkey
      if (!this.relayHub || (!this.authService?.user?.value)) {
        console.warn('Cannot load message history: missing services')
        return
      }
      if (!userPubkey || !userPrivkey) {
        console.warn('Cannot load message history: missing user keys')
        return
      }
      const peerPubkeys = Array.from(this.peers.value.keys())
      if (peerPubkeys.length === 0) {
        console.log('No peers to load message history for')
        return
      }
      console.log('Loading message history for', peerPubkeys.length, 'peers')
      // Query historical messages (kind 4) to/from known peers
      // We need separate queries for sent vs received messages due to different tagging
      const receivedEvents = await this.relayHub.queryEvents([
        {
          kinds: [4],
          authors: peerPubkeys, // Messages from peers
          '#p': [userPubkey], // Messages tagged to us
          limit: 100
        }
      ])
      const sentEvents = await this.relayHub.queryEvents([
        {
          kinds: [4], 
          authors: [userPubkey], // Messages from us
          '#p': peerPubkeys, // Messages tagged to peers
          limit: 100
        }
      ])
      const events = [...receivedEvents, ...sentEvents]
        .sort((a, b) => a.created_at - b.created_at) // Sort by timestamp
      console.log('Found', events.length, 'historical messages:', receivedEvents.length, 'received,', sentEvents.length, 'sent')
      // Process historical messages
      for (const event of events) {
        try {
          const isFromUs = event.pubkey === userPubkey
          const peerPubkey = isFromUs 
            ? event.tags.find((tag: string[]) => tag[0] === 'p')?.[1] // Get recipient from tag
            : event.pubkey // Sender is the peer
          if (!peerPubkey || peerPubkey === userPubkey) continue
          // Decrypt the message
          const decryptedContent = await nip04.decrypt(userPrivkey, peerPubkey, event.content)
          // Create a chat message
          const message: ChatMessage = {
            id: event.id,
            content: decryptedContent,
            created_at: event.created_at,
            sent: isFromUs,
            pubkey: event.pubkey
          }
          // Add the message (will avoid duplicates)
          this.addMessage(peerPubkey, message)
        } catch (error) {
          console.error('Failed to decrypt historical message:', error)
        }
      }
      console.log('Message history loaded successfully')
    } catch (error) {
      console.error('Failed to load message history:', error)
    }
  }
  // Setup subscription for incoming messages
  private async setupMessageSubscription(): Promise<void> {
    try {
      // Check both injected auth service AND global auth composable
      // Removed dual auth import
      // const hasAuthService = this.authService?.user?.value?.pubkey
      const userPubkey = this.authService?.user?.value?.pubkey
      this.debug('Setup message subscription auth check:', {
        hasAuthService: !!this.authService?.user?.value,
        hasRelayHub: !!this.relayHub,
        relayHubConnected: this.relayHub?.isConnected,
        userPubkey: userPubkey ? userPubkey.substring(0, 10) : 'none',
      })
      if (!this.relayHub || (!this.authService?.user?.value)) {
        console.warn('💬 Cannot setup message subscription: missing services')
        // Retry after 2 seconds
        setTimeout(() => this.setupMessageSubscription(), 2000)
        return
      }
      if (!this.relayHub.isConnected) {
        console.warn('💬 RelayHub not connected, waiting for connection...')
        // Listen for connection event
        this.relayHub.on('connected', () => {
          console.log('💬 RelayHub connected, setting up message subscription...')
          this.setupMessageSubscription()
        })
        // Also retry after timeout in case event is missed
        setTimeout(() => this.setupMessageSubscription(), 5000)
        return
      }
      if (!userPubkey) {
        console.warn('💬 No user pubkey available for subscription')
        setTimeout(() => this.setupMessageSubscription(), 2000)
        return
      }
      // Subscribe to encrypted direct messages (kind 4) addressed to this user
      this.subscriptionUnsubscriber = this.relayHub.subscribe({
        id: 'chat-messages',
        filters: [
          {
            kinds: [4], // Encrypted direct messages
            '#p': [userPubkey] // Messages tagged with our pubkey
          }
        ],
        onEvent: async (event: Event) => {
          // Skip our own messages
          if (event.pubkey === userPubkey) {
            return
          }
          await this.processIncomingMessage(event)
        },
        onEose: () => {
          console.log('💬 Chat message subscription EOSE received')
        }
      })
      console.log('💬 Chat message subscription set up successfully for pubkey:', userPubkey.substring(0, 10) + '...')
    } catch (error) {
      console.error('💬 Failed to setup message subscription:', error)
      // Retry after delay
      setTimeout(() => this.setupMessageSubscription(), 3000)
    }
  }
  /**
   * Register with VisibilityService for connection management
   */
  private registerWithVisibilityService(): void {
    if (!this.visibilityService) {
      this.debug('VisibilityService not available')
      return
    }
    this.visibilityUnsubscribe = this.visibilityService.registerService(
      this.metadata.name,
      async () => this.handleAppResume(),
      async () => this.handleAppPause()
    )
    this.debug('Registered with VisibilityService')
  }
  /**
   * Handle app resuming from visibility change
   */
  private async handleAppResume(): Promise<void> {
    this.debug('App resumed - checking chat connections')
    // Check if subscription is still active
    if (!this.subscriptionUnsubscriber) {
      this.debug('Chat subscription lost, re-establishing...')
      this.setupMessageSubscription()
    }
    // Check if we need to sync missed messages
    await this.syncMissedMessages()
  }
  /**
   * Handle app pausing from visibility change
   */
  private async handleAppPause(): Promise<void> {
    this.debug('App paused - chat subscription will be maintained for quick resume')
    // Don't immediately unsubscribe - let RelayHub handle connection management
    // Subscriptions will be restored automatically on resume if needed
  }
  /**
   * Sync any messages that might have been missed while app was hidden
   */
  private async syncMissedMessages(): Promise<void> {
    try {
      // For each peer, try to load recent messages
      const peers = Array.from(this.peers.value.values())
      const syncPromises = peers.map(peer => this.loadRecentMessagesForPeer(peer.pubkey))
      await Promise.allSettled(syncPromises)
      this.debug('Missed messages sync completed')
    } catch (error) {
      console.warn('Failed to sync missed messages:', error)
    }
  }
  /**
   * Process an incoming message event
   */
  private async processIncomingMessage(event: any): Promise<void> {
    try {
      // Check both injected auth service AND global auth composable
      // Removed dual auth import
      // const hasAuthService = this.authService?.user?.value?.pubkey
      const userPubkey = this.authService?.user?.value?.pubkey
      const userPrivkey = this.authService?.user?.value?.prvkey
      if (!userPubkey || !userPrivkey) {
        console.warn('Cannot process message: user not authenticated')
        return
      }
      // Get sender pubkey from event
      const senderPubkey = event.pubkey
      // Decrypt the message content
      const decryptedContent = await nip04.decrypt(userPrivkey, senderPubkey, event.content)
      // Check if this is a market-related message
      let isMarketMessage = false
      try {
        const parsedContent = JSON.parse(decryptedContent)
        if (parsedContent.type === 1 || parsedContent.type === 2) {
          // This is a market order message
          isMarketMessage = true
          // Forward to market handler
          if (this.marketMessageHandler) {
            await this.marketMessageHandler(event)
            console.log('💬 Market message forwarded to market handler and will also be added to chat')
          } else {
            console.warn('Market message handler not available, message will be treated as chat')
          }
        }
      } catch (e) {
        // Not JSON or not a market message, treat as regular chat
      }
      // Process as chat message regardless (market messages should also appear in chat)
      {
        // Format the content for display based on whether it's a market message
        let displayContent = decryptedContent
        if (isMarketMessage) {
          try {
            const parsedContent = JSON.parse(decryptedContent)
            if (parsedContent.type === 1) {
              // Payment request
              displayContent = `💰 Payment Request for Order ${parsedContent.id}\n${parsedContent.message || 'Please pay to proceed with your order'}`
            } else if (parsedContent.type === 2) {
              // Order status update
              const status = []
              if (parsedContent.paid === true) status.push('✅ Paid')
              else if (parsedContent.paid === false) status.push('⏳ Payment Pending')
              if (parsedContent.shipped === true) status.push('📦 Shipped')
              else if (parsedContent.shipped === false) status.push('🔄 Processing')
              displayContent = `📋 Order Update: ${parsedContent.id}\n${status.join(' | ')}\n${parsedContent.message || ''}`
            }
          } catch (e) {
            // Fallback to raw content if parsing fails
          }
        }
        // Create a chat message
        const message: ChatMessage = {
          id: event.id,
          content: displayContent,
          created_at: event.created_at,
          sent: false,
          pubkey: senderPubkey
        }
        // Ensure we have a peer record for the sender
        this.addPeer(senderPubkey)
        // Add the message
        this.addMessage(senderPubkey, message)
        console.log('Received encrypted chat message from:', senderPubkey.slice(0, 8))
      }
    } catch (error) {
      console.error('Failed to process incoming message:', error)
    }
  }
  /**
   * Load recent messages for a specific peer
   */
  private async loadRecentMessagesForPeer(peerPubkey: string): Promise<void> {
    // Check both injected auth service AND global auth composable
    // Removed dual auth import
    const userPubkey = this.authService?.user?.value?.pubkey
    if (!userPubkey || !this.relayHub) return
    try {
      // Get last 10 messages from the last hour for this peer
      const oneHourAgo = Math.floor(Date.now() / 1000) - 3600
      const events = await this.relayHub.queryEvents([
        {
          kinds: [4], // Encrypted DMs
          authors: [peerPubkey],
          '#p': [userPubkey],
          since: oneHourAgo,
          limit: 10
        },
        {
          kinds: [4], // Encrypted DMs  
          authors: [userPubkey],
          '#p': [peerPubkey],
          since: oneHourAgo,
          limit: 10
        }
      ])
      // Process any new messages
      for (const event of events) {
        await this.processIncomingMessage(event)
      }
    } catch (error) {
      this.debug(`Failed to load recent messages for peer ${peerPubkey.slice(0, 8)}:`, error)
    }
  }
  /**
   * Cleanup when service is disposed (overrides BaseService)
   */
  protected async onDispose(): Promise<void> {
    // Clear auth check interval
    if (this.authCheckInterval) {
      clearInterval(this.authCheckInterval)
      this.authCheckInterval = undefined
    }
    // Unregister from visibility service
    if (this.visibilityUnsubscribe) {
      this.visibilityUnsubscribe()
      this.visibilityUnsubscribe = undefined
    }
    // Unsubscribe from message subscription
    if (this.subscriptionUnsubscriber) {
      this.subscriptionUnsubscriber()
      this.subscriptionUnsubscriber = undefined
    }
    this.messages.value.clear()
    this.peers.value.clear()
    this.isFullyInitialized = false
    this.debug('Chat service disposed')
  }
  /**
   * Legacy destroy method for backward compatibility
   */
  destroy(): void {
    this.dispose()
  }
}