import { v4 as uuidv4 } from 'uuid'
import { vezaFaker } from '../utils/faker-config'
import { globalConfig } from '../config'
import type { 
  Conversation, 
  ConversationType,
  Message,
  MessageType,
  MessageStatus
} from '../schemas/database'

/**
 * Conversation generation options
 */
export interface ConversationGenerationOptions {
  type?: ConversationType
  participantIds?: string[]
  createdById?: string
  messageCount?: number
  isPrivate?: boolean
  withAvatar?: boolean
  timeSpan?: 'recent' | 'week' | 'month' | 'year'
}

/**
 * Message generation options
 */
export interface MessageGenerationOptions {
  type?: MessageType
  senderId?: string
  conversationId?: string
  withAttachments?: boolean
  withReactions?: boolean
  withMentions?: boolean
  isReply?: boolean
  parentMessageId?: string
  timeSpan?: 'recent' | 'week' | 'month'
}

/**
 * Conversation scenario for realistic data generation
 */
export interface ConversationScenario {
  name: string
  participantCount: number
  messageCount: number
  timeSpan: 'recent' | 'week' | 'month'
  messageTypes: MessageType[]
  topics: string[]
}

/**
 * Advanced conversation and messaging generator for Veza Platform
 */
export class ConversationGenerator {
  private static generatedConversations: Map<string, Conversation> = new Map()
  private static generatedMessages: Map<string, Message> = new Map()
  private static conversationMessages: Map<string, string[]> = new Map()

  /**
   * Generate a single conversation
   */
  static generateConversation(options: ConversationGenerationOptions = {}): Conversation {
    const type = options.type || vezaFaker.helpers.arrayElement(['direct', 'group', 'channel'])
    const participantIds = options.participantIds || this.generateParticipantIds(type)
    const createdById = options.createdById || vezaFaker.helpers.arrayElement(participantIds)
    const createdAt = this.generateCreationDate(options.timeSpan)
    
    const conversation: Conversation = {
      id: uuidv4(),
      name: type === 'direct' ? undefined : vezaFaker.chat.conversationName(type),
      description: type === 'channel' ? this.generateChannelDescription() : undefined,
      type,
      isPrivate: options.isPrivate ?? this.shouldBePrivate(type),
      avatarUrl: options.withAvatar !== false && type !== 'direct' ? 
        vezaFaker.utils.fileUrl('image', 'conversation-avatar.jpg') : undefined,
      createdById,
      participantIds,
      lastMessageId: undefined, // Will be set after generating messages
      lastActivityAt: createdAt,
      createdAt,
      updatedAt: createdAt,
    }

    // Generate messages for this conversation
    if (options.messageCount !== 0) {
      const messageCount = options.messageCount || this.getDefaultMessageCount(type)
      const messages = this.generateMessages(conversation.id, participantIds, messageCount, {
        timeSpan: options.timeSpan
      })
      
      if (messages.length > 0) {
        const lastMessage = messages[messages.length - 1]
        conversation.lastMessageId = lastMessage.id
        conversation.lastActivityAt = lastMessage.createdAt
        conversation.updatedAt = lastMessage.createdAt
      }
    }

    this.generatedConversations.set(conversation.id, conversation)
    return conversation
  }

  /**
   * Generate multiple conversations
   */
  static generateBatch(count: number, options: ConversationGenerationOptions = {}): Conversation[] {
    const conversations: Conversation[] = []
    
    for (let i = 0; i < count; i++) {
      conversations.push(this.generateConversation(options))
    }
    
    return conversations
  }

