import { Client } from 'pg'
import { createClient } from 'redis'
import { DataRelationManager } from '../../core/utils/data-relations'
import { globalConfig } from '../../core/config'
import type { User, Conversation, Message } from '../../core/schemas/database'

/**
 * Chat server fixtures - Database seeding and Redis cache
 */
export class ChatServerFixtures {
  private static dbClient: Client | null = null
  private static redisClient: any = null

  /**
   * Initialize database connections
   */
  static async initialize(): Promise<void> {
    console.log('💬 Initializing chat server fixtures...')
    
    // Initialize PostgreSQL connection
    this.dbClient = new Client({
      host: globalConfig.database.host,
      port: globalConfig.database.port,
      user: globalConfig.database.username,
      password: globalConfig.database.password,
      database: globalConfig.database.databases.chat,
    })
    
    try {
      await this.dbClient.connect()
      console.log('✅ Connected to PostgreSQL (chat)')
    } catch (error) {
      console.warn('⚠️ Could not connect to PostgreSQL (chat):', error)
      this.dbClient = null
    }
    
    // Initialize Redis connection
    this.redisClient = createClient({
      socket: {
        host: globalConfig.redis.host,
        port: globalConfig.redis.port,
      },
      password: globalConfig.redis.password || undefined,
    })
    
    try {
      await this.redisClient.connect()
      console.log('✅ Connected to Redis')
    } catch (error) {
      console.warn('⚠️ Could not connect to Redis:', error)
      this.redisClient = null
    }
  }

  /**
   * Seed PostgreSQL database with chat data
   */
  static async seedDatabase(): Promise<void> {
    if (!this.dbClient) {
      console.warn('⚠️ No database connection available')
      return
    }

    console.log('🗄️ Seeding chat database...')
    
    try {
      // Begin transaction
      await this.dbClient.query('BEGIN')
      
      // Clear existing data
      await this.clearDatabase()
      
      // Get data from relation manager
      const allData = DataRelationManager.getAll()
      const users = Array.from(allData.users.values())
      const conversations = Array.from(allData.conversations.values())
      const messages = Array.from(allData.messages.values())
      
      // Seed users
      console.log(`📝 Seeding ${users.length} users...`)
      await this.seedUsers(users)
      
      // Seed conversations
      console.log(`📝 Seeding ${conversations.length} conversations...`)
      await this.seedConversations(conversations)
      
      // Seed messages
      console.log(`📝 Seeding ${messages.length} messages...`)
      await this.seedMessages(messages)
      
      // Commit transaction
      await this.dbClient.query('COMMIT')
      
      console.log('✅ Chat database seeded successfully')
      
    } catch (error) {
      await this.dbClient.query('ROLLBACK')
      console.error('❌ Error seeding chat database:', error)
      throw error
    }
  }

