const WebSocket = require('ws');
const jwt = require('jsonwebtoken');
const User = require('./models/User');
const Group = require('./models/Group');
const GroupMessage = require('./models/GroupMessage');
const fs = require('fs');
const path = require('path');

class GroupChatWebSocket {
    constructor(server) {
        try {
            this.wss = new WebSocket.Server({ 
                server,
                path: '/ws/group-chat',
                perMessageDeflate: false, // Disable compression to avoid conflicts
                maxPayload: 16 * 1024 * 1024 // 16MB max payload
            });
            
            // Store active connections by groupId
            this.groupConnections = new Map();
            // Store user connections for cleanup
            this.userConnections = new Map();
            
            // Connection statistics
            this.stats = {
                totalConnections: 0,
                activeConnections: 0,
                totalMessages: 0,
                errors: 0
            };
            
            console.log('💬 [WEBSOCKET] Group Chat WebSocket server started on /ws/group-chat');
            
            this.setupWebSocketHandlers();
            
            // Set up periodic cleanup
            this.setupPeriodicCleanup();
            
        } catch (error) {
            console.error('❌ [WEBSOCKET] Failed to initialize Group Chat WebSocket:', error);
            throw error;
        }
    }
    
    setupPeriodicCleanup() {
        // Clean up broken connections every 30 seconds
        setInterval(() => {
            this.cleanupBrokenConnections();
        }, 30000);
        
        // Log statistics every 5 minutes
        setInterval(() => {
            this.logStatistics();
        }, 300000);
    }
    
    cleanupBrokenConnections() {
        let cleanedCount = 0;
        
        for (const [groupId, connections] of this.groupConnections.entries()) {
            const brokenConnections = [];
            
            for (const ws of connections) {
                if (ws.readyState !== WebSocket.OPEN) {
                    brokenConnections.push(ws);
                    cleanedCount++;
                }
            }
            
            // Remove broken connections
            brokenConnections.forEach(ws => {
                connections.delete(ws);
                if (ws.user?.id) {
                    this.userConnections.delete(ws.user.id);
                }
            });
            
            // Remove empty group connections
            if (connections.size === 0) {
                this.groupConnections.delete(groupId);
            }
        }
        
        if (cleanedCount > 0) {
            console.log(`🧹 [WEBSOCKET] Cleaned up ${cleanedCount} broken connections`);
        }
    }
    
    logStatistics() {
        const stats = this.getStats();
        console.log(`📊 [WEBSOCKET STATS]`, {
            totalConnections: this.stats.totalConnections,
            activeConnections: this.stats.activeConnections,
            activeGroups: stats.activeGroups,
            totalMessages: this.stats.totalMessages,
            errors: this.stats.errors
        });
    }
    
    setupWebSocketHandlers() {
        this.wss.on('connection', async (ws, req) => {
            this.stats.totalConnections++;
            this.stats.activeConnections++;
            console.log('💬 [WEBSOCKET] New connection attempt');
            
            try {
                // Authenticate user from query parameters or headers
                const token = this.extractToken(req);
                if (!token) {
                    ws.send(JSON.stringify({
                        type: 'error',
                        message: 'Authentication token required'
                    }));
                    ws.close();
                    return;
                }
                
                // Verify JWT token
                const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key');
                const user = await User.findById(decoded.id).select('name mobile profileImage');
                
                if (!user) {
                    ws.send(JSON.stringify({
                        type: 'error',
                        message: 'User not found'
                    }));
                    ws.close();
                    return;
                }
                
                // Store user info in connection
                ws.user = {
                    id: user._id.toString(),
                    name: user.name,
                    mobile: user.mobile,
                    profileImage: user.profileImage
                };
                
                console.log(`💬 [WEBSOCKET] User ${user.name} (${user._id}) connected`);
                
                // Handle incoming messages
                ws.on('message', (data) => {
                    this.handleMessage(ws, data);
                });
                
                // Handle connection close
                ws.on('close', () => {
                    this.stats.activeConnections--;
                    this.handleDisconnect(ws);
                });
                
                // Handle errors
                ws.on('error', (error) => {
                    this.stats.errors++;
                    this.stats.activeConnections--;
                    console.error(`💬 [WEBSOCKET ERROR] Connection error for user ${ws.user?.id}:`, error);
                    this.handleDisconnect(ws);
                });
                
                // Send welcome message
                ws.send(JSON.stringify({
                    type: 'connected',
                    message: 'Connected to group chat',
                    user: ws.user
                }));
                
            } catch (error) {
                console.error('💬 [WEBSOCKET ERROR] Authentication failed:', error);
                ws.send(JSON.stringify({
                    type: 'error',
                    message: 'Authentication failed'
                }));
                ws.close();
            }
        });
    }
    