  /**
   * Generate conversations with realistic distribution
   */
  static generateWithDistribution(userIds: string[]): Conversation[] {
    const config = globalConfig.generation.conversations
    const conversations: Conversation[] = []

    // Generate direct conversations
    for (let i = 0; i < config.directCount; i++) {
      const participantIds = vezaFaker.helpers.arrayElements(userIds, 2)
      conversations.push(this.generateConversation({
        type: 'direct',
        participantIds,
        createdById: participantIds[0] || uuidv4(),
        timeSpan: vezaFaker.helpers.arrayElement(['recent', 'week', 'month'])
      }))
    }

    // Generate group conversations
    for (let i = 0; i < config.groupCount; i++) {
      const participantCount = vezaFaker.number.int({ min: 3, max: 15 })
      const participantIds = vezaFaker.helpers.arrayElements(userIds, participantCount)
      conversations.push(this.generateConversation({
        type: 'group',
        participantIds,
        createdById: participantIds[0] || uuidv4(),
        timeSpan: vezaFaker.helpers.arrayElement(['week', 'month', 'year'])
      }))
    }

    // Generate channels
    for (let i = 0; i < config.channelCount; i++) {
      const participantCount = vezaFaker.number.int({ min: 10, max: Math.min(50, userIds.length) })
      const participantIds = vezaFaker.helpers.arrayElements(userIds, participantCount)
      conversations.push(this.generateConversation({
        type: 'channel',
        participantIds,
        createdById: participantIds[0] || uuidv4(),
        isPrivate: vezaFaker.datatype.boolean({ probability: 0.3 }),
        timeSpan: 'year'
      }))
    }

    return conversations
  }

  /**
   * Generate messages for a conversation
   */
  static generateMessages(
    conversationId: string, 
    participantIds: string[], 
    count: number,
    options: Omit<MessageGenerationOptions, 'conversationId'> = {}
  ): Message[] {
    const messages: Message[] = []
    const messageIds: string[] = []
    const timeSpan = options.timeSpan || 'month'
    const startDate = this.getTimeSpanStart(timeSpan)
    const endDate = new Date()
    
    // Generate conversation flow with realistic patterns
    let currentSender = vezaFaker.helpers.arrayElement(participantIds)
    let lastMessageTime = startDate
    let conversationMomentum = 0.5 // How active the conversation is
    
    for (let i = 0; i < count; i++) {
      // Determine message timing
      const timeSinceLastMessage = this.generateMessageDelay(conversationMomentum, timeSpan)
      const messageTime = new Date(lastMessageTime.getTime() + timeSinceLastMessage)
      
      if (messageTime > endDate) break
      
      // Determine sender (realistic conversation flow)
      const senderChangeProb = this.getSenderChangeprobability(i, conversationMomentum)
      if (vezaFaker.datatype.boolean({ probability: senderChangeProb })) {
        currentSender = vezaFaker.helpers.arrayElement(
          participantIds.filter(id => id !== currentSender)
        )
      }
      
      // Generate message
      const message = this.generateMessage({
        conversationId,
        senderId: currentSender,
        withAttachments: vezaFaker.datatype.boolean({ probability: 0.1 }),
        withReactions: vezaFaker.datatype.boolean({ probability: 0.3 }),
        withMentions: participantIds.length > 2 && vezaFaker.datatype.boolean({ probability: 0.15 }),
        isReply: i > 0 && vezaFaker.datatype.boolean({ probability: 0.1 }),
        parentMessageId: i > 0 && vezaFaker.datatype.boolean({ probability: 0.1 }) ? 
          vezaFaker.helpers.arrayElement(messageIds) : undefined
      })
      
      // Set realistic timestamp
      message.createdAt = messageTime
      message.updatedAt = messageTime
      
      messages.push(message)
      messageIds.push(message.id)
      lastMessageTime = messageTime
      
      // Update conversation momentum
      conversationMomentum = this.updateConversationMomentum(conversationMomentum, i, count)
    }
    
    // Store message IDs for this conversation
    this.conversationMessages.set(conversationId, messageIds)
    
    return messages
  }

