import { UserGenerator } from '../generators/users'
import { AudioGenerator } from '../generators/audio'
import { ConversationGenerator } from '../generators/conversations'
import type { User, Audio, Playlist, Conversation, Message } from '../schemas/database'

/**
 * Manages relationships and consistency between generated data
 */
export class DataRelationManager {
  private static users: Map<string, User> = new Map()
  private static tracks: Map<string, Audio> = new Map()
  private static playlists: Map<string, Playlist> = new Map()
  private static conversations: Map<string, Conversation> = new Map()
  private static messages: Map<string, Message> = new Map()
  
  // Relationship mappings
  private static userTracks: Map<string, string[]> = new Map()
  private static userPlaylists: Map<string, string[]> = new Map()
  private static userConversations: Map<string, string[]> = new Map()
  private static playlistTracks: Map<string, string[]> = new Map()
  private static conversationParticipants: Map<string, string[]> = new Map()
  private static conversationMessages: Map<string, string[]> = new Map()

  /**
   * Register entities and build relationships
   */
  static registerUser(user: User): void {
    this.users.set(user.id, user)
    this.userTracks.set(user.id, [])
    this.userPlaylists.set(user.id, [])
    this.userConversations.set(user.id, [])
  }

  static registerTrack(track: Audio): void {
    this.tracks.set(track.id, track)
    
    // Link track to uploader
    const userTracks = this.userTracks.get(track.uploadedById) || []
    userTracks.push(track.id)
    this.userTracks.set(track.uploadedById, userTracks)
  }

  static registerPlaylist(playlist: Playlist): void {
    this.playlists.set(playlist.id, playlist)
    this.playlistTracks.set(playlist.id, playlist.trackIds)
    
    // Link playlist to creator
    const userPlaylists = this.userPlaylists.get(playlist.createdById) || []
    userPlaylists.push(playlist.id)
    this.userPlaylists.set(playlist.createdById, userPlaylists)
  }

  static registerConversation(conversation: Conversation): void {
    this.conversations.set(conversation.id, conversation)
    this.conversationParticipants.set(conversation.id, conversation.participantIds)
    this.conversationMessages.set(conversation.id, [])
    
    // Link conversation to participants
    conversation.participantIds.forEach(participantId => {
      const userConversations = this.userConversations.get(participantId) || []
      userConversations.push(conversation.id)
      this.userConversations.set(participantId, userConversations)
    })
  }

  static registerMessage(message: Message): void {
    this.messages.set(message.id, message)
    
    // Link message to conversation
    const conversationMessages = this.conversationMessages.get(message.conversationId) || []
    conversationMessages.push(message.id)
    this.conversationMessages.set(message.conversationId, conversationMessages)
  }

  /**
   * Generate complete dataset with consistent relationships
   */
  static generateCompleteDataset(): {
    users: User[]
    tracks: Audio[]
    playlists: Playlist[]
    conversations: Conversation[]
    messages: Message[]
  } {
    console.log('🎯 Generating complete dataset with consistent relationships...')
    
    // Clear existing data
    this.clearAll()
    
    // Step 1: Generate users
    console.log('👥 Generating users...')
    const users = UserGenerator.generateWithDistribution()
    users.forEach(user => this.registerUser(user))
    console.log(`✅ Generated ${users.length} users`)
    
    // Step 2: Generate audio content for artists and producers
    console.log('🎵 Generating audio content...')
    const tracks: Audio[] = []
    const playlists: Playlist[] = []
    
    users.forEach(user => {
      if (user.role === 'artist' || user.role === 'producer') {
        const content = AudioGenerator.generateUserContent(user.id, user.role)
        
        // Register tracks
        content.tracks.forEach(track => {
          track.uploadedById = user.id // Ensure consistency
          this.registerTrack(track)
          tracks.push(track)
        })
        
        // Register playlists
        content.playlists.forEach(playlist => {
          playlist.createdById = user.id // Ensure consistency
          // Use real track IDs for playlists
          playlist.trackIds = this.getRandomTrackIds(playlist.trackIds.length)
          this.registerPlaylist(playlist)
          playlists.push(playlist)
        })
      } else if (user.role === 'user') {
        // Regular users create playlists but don't upload tracks
        const content = AudioGenerator.generateUserContent(user.id, user.role)
        content.playlists.forEach(playlist => {
          playlist.createdById = user.id
          playlist.trackIds = this.getRandomTrackIds(playlist.trackIds.length)
          this.registerPlaylist(playlist)
          playlists.push(playlist)
        })
      }
    })
    
    console.log(`✅ Generated ${tracks.length} tracks and ${playlists.length} playlists`)
    
    // Step 3: Generate conversations
    console.log('💬 Generating conversations...')
    const userIds = users.map(u => u.id)
    const conversations = ConversationGenerator.generateWithDistribution(userIds)
    conversations.forEach(conv => this.registerConversation(conv))
    
    // Step 4: Generate messages for conversations
    console.log('📝 Generating messages...')
    const allMessages: Message[] = []
    conversations.forEach(conversation => {
      const messages = ConversationGenerator.generateMessages(
        conversation.id,
        conversation.participantIds,
        this.getMessageCountForConversation(conversation.type)
      )
      
      messages.forEach(message => {
        this.registerMessage(message)
        allMessages.push(message)
      })
      
      // Update conversation with last message info
      if (messages.length > 0) {
        const lastMessage = messages[messages.length - 1]
        conversation.lastMessageId = lastMessage.id
        conversation.lastActivityAt = lastMessage.createdAt
        conversation.updatedAt = lastMessage.createdAt
      }
    })
    
    console.log(`✅ Generated ${conversations.length} conversations with ${allMessages.length} messages`)
    
    // Step 5: Update user statistics based on generated content
    this.updateUserStatistics(users)
    
    console.log('✅ Dataset generation complete!')
    
    return {
      users,
      tracks,
      playlists,
      conversations,
      messages: allMessages
    }
  }