    extractToken(req) {
        // Try to get token from query parameters
        const url = new URL(req.url, `http://${req.headers.host}`);
        let token = url.searchParams.get('token');
        
        // Try to get token from Authorization header
        if (!token && req.headers.authorization) {
            const authHeader = req.headers.authorization;
            if (authHeader.startsWith('Bearer ')) {
                token = authHeader.substring(7);
            }
        }
        
        return token;
    }
    
    async handleMessage(ws, data) {
        try {
            this.stats.totalMessages++;
            const message = JSON.parse(data);
            console.log(`💬 [WEBSOCKET MESSAGE] Received:`, message);
            
            switch (message.type) {
                case 'join_group':
                    await this.handleJoinGroup(ws, message);
                    break;
                    
                case 'leave_group':
                    await this.handleLeaveGroup(ws, message);
                    break;
                    
                case 'send_message':
                    await this.handleSendMessage(ws, message);
                    break;
                    
                case 'typing_start':
                    await this.handleTypingStart(ws, message);
                    break;
                    
                case 'typing_stop':
                    await this.handleTypingStop(ws, message);
                    break;
                    
                case 'ping':
                    ws.send(JSON.stringify({ type: 'pong' }));
                    break;
                    
                default:
                    ws.send(JSON.stringify({
                        type: 'error',
                        message: 'Unknown message type'
                    }));
            }
            
        } catch (error) {
            console.error('💬 [WEBSOCKET ERROR] Message handling failed:', error);
            ws.send(JSON.stringify({
                type: 'error',
                message: 'Invalid message format'
            }));
        }
    }
    
    async handleJoinGroup(ws, message) {
        try {
            const { groupId } = message;
            
            if (!groupId) {
                ws.send(JSON.stringify({
                    type: 'error',
                    message: 'Group ID is required'
                }));
                return;
            }
            
            // Verify group exists
            const group = await Group.findById(groupId);
            if (!group) {
                ws.send(JSON.stringify({
                    type: 'error',
                    message: 'Group not found'
                }));
                return;
            }
            
            // Check if user is member of the group
            const isMember = group.members ? group.members.some(member => 
                member.user_id.toString() === ws.user.id
            ) : false;
            
            if (!isMember) {
                ws.send(JSON.stringify({
                    type: 'error',
                    message: 'You are not a member of this group'
                }));
                return;
            }
            
            // Remove user from any previous group connections
            this.removeUserFromAllGroups(ws.user.id);
            
            // Add user to group connections
            if (!this.groupConnections.has(groupId)) {
                this.groupConnections.set(groupId, new Set());
            }
            this.groupConnections.get(groupId).add(ws);
            
            // Store group info in connection
            ws.groupId = groupId;
            
            console.log(`💬 [WEBSOCKET] User ${ws.user.name} joined group ${groupId}`);
            
            // Notify other members in the group
            this.broadcastToGroup(groupId, {
                type: 'user_joined',
                user: ws.user,
                groupId: groupId,
                timestamp: new Date().toISOString()
            }, ws);
            
            // Send confirmation
            ws.send(JSON.stringify({
                type: 'joined_group',
                groupId: groupId,
                groupName: group.groupName,
                message: `Joined ${group.groupName}`
            }));
            
            // Send recent messages
            const recentMessages = await GroupMessage.find({
                groupId: groupId,
                isVisible: true,
                isDeleted: false
            })
            .populate('userId', 'name mobile profileImage')
            .sort({ createdAt: -1 })
            .limit(50);
            
            ws.send(JSON.stringify({
                type: 'recent_messages',
                messages: recentMessages.reverse(),
                groupId: groupId
            }));
            
        } catch (error) {
            console.error('💬 [WEBSOCKET ERROR] Join group failed:', error);
            ws.send(JSON.stringify({
                type: 'error',
                message: 'Failed to join group'
            }));
        }
    }
    
    async handleLeaveGroup(ws, message) {
        try {
            const { groupId } = message.groupId || ws.groupId;
            
            if (groupId && this.groupConnections.has(groupId)) {
                this.groupConnections.get(groupId).delete(ws);
                
                // Notify other members
                this.broadcastToGroup(groupId, {
                    type: 'user_left',
                    user: ws.user,
                    groupId: groupId,
                    timestamp: new Date().toISOString()
                }, ws);
                
                console.log(`💬 [WEBSOCKET] User ${ws.user.name} left group ${groupId}`);
            }
            
            ws.groupId = null;
            
            ws.send(JSON.stringify({
                type: 'left_group',
                message: 'Left group successfully'
            }));
            
        } catch (error) {
            console.error('💬 [WEBSOCKET ERROR] Leave group failed:', error);
        }
    }
    