  /**
   * Seed Redis cache with session data
   */
  static async seedRedis(): Promise<void> {
    if (!this.redisClient) {
      console.warn('⚠️ No Redis connection available')
      return
    }

    console.log('🔄 Seeding Redis cache...')
    
    try {
      // Clear existing cache
      await this.redisClient.flushDb()
      
      const allData = DataRelationManager.getAll()
      const users = Array.from(allData.users.values())
      const conversations = Array.from(allData.conversations.values())
      
      // Cache active users
      const activeUsers = users.filter(user => user.status === 'active')
      for (const user of activeUsers) {
        const sessionData = {
          userId: user.id,
          username: user.username,
          status: 'online',
          lastSeen: new Date().toISOString(),
          socketId: `socket_${user.id}_${Date.now()}`,
        }
        
        await this.redisClient.setEx(
          `session:${user.id}`,
          3600, // 1 hour TTL
          JSON.stringify(sessionData)
        )
        
        // Add to online users set
        await this.redisClient.sAdd('users:online', user.id)
      }
      
      // Cache conversation metadata
      for (const conversation of conversations) {
        const metadata = {
          id: conversation.id,
          name: conversation.name,
          type: conversation.type,
          participantCount: conversation.participantIds.length,
          lastActivity: conversation.lastActivityAt.toISOString(),
        }
        
        await this.redisClient.setEx(
          `conversation:${conversation.id}`,
          1800, // 30 minutes TTL
          JSON.stringify(metadata)
        )
        
        // Cache participant lists
        await this.redisClient.sAdd(
          `conversation:${conversation.id}:participants`,
          ...conversation.participantIds
        )
        
        // Set expiry for participant sets
        await this.redisClient.expire(
          `conversation:${conversation.id}:participants`,
          1800
        )
      }
      
      // Cache typing indicators (empty initially)
      await this.redisClient.set('typing:indicators', '{}')
      
      // Cache message delivery status
      const recentMessages = Array.from(allData.messages.values())
        .filter(msg => {
          const dayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000)
          return msg.createdAt > dayAgo
        })
      
      for (const message of recentMessages) {
        await this.redisClient.setEx(
          `message:${message.id}:status`,
          86400, // 24 hours TTL
          message.status
        )
      }
      
      console.log(`✅ Redis cache seeded with ${activeUsers.length} sessions and ${conversations.length} conversations`)
      
    } catch (error) {
      console.error('❌ Error seeding Redis cache:', error)
      throw error
    }
  }

  /**
   * Generate WebSocket event data
   */
  static generateWebSocketEvents(): any[] {
    const allData = DataRelationManager.getAll()
    const users = Array.from(allData.users.values())
    const conversations = Array.from(allData.conversations.values())
    const messages = Array.from(allData.messages.values())
    
    const events: any[] = []
    
    // User connection events
    users.filter(u => u.status === 'active').forEach(user => {
      events.push({
        type: 'user_connected',
        userId: user.id,
        data: {
          username: user.username,
          status: 'online',
          lastSeen: new Date().toISOString()
        },
        timestamp: new Date().toISOString()
      })
    })
    
    // Recent message events
    const recentMessages = messages
      .filter(msg => {
        const hourAgo = new Date(Date.now() - 60 * 60 * 1000)
        return msg.createdAt > hourAgo
      })
      .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
    
    recentMessages.forEach(message => {
      events.push({
        type: 'message_sent',
        conversationId: message.conversationId,
        data: {
          id: message.id,
          senderId: message.senderId,
          content: message.content,
          type: message.type,
          createdAt: message.createdAt.toISOString()
        },
        timestamp: message.createdAt.toISOString()
      })
    })
    
    // Typing indicator events
    conversations.forEach(conversation => {
      const typingUser = conversation.participantIds[0]
      events.push({
        type: 'typing_start',
        conversationId: conversation.id,
        data: {
          userId: typingUser,
          isTyping: true
        },
        timestamp: new Date(Date.now() - Math.random() * 30000).toISOString()
      })
    })
    
    return events.sort((a, b) => 
      new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
    )
  }

  /**
   * Get conversation statistics
   */
  static async getConversationStats(): Promise<any> {
    if (!this.dbClient) {
      return this.getMockConversationStats()
    }

    try {
      const result = await this.dbClient.query(`
        SELECT 
          c.type,
          COUNT(*) as count,
          AVG(array_length(c.participant_ids, 1)) as avg_participants,
          COUNT(m.id) as total_messages
        FROM conversations c
        LEFT JOIN messages m ON c.id = m.conversation_id
        GROUP BY c.type
      `)
      
      return result.rows.reduce((stats, row) => {
        stats[row.type] = {
          count: parseInt(row.count),
          avgParticipants: Math.round(parseFloat(row.avg_participants)),
          totalMessages: parseInt(row.total_messages)
        }
        return stats
      }, {})
      
    } catch (error) {
      console.error('Error getting conversation stats:', error)
      return this.getMockConversationStats()
    }
  }

  /**
   * Private helper methods
   */
  private static async clearDatabase(): Promise<void> {
    if (!this.dbClient) return
    
    const tables = ['messages', 'conversation_members', 'conversations', 'users']
    
    for (const table of tables) {
      await this.dbClient.query(`DELETE FROM ${table}`)
    }
    
    console.log('🗑️ Cleared existing chat database data')
  }

  private static async seedUsers(users: User[]): Promise<void> {
    if (!this.dbClient) return
    
    const query = `
      INSERT INTO users (
        id, username, email, display_name, avatar_url, 
        is_active, last_seen, created_at, updated_at
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
    `
    
    for (const user of users) {
      await this.dbClient.query(query, [
        user.id,
        user.username,
        user.email,
        user.displayName || `${user.firstName} ${user.lastName}`,
        user.avatar,
        user.status === 'active',
        user.lastSeenAt,
        user.createdAt,
        user.updatedAt
      ])
    }
  }

  private static async seedConversations(conversations: Conversation[]): Promise<void> {
    if (!this.dbClient) return
    
    const conversationQuery = `
      INSERT INTO conversations (
        id, name, description, conversation_type, is_private,
        created_by, created_at, updated_at
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
    `
    
    const memberQuery = `
      INSERT INTO conversation_members (conversation_id, user_id, role, joined_at)
      VALUES ($1, $2, $3, $4)
    `
    
    for (const conversation of conversations) {
      // Insert conversation
      await this.dbClient.query(conversationQuery, [
        conversation.id,
        conversation.name,
        conversation.description,
        conversation.type,
        conversation.isPrivate,
        conversation.createdById,
        conversation.createdAt,
        conversation.updatedAt
      ])
      
      // Insert participants
      for (const participantId of conversation.participantIds) {
        const role = participantId === conversation.createdById ? 'admin' : 'member'
        await this.dbClient.query(memberQuery, [
          conversation.id,
          participantId,
          role,
          conversation.createdAt
        ])
      }
    }
  }

  private static async seedMessages(messages: Message[]): Promise<void> {
    if (!this.dbClient) return
    
    const query = `
      INSERT INTO messages (
        id, conversation_id, sender_id, content, message_type,
        parent_message_id, is_pinned, is_deleted, created_at, updated_at, status
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
    `
    
    for (const message of messages) {
      await this.dbClient.query(query, [
        message.id,
        message.conversationId,
        message.senderId,
        message.content,
        message.type,
        message.parentMessageId,
        false, // is_pinned
        message.isDeleted,
        message.createdAt,
        message.updatedAt,
        message.status
      ])
    }
  }

  private static getMockConversationStats(): any {
    const stats = DataRelationManager.getStatistics()
    return {
      direct: {
        count: Math.floor(stats.conversations * 0.6),
        avgParticipants: 2,
        totalMessages: Math.floor(stats.messages * 0.4)
      },
      group: {
        count: Math.floor(stats.conversations * 0.3),
        avgParticipants: 6,
        totalMessages: Math.floor(stats.messages * 0.4)
      },
      channel: {
        count: Math.floor(stats.conversations * 0.1),
        avgParticipants: 25,
        totalMessages: Math.floor(stats.messages * 0.2)
      }
    }
  }

  /**
   * Cleanup methods
   */
  static async cleanup(): Promise<void> {
    if (this.dbClient) {
      await this.dbClient.end()
      this.dbClient = null
    }
    
    if (this.redisClient) {
      await this.redisClient.quit()
      this.redisClient = null
    }
    
    console.log('🧹 Chat server fixtures cleanup completed')
  }

  /**
   * Health check
   */
  static async healthCheck(): Promise<{
    database: boolean
    redis: boolean
    dataConsistency: boolean
  }> {
    let database = false
    let redis = false
    let dataConsistency = false
    
    // Check database connection
    if (this.dbClient) {
      try {
        await this.dbClient.query('SELECT 1')
        database = true
      } catch (error) {
        console.error('Database health check failed:', error)
      }
    }
    
    // Check Redis connection
    if (this.redisClient) {
      try {
        await this.redisClient.ping()
        redis = true
      } catch (error) {
        console.error('Redis health check failed:', error)
      }
    }
    
    // Check data consistency
    try {
      const validation = DataRelationManager.validateRelations()
      dataConsistency = validation.isValid
      
      if (!validation.isValid) {
        console.warn('Data consistency issues:', validation.errors)
      }
    } catch (error) {
      console.error('Data consistency check failed:', error)
    }
    
    return { database, redis, dataConsistency }
  }
}