  /**
   * Generate a single message
   */
  static generateMessage(options: MessageGenerationOptions = {}): Message {
    const type = options.type || 'text'
    const senderId = options.senderId || uuidv4()
    const conversationId = options.conversationId || uuidv4()
    const createdAt = new Date()
    
    const message: Message = {
      id: uuidv4(),
      conversationId,
      senderId,
      content: this.generateMessageContent(type),
      type,
      status: 'delivered' as MessageStatus,
      parentMessageId: options.parentMessageId,
      attachments: options.withAttachments ? this.generateAttachments() : [],
      reactions: options.withReactions ? this.generateReactions() : [],
      mentions: options.withMentions ? this.generateMentions() : [],
      isEdited: vezaFaker.datatype.boolean({ probability: 0.05 }),
      isDeleted: false,
      createdAt,
      updatedAt: createdAt,
    }

    this.generatedMessages.set(message.id, message)
    return message
  }

  /**
   * Generate conversation scenarios
   */
  static generateScenario(scenario: ConversationScenario, userIds: string[]): Conversation {
    const participantIds = vezaFaker.helpers.arrayElements(userIds, scenario.participantCount)
    
    const conversation = this.generateConversation({
      type: scenario.participantCount === 2 ? 'direct' : 'group',
      participantIds,
      messageCount: 0, // We'll generate messages separately
      timeSpan: scenario.timeSpan
    })

    // Generate scenario-specific messages
    const messages = this.generateScenarioMessages(
      conversation.id,
      participantIds,
      scenario
    )

    if (messages.length > 0) {
      const lastMessage = messages[messages.length - 1]
      conversation.lastMessageId = lastMessage.id
      conversation.lastActivityAt = lastMessage.createdAt
      conversation.updatedAt = lastMessage.createdAt
    }

    return conversation
  }

  /**
   * Predefined conversation scenarios
   */
  static readonly SCENARIOS: ConversationScenario[] = [
    {
      name: 'Studio Collaboration',
      participantCount: 4,
      messageCount: 50,
      timeSpan: 'week',
      messageTypes: ['text', 'audio', 'file'],
      topics: ['recording', 'mixing', 'feedback', 'scheduling']
    },
    {
      name: 'Music Discovery',
      participantCount: 6,
      messageCount: 30,
      timeSpan: 'month',
      messageTypes: ['text'],
      topics: ['recommendations', 'new releases', 'concerts', 'reviews']
    },
    {
      name: 'Event Planning',
      participantCount: 8,
      messageCount: 80,
      timeSpan: 'month',
      messageTypes: ['text', 'image', 'file'],
      topics: ['venue', 'lineup', 'promotion', 'logistics']
    },
    {
      name: 'Producer Feedback',
      participantCount: 3,
      messageCount: 25,
      timeSpan: 'week',
      messageTypes: ['text', 'audio'],
      topics: ['mix notes', 'arrangement', 'sound design', 'mastering']
    }
  ]

  /**
   * Private helper methods
   */
  private static generateParticipantIds(type: ConversationType): string[] {
    const counts = {
      direct: 2,
      group: vezaFaker.number.int({ min: 3, max: 15 }),
      channel: vezaFaker.number.int({ min: 10, max: 50 })
    }
    
    const count = counts[type]
    return Array.from({ length: count }, () => uuidv4())
  }

  private static shouldBePrivate(type: ConversationType): boolean {
    const probabilities = {
      direct: 0.8,
      group: 0.4,
      channel: 0.2
    }
    
    return vezaFaker.datatype.boolean({ probability: probabilities[type] })
  }

  private static getDefaultMessageCount(type: ConversationType): number {
    const ranges = {
      direct: { min: 10, max: 100 },
      group: { min: 20, max: 200 },
      channel: { min: 50, max: 500 }
    }
    
    const range = ranges[type]
    return vezaFaker.number.int(range)
  }

  private static generateCreationDate(timeSpan?: string): Date {
    const spans = {
      recent: { days: 7 },
      week: { days: 30 },
      month: { days: 90 },
      year: { days: 365 }
    }
    
    const span = spans[timeSpan as keyof typeof spans] || spans.month
    return vezaFaker.date.recent(span)
  }