    async handleSendMessage(ws, message) {
        try {
            const { groupId, content, messageType = 'text', replyTo } = message;
            
            if (!groupId || !content) {
                ws.send(JSON.stringify({
                    type: 'error',
                    message: 'Group ID and message content are required'
                }));
                return;
            }
            
            // Verify user is in the group
            if (!this.groupConnections.has(groupId) || !this.groupConnections.get(groupId).has(ws)) {
                ws.send(JSON.stringify({
                    type: 'error',
                    message: 'You must join the group first'
                }));
                return;
            }
            
            // Save message to database
            const groupMessage = new GroupMessage({
                groupId: groupId,
                userId: ws.user.id,
                message: content,
                messageType: messageType,
                replyTo: replyTo || null,
                metadata: {
                    clientMessageId: message.clientMessageId,
                    deviceInfo: message.deviceInfo,
                    ipAddress: ws._socket.remoteAddress
                }
            });
            
            await groupMessage.save();
            
            // Populate the message
            const populatedMessage = await GroupMessage.findById(groupMessage._id)
                .populate('userId', 'name mobile profileImage')
                .populate('replyTo', 'message userId');
            
            console.log(`💬 [WEBSOCKET] Message saved: ${groupMessage._id}`);
            
            // Broadcast message to all group members
            const messageData = {
                type: 'new_message',
                message: populatedMessage,
                groupId: groupId,
                timestamp: new Date().toISOString()
            };
            
            this.broadcastToGroup(groupId, messageData);
            
            // Send confirmation to sender
            ws.send(JSON.stringify({
                type: 'message_sent',
                messageId: groupMessage._id,
                clientMessageId: message.clientMessageId
            }));
            
        } catch (error) {
            console.error('💬 [WEBSOCKET ERROR] Send message failed:', error);
            ws.send(JSON.stringify({
                type: 'error',
                message: 'Failed to send message'
            }));
        }
    }
    
    async handleTypingStart(ws, message) {
        const { groupId } = message;
        
        if (groupId && this.groupConnections.has(groupId)) {
            this.broadcastToGroup(groupId, {
                type: 'user_typing_start',
                user: ws.user,
                groupId: groupId
            }, ws);
        }
    }
    
    async handleTypingStop(ws, message) {
        const { groupId } = message;
        
        if (groupId && this.groupConnections.has(groupId)) {
            this.broadcastToGroup(groupId, {
                type: 'user_typing_stop',
                user: ws.user,
                groupId: groupId
            }, ws);
        }
    }
    
    handleDisconnect(ws) {
        console.log(`💬 [WEBSOCKET] User ${ws.user?.name} disconnected`);
        
        // Remove from all groups
        if (ws.user?.id) {
            this.removeUserFromAllGroups(ws.user.id);
        }
        
        // Clean up user connections
        if (ws.user?.id && this.userConnections.has(ws.user.id)) {
            this.userConnections.delete(ws.user.id);
        }
    }
    
    removeUserFromAllGroups(userId) {
        for (const [groupId, connections] of this.groupConnections.entries()) {
            for (const connection of connections) {
                if (connection.user?.id === userId) {
                    connections.delete(connection);
                    connection.groupId = null;
                    
                    // Notify group members
                    this.broadcastToGroup(groupId, {
                        type: 'user_left',
                        user: connection.user,
                        groupId: groupId,
                        timestamp: new Date().toISOString()
                    }, connection);
                    
                    break;
                }
            }
            
            // Clean up empty group connections
            if (connections.size === 0) {
                this.groupConnections.delete(groupId);
            }
        }
    }
    
    broadcastToGroup(groupId, data, excludeWs = null) {
        if (!this.groupConnections.has(groupId)) {
            return;
        }
        
        const connections = this.groupConnections.get(groupId);
        const message = JSON.stringify(data);
        
        console.log(`💬 [WEBSOCKET] Broadcasting to ${connections.size} connections in group ${groupId}:`, data.type);
        
        for (const ws of connections) {
            if (ws.readyState === WebSocket.OPEN && ws !== excludeWs) {
                try {
                    ws.send(message);
                } catch (error) {
                    console.error('💬 [WEBSOCKET ERROR] Failed to send message:', error);
                    // Remove broken connection
                    connections.delete(ws);
                }
            }
        }
    }
    
    // Method to send system messages (can be called from API)
    async sendSystemMessage(groupId, content, messageType = 'system') {
        try {
            const systemMessage = new GroupMessage({
                groupId: groupId,
                userId: null, // System message
                message: content,
                messageType: messageType
            });
            
            await systemMessage.save();
            
            this.broadcastToGroup(groupId, {
                type: 'system_message',
                message: systemMessage,
                groupId: groupId,
                timestamp: new Date().toISOString()
            });
            
            console.log(`💬 [WEBSOCKET] System message sent to group ${groupId}: ${content}`);
            
        } catch (error) {
            console.error('💬 [WEBSOCKET ERROR] Failed to send system message:', error);
        }
    }
    
    // Get connection statistics
    getStats() {
        const stats = {
            totalConnections: this.wss.clients.size,
            activeGroups: this.groupConnections.size,
            groupStats: {}
        };
        
        for (const [groupId, connections] of this.groupConnections.entries()) {
            stats.groupStats[groupId] = connections.size;
        }
        
        return stats;
    }
}

module.exports = GroupChatWebSocket;