  /**
   * Validation methods
   */
  static validateRelations(): {
    isValid: boolean
    errors: string[]
    warnings: string[]
  } {
    const errors: string[] = []
    const warnings: string[] = []

    // Validate user-track relationships
    this.userTracks.forEach((trackIds, userId) => {
      if (!this.users.has(userId)) {
        errors.push(`User ${userId} not found but has tracks`)
      }
      
      trackIds.forEach(trackId => {
        const track = this.tracks.get(trackId)
        if (!track) {
          errors.push(`Track ${trackId} not found`)
        } else if (track.uploadedById !== userId) {
          errors.push(`Track ${trackId} uploader mismatch`)
        }
      })
    })

    // Validate playlist-track relationships
    this.playlistTracks.forEach((trackIds, playlistId) => {
      const playlist = this.playlists.get(playlistId)
      if (!playlist) {
        errors.push(`Playlist ${playlistId} not found`)
        return
      }
      
      trackIds.forEach(trackId => {
        if (!this.tracks.has(trackId)) {
          warnings.push(`Playlist ${playlistId} references non-existent track ${trackId}`)
        }
      })
    })

    // Validate conversation-participant relationships
    this.conversationParticipants.forEach((participantIds, conversationId) => {
      const conversation = this.conversations.get(conversationId)
      if (!conversation) {
        errors.push(`Conversation ${conversationId} not found`)
        return
      }
      
      participantIds.forEach(participantId => {
        if (!this.users.has(participantId)) {
          errors.push(`Conversation ${conversationId} has non-existent participant ${participantId}`)
        }
      })
    })

    // Validate message-conversation relationships
    this.conversationMessages.forEach((messageIds, conversationId) => {
      if (!this.conversations.has(conversationId)) {
        errors.push(`Messages reference non-existent conversation ${conversationId}`)
        return
      }
      
      messageIds.forEach(messageId => {
        const message = this.messages.get(messageId)
        if (!message) {
          errors.push(`Message ${messageId} not found`)
        } else if (message.conversationId !== conversationId) {
          errors.push(`Message ${messageId} conversation mismatch`)
        }
      })
    })

    return {
      isValid: errors.length === 0,
      errors,
      warnings
    }
  }

  /**
   * Query methods
   */
  static getUserTracks(userId: string): Audio[] {
    const trackIds = this.userTracks.get(userId) || []
    return trackIds.map(id => this.tracks.get(id)!).filter(Boolean)
  }

  static getUserPlaylists(userId: string): Playlist[] {
    const playlistIds = this.userPlaylists.get(userId) || []
    return playlistIds.map(id => this.playlists.get(id)!).filter(Boolean)
  }

  static getUserConversations(userId: string): Conversation[] {
    const conversationIds = this.userConversations.get(userId) || []
    return conversationIds.map(id => this.conversations.get(id)!).filter(Boolean)
  }

  static getPlaylistTracks(playlistId: string): Audio[] {
    const trackIds = this.playlistTracks.get(playlistId) || []
    return trackIds.map(id => this.tracks.get(id)!).filter(Boolean)
  }

  static getConversationMessages(conversationId: string): Message[] {
    const messageIds = this.conversationMessages.get(conversationId) || []
    return messageIds.map(id => this.messages.get(id)!).filter(Boolean)
  }