  private static generateChannelDescription(): string {
    const descriptions = [
      'Canal pour les discussions générales de la communauté',
      'Espace dédié aux annonces importantes',
      'Partage de musique et découvertes',
      'Discussions techniques et production musicale',
      'Feedback et critiques constructives',
      'Organisation d\'événements et concerts',
      'Collaborations entre artistes',
      'Support et entraide communautaire'
    ]
    
    return vezaFaker.helpers.arrayElement(descriptions)
  }

  private static generateMessageContent(type: MessageType): string {
    if (type === 'system') {
      return vezaFaker.chat.messageContent('system')
    }
    
    if (type === 'audio') {
      return 'Message audio'
    }
    
    if (type === 'image') {
      return 'Image partagée'
    }
    
    if (type === 'file') {
      return 'Fichier partagé'
    }
    
    return vezaFaker.chat.messageContent('text')
  }

  private static generateAttachments() {
    const attachmentTypes = ['image', 'audio', 'file'] as const
    const type = vezaFaker.helpers.arrayElement(attachmentTypes)
    
    return [{
      id: uuidv4(),
      type,
      url: vezaFaker.utils.fileUrl(type === 'image' ? 'image' : 'audio'),
      filename: `attachment.${type === 'image' ? 'jpg' : type === 'audio' ? 'mp3' : 'pdf'}`,
      size: vezaFaker.number.int({ min: 1024, max: 10485760 }), // 1KB to 10MB
      mimeType: type === 'image' ? 'image/jpeg' : type === 'audio' ? 'audio/mpeg' : 'application/pdf'
    }]
  }

  private static generateReactions() {
    const reactionCount = vezaFaker.number.int({ min: 1, max: 5 })
    const reactions = []
    
    for (let i = 0; i < reactionCount; i++) {
      reactions.push({
        emoji: vezaFaker.chat.reactionEmoji(),
        userId: uuidv4(),
        createdAt: vezaFaker.date.recent({ days: 1 })
      })
    }
    
    return reactions
  }

  private static generateMentions(): string[] {
    const mentionCount = vezaFaker.number.int({ min: 1, max: 3 })
    return Array.from({ length: mentionCount }, () => uuidv4())
  }

  private static getTimeSpanStart(timeSpan: string): Date {
    const now = new Date()
    const spans = {
      recent: 7 * 24 * 60 * 60 * 1000, // 7 days
      week: 30 * 24 * 60 * 60 * 1000, // 30 days
      month: 90 * 24 * 60 * 60 * 1000 // 90 days
    }
    
    const span = spans[timeSpan as keyof typeof spans] || spans.month
    return new Date(now.getTime() - span)
  }

  private static generateMessageDelay(momentum: number, timeSpan: string): number {
    const baseDelays = {
      recent: { min: 60000, max: 3600000 }, // 1 minute to 1 hour
      week: { min: 3600000, max: 86400000 }, // 1 hour to 1 day
      month: { min: 86400000, max: 604800000 } // 1 day to 1 week
    }
    
    const delays = baseDelays[timeSpan as keyof typeof baseDelays] || baseDelays.month
    const momentumFactor = 1 / (momentum + 0.1) // Higher momentum = shorter delays
    
    return vezaFaker.number.int({
      min: delays.min * momentumFactor,
      max: delays.max * momentumFactor
    })
  }

  private static getSenderChangeProability(messageIndex: number, momentum: number): number {
    // Higher momentum and later in conversation = more likely to change sender
    const baseProbability = 0.3
    const momentumBonus = momentum * 0.3
    const indexBonus = Math.min(messageIndex / 10, 0.2)
    
    return Math.min(baseProbability + momentumBonus + indexBonus, 0.8)
  }

  private static updateConversationMomentum(
    currentMomentum: number, 
    messageIndex: number, 
    totalMessages: number
  ): number {
    // Conversations tend to be more active in the middle
    const progress = messageIndex / totalMessages
    const peakActivity = 0.3 + 0.4 * Math.sin(progress * Math.PI)
    const randomVariation = vezaFaker.number.float({ min: -0.1, max: 0.1 })
    
    return Math.max(0.1, Math.min(1, peakActivity + randomVariation))
  }

  private static generateScenarioMessages(
    conversationId: string,
    participantIds: string[],
    scenario: ConversationScenario
  ): Message[] {
    const messages: Message[] = []
    const topicMessages = this.getTopicMessages(scenario.topics)
    
    for (let i = 0; i < scenario.messageCount; i++) {
      const messageType = vezaFaker.helpers.arrayElement(scenario.messageTypes)
      const senderId = vezaFaker.helpers.arrayElement(participantIds)
      const content = vezaFaker.helpers.arrayElement(topicMessages)
      
      const message = this.generateMessage({
        conversationId,
        senderId,
        type: messageType,
      })
      
      message.content = content
      messages.push(message)
    }
    
    return messages
  }

  private static getTopicMessages(topics: string[]): string[] {
    const messagesByTopic: Record<string, string[]> = {
      recording: [
        'On peut programmer la session pour demain ?',
        'J\'ai préparé les pistes de base',
        'Le studio est libre à partir de 14h',
        'N\'oubliez pas vos instruments !'
      ],
      mixing: [
        'Le mix sonne vraiment bien maintenant',
        'On pourrait baisser un peu les graves ?',
        'Cette reverb est parfaite',
        'Qu\'est-ce que vous pensez du niveau vocal ?'
      ],
      feedback: [
        'Excellent travail sur cette composition !',
        'La mélodie est accrocheuse',
        'Peut-être ajouter plus de variation dans le refrain ?',
        'Cette section groove vraiment bien'
      ],
      recommendations: [
        'Vous devez absolument écouter ce nouvel album !',
        'Cette artiste va cartonner, j\'en suis sûr',
        'Quelqu\'un connaît ce genre musical ?',
        'Merci pour la découverte, c\'est génial !'
      ],
      venue: [
        'La salle est confirmée pour le 15',
        'Capacité de 500 personnes',
        'Le matériel son est inclus',
        'Parking disponible pour les artistes'
      ],
      'sound design': [
        'Ce synthé sonne incroyable',
        'Comment tu as fait cet effet ?',
        'On pourrait essayer avec plus de distortion',
        'Cette texture sonore est parfaite'
      ]
    }
    
    const allMessages: string[] = []
    topics.forEach(topic => {
      if (messagesByTopic[topic]) {
        allMessages.push(...messagesByTopic[topic])
      }
    })
    
    return allMessages.length > 0 ? allMessages : [vezaFaker.chat.messageContent()]
  }

  /**
   * Utility methods
   */
  static getGeneratedConversation(id: string): Conversation | undefined {
    return this.generatedConversations.get(id)
  }

  static getGeneratedMessage(id: string): Message | undefined {
    return this.generatedMessages.get(id)
  }

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

  static getAllGeneratedConversations(): Conversation[] {
    return Array.from(this.generatedConversations.values())
  }

  static getAllGeneratedMessages(): Message[] {
    return Array.from(this.generatedMessages.values())
  }

  static clearCache(): void {
    this.generatedConversations.clear()
    this.generatedMessages.clear()
    this.conversationMessages.clear()
  }

  static getStats(): {
    totalConversations: number
    totalMessages: number
    byType: Record<ConversationType, number>
    averageMessagesPerConversation: number
  } {
    const conversations = this.getAllGeneratedConversations()
    const messages = this.getAllGeneratedMessages()
    
    const byType: Record<ConversationType, number> = {
      direct: 0,
      group: 0,
      channel: 0
    }

    conversations.forEach(conv => {
      byType[conv.type]++
    })

    return {
      totalConversations: conversations.length,
      totalMessages: messages.length,
      byType,
      averageMessagesPerConversation: conversations.length > 0 ? 
        Math.round(messages.length / conversations.length) : 0
    }
  }
}