  /**
   * Statistics and insights
   */
  static getStatistics(): {
    users: number
    tracks: number
    playlists: number
    conversations: number
    messages: number
    relationships: {
      avgTracksPerArtist: number
      avgPlaylistsPerUser: number
      avgMessagesPerConversation: number
      avgParticipantsPerConversation: number
    }
  } {
    const artistCount = Array.from(this.users.values())
      .filter(u => u.role === 'artist' || u.role === 'producer').length
    
    const totalTracks = Array.from(this.userTracks.values())
      .reduce((sum, tracks) => sum + tracks.length, 0)
    
    const totalPlaylists = Array.from(this.userPlaylists.values())
      .reduce((sum, playlists) => sum + playlists.length, 0)
    
    const totalMessages = Array.from(this.conversationMessages.values())
      .reduce((sum, messages) => sum + messages.length, 0)
    
    const totalParticipants = Array.from(this.conversationParticipants.values())
      .reduce((sum, participants) => sum + participants.length, 0)

    return {
      users: this.users.size,
      tracks: this.tracks.size,
      playlists: this.playlists.size,
      conversations: this.conversations.size,
      messages: this.messages.size,
      relationships: {
        avgTracksPerArtist: artistCount > 0 ? Math.round(totalTracks / artistCount) : 0,
        avgPlaylistsPerUser: this.users.size > 0 ? Math.round(totalPlaylists / this.users.size) : 0,
        avgMessagesPerConversation: this.conversations.size > 0 ? Math.round(totalMessages / this.conversations.size) : 0,
        avgParticipantsPerConversation: this.conversations.size > 0 ? Math.round(totalParticipants / this.conversations.size) : 0,
      }
    }
  }

  /**
   * Export methods for different formats
   */
  static exportForDatabase(): {
    users: User[]
    tracks: Audio[]
    playlists: Playlist[]
    conversations: Conversation[]
    messages: Message[]
  } {
    return {
      users: Array.from(this.users.values()),
      tracks: Array.from(this.tracks.values()),
      playlists: Array.from(this.playlists.values()),
      conversations: Array.from(this.conversations.values()),
      messages: Array.from(this.messages.values())
    }
  }

  static exportForAPI(): any {
    // Transform data for API responses
    const users = Array.from(this.users.values()).map(user => ({
      ...user,
      tracks: this.getUserTracks(user.id).length,
      playlists: this.getUserPlaylists(user.id).length,
      conversations: this.getUserConversations(user.id).length
    }))

    const tracks = Array.from(this.tracks.values()).map(track => ({
      ...track,
      uploader: this.users.get(track.uploadedById)
    }))

    return { users, tracks }
  }

  /**
   * Private helper methods
   */
  private static getRandomTrackIds(count: number): string[] {
    const allTrackIds = Array.from(this.tracks.keys())
    if (allTrackIds.length === 0) return []
    
    const selectedIds: string[] = []
    for (let i = 0; i < Math.min(count, allTrackIds.length); i++) {
      const randomId = allTrackIds[Math.floor(Math.random() * allTrackIds.length)]
      if (!selectedIds.includes(randomId)) {
        selectedIds.push(randomId)
      }
    }
    
    return selectedIds
  }

  private static getMessageCountForConversation(type: string): number {
    const counts = {
      direct: Math.floor(Math.random() * 50) + 10,
      group: Math.floor(Math.random() * 100) + 20,
      channel: Math.floor(Math.random() * 200) + 50
    }
    
    return counts[type as keyof typeof counts] || 30
  }

  private static updateUserStatistics(users: User[]): void {
    users.forEach(user => {
      const tracks = this.getUserTracks(user.id)
      const playlists = this.getUserPlaylists(user.id)
      
      user.stats.tracksUploaded = tracks.length
      user.stats.playlistsCreated = playlists.length
      user.stats.totalPlays = tracks.reduce((sum, track) => sum + track.stats.plays, 0)
    })
  }

  /**
   * Cleanup methods
   */
  static clearAll(): void {
    this.users.clear()
    this.tracks.clear()
    this.playlists.clear()
    this.conversations.clear()
    this.messages.clear()
    this.userTracks.clear()
    this.userPlaylists.clear()
    this.userConversations.clear()
    this.playlistTracks.clear()
    this.conversationParticipants.clear()
    this.conversationMessages.clear()
  }

  static getAll(): {
    users: Map<string, User>
    tracks: Map<string, Audio>
    playlists: Map<string, Playlist>
    conversations: Map<string, Conversation>
    messages: Map<string, Message>
  } {
    return {
      users: this.users,
      tracks: this.tracks,
      playlists: this.playlists,
      conversations: this.conversations,
      messages: this.messages
    }
  }
}