<?php
/**
 * ============================================================================
 * classes/Message.php - Enhanced Message Management Class
 * ============================================================================
 */

namespace App;

use PDO;
use PDOException;
use Exception;

class Message {
    private $db;
    private $table = 'messages';
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    /**
     * Send message with optional encryption
     */
    public function send($chatId, $senderId, $data, $encrypt = false) {
        try {
            $encryptedContent = null;
            $encryptedKey = null;
            $iv = null;
            $messageHash = null;
            $signature = null;
            $isEncrypted = 0;
            
            if ($encrypt && !empty($data['content'])) {
                // Get recipient's public key(s)
                $recipients = $this->getChatMembers($chatId, $senderId);
                
                if (!empty($recipients)) {
                    // Encrypt message
                    $encryption = new \App\Encryption();
                    
                    if (count($recipients) == 1) {
                        // One-to-one: encrypt with recipient's public key
                        $recipientKey = $recipients[0]['public_key'];
                        $encryptedData = $encryption::encryptMessage($data['content'], $recipientKey);
                        
                        $encryptedContent = $encryptedData['encrypted_message'];
                        $encryptedKey = $encryptedData['encrypted_key'];
                        $iv = $encryptedData['iv'];
                    } else {
                        // Group chat: encrypt for multiple recipients
                        $publicKeys = [];
                        foreach ($recipients as $recipient) {
                            $publicKeys[$recipient['user_id']] = $recipient['public_key'];
                        }
                        
                        $encryptedData = $encryption::encryptForMultipleRecipients($data['content'], $publicKeys);
                        $encryptedContent = $encryptedData['encrypted_message'];
                        $iv = $encryptedData['iv'];
                        
                        // Store encrypted keys for each recipient
                        $this->storeGroupEncryptionKeys($chatId, $encryptedData['encrypted_keys']);
                    }
                    
                    // Get sender's private key for signing
                    $senderPrivateKey = $this->getUserPrivateKey($senderId);
                    if ($senderPrivateKey) {
                        $signature = $encryption::signMessage($data['content'], $senderPrivateKey);
                    }
                    
                    $messageHash = $encryption::hashMessage($data['content']);
                    $isEncrypted = 1;
                    
                    // Log encryption action
                    $this->logEncryptionAction($senderId, 'encrypt_message', 'Message encrypted for chat ' . $chatId);
                }
            }
            
            $sql = "INSERT INTO {$this->table} 
                    (chat_id, sender_id, message_type, content, encrypted_content, 
                     encrypted_key, iv, message_hash, sender_signature, is_encrypted, media_url)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
            
            $stmt = $this->db->prepare($sql);
            $result = $stmt->execute([
                $chatId,
                $senderId,
                $data['message_type'] ?? 'text',
                $isEncrypted ? '[Encrypted Message]' : $data['content'], // Store placeholder if encrypted
                $encryptedContent,
                $encryptedKey,
                $iv,
                $messageHash,
                $signature,
                $isEncrypted,
                $data['media_url'] ?? null
            ]);
            
            if ($result) {
                $messageId = $this->db->lastInsertId();
                
                // Handle attachments if any
                if (isset($data['attachments']) && is_array($data['attachments'])) {
                    $this->addAttachments($messageId, $data['attachments']);
                }
                
                // Create notifications for chat members
                $this->createMessageNotifications($chatId, $senderId, $messageId);
                
                return [
                    'success' => true,
                    'message_id' => $messageId,
                    'is_encrypted' => (bool)$isEncrypted
                ];
            }
            
            return ['success' => false, 'message' => 'Failed to send message'];
            
        } catch (PDOException $e) {
            error_log("Message send error: " . $e->getMessage());
            $this->logEncryptionAction($senderId, 'encrypt_failed', 'Failed to send encrypted message: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Database error'];
        } catch (Exception $e) {
            error_log("Message send error: " . $e->getMessage());
            return ['success' => false, 'message' => 'Error: ' . $e->getMessage()];
        }
    }
    
    /**
     * Get chat messages
     */
    public function getChatMessages($chatId, $userId, $limit = 50, $beforeId = null) {
        $sql = "SELECT m.*, 
                u.full_name as sender_name, 
                u.profile_picture as sender_avatar,
                (SELECT COUNT(*) FROM message_reactions WHERE message_id = m.id) as reactions_count,
                (SELECT GROUP_CONCAT(CONCAT(reaction, ':', user_id)) 
                 FROM message_reactions 
                 WHERE message_id = m.id) as reactions
                FROM {$this->table} m
                JOIN users u ON m.sender_id = u.id
                WHERE m.chat_id = ?";
        
        $params = [$chatId];
        
        if ($beforeId) {
            $sql .= " AND m.id < ?";
            $params[] = $beforeId;
        }
        
        $sql .= " ORDER BY m.created_at DESC LIMIT ?";
        $params[] = $limit;
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        $messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        // Get attachments for messages
        foreach ($messages as &$message) {
            $message['attachments'] = $this->getMessageAttachments($message['id']);
        }
        
        return array_reverse($messages); // Return in chronological order
    }
    
    /**
     * Get unread message count for user
     */
    public function getUnreadCount($userId) {
        $sql = "SELECT COUNT(DISTINCT m.chat_id) as unread_chats
                FROM {$this->table} m
                INNER JOIN chat_members cm ON m.chat_id = cm.chat_id
                WHERE cm.user_id = ?
                AND m.sender_id != ?
                AND m.created_at > COALESCE(cm.last_read_at, '2000-01-01')";
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$userId, $userId]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        return $result['unread_chats'] ?? 0;
    }
    
    /**
     * Add reaction to message
     */
    public function addReaction($messageId, $userId, $reaction) {
        try {
            // Check if already reacted
            $sql = "SELECT id FROM message_reactions 
                    WHERE message_id = ? AND user_id = ?";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([$messageId, $userId]);
            
            if ($stmt->fetch()) {
                // Update reaction
                $sql = "UPDATE message_reactions 
                        SET reaction = ? 
                        WHERE message_id = ? AND user_id = ?";
                $stmt = $this->db->prepare($sql);
                $stmt->execute([$reaction, $messageId, $userId]);
            } else {
                // Add new reaction
                $sql = "INSERT INTO message_reactions (message_id, user_id, reaction)
                        VALUES (?, ?, ?)";
                $stmt = $this->db->prepare($sql);
                $stmt->execute([$messageId, $userId, $reaction]);
            }
            
            return ['success' => true];
            
        } catch (PDOException $e) {
            error_log("Reaction error: " . $e->getMessage());
            return ['success' => false, 'message' => 'Failed to add reaction'];
        }
    }
    
    /**
     * Remove reaction
     */
    public function removeReaction($messageId, $userId) {
        $sql = "DELETE FROM message_reactions 
                WHERE message_id = ? AND user_id = ?";
        $stmt = $this->db->prepare($sql);
        
        if ($stmt->execute([$messageId, $userId])) {
            return ['success' => true];
        }
        
        return ['success' => false, 'message' => 'Failed to remove reaction'];
    }
    
    /**
     * Delete message
     */
    public function delete($messageId, $userId) {
        $sql = "DELETE FROM {$this->table} 
                WHERE id = ? AND sender_id = ?";
        $stmt = $this->db->prepare($sql);
        
        if ($stmt->execute([$messageId, $userId])) {
            return ['success' => true, 'message' => 'Message deleted'];
        }
        
        return ['success' => false, 'message' => 'Failed to delete message'];
    }
    
    /**
     * Add attachments to message
     */
    private function addAttachments($messageId, $attachments) {
        $sql = "INSERT INTO message_attachments 
                (message_id, file_name, file_type, file_size, file_url)
                VALUES (?, ?, ?, ?, ?)";
        
        $stmt = $this->db->prepare($sql);
        
        foreach ($attachments as $attachment) {
            $stmt->execute([
                $messageId,
                $attachment['file_name'],
                $attachment['file_type'],
                $attachment['file_size'],
                $attachment['file_url']
            ]);
        }
    }
    
    /**
     * Get message attachments
     */
    private function getMessageAttachments($messageId) {
        $sql = "SELECT * FROM message_attachments WHERE message_id = ?";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$messageId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    /**
     * Create notifications for new messages
     */
    private function createMessageNotifications($chatId, $senderId, $messageId) {
        // Get chat members except sender
        $sql = "SELECT cm.user_id, u.full_name, c.chat_name, c.chat_type
                FROM chat_members cm
                JOIN users u ON cm.user_id = u.id
                JOIN chats c ON cm.chat_id = c.id
                WHERE cm.chat_id = ? AND cm.user_id != ?";
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$chatId, $senderId]);
        $members = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        // Get sender name
        $sql = "SELECT full_name FROM users WHERE id = ?";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$senderId]);
        $sender = $stmt->fetch(PDO::FETCH_ASSOC);
        
        // Create notification for each member
        foreach ($members as $member) {
            $chatName = $member['chat_type'] === 'one_to_one' 
                ? $sender['full_name'] 
                : $member['chat_name'];
            
            $sql = "INSERT INTO notifications (user_id, title, message, type, icon, link)
                    VALUES (?, ?, ?, 'info', 'chat-dots', ?)";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([
                $member['user_id'],
                'New Message',
                $sender['full_name'] . ' sent you a message in ' . $chatName,
                '/chat/one-on-one.php?chat_id=' . $chatId
            ]);
        }
    }
    
    /**
     * Mark messages as read
     */
    public function markAsRead($chatId, $userId) {
        $sql = "UPDATE chat_members 
                SET last_read_at = CURRENT_TIMESTAMP
                WHERE chat_id = ? AND user_id = ?";
        
        $stmt = $this->db->prepare($sql);
        return $stmt->execute([$chatId, $userId]);
    }
    
    /**
     * Search messages in chat
     */
    public function searchInChat($chatId, $query) {
        $sql = "SELECT m.*, u.full_name as sender_name
                FROM {$this->table} m
                JOIN users u ON m.sender_id = u.id
                WHERE m.chat_id = ? AND m.content LIKE ?
                ORDER BY m.created_at DESC
                LIMIT 50";
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$chatId, "%{$query}%"]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    /**
     * Get chat members with their public keys
     */
    private function getChatMembers($chatId, $excludeUserId = null) {
        $sql = "SELECT cm.user_id, uek.public_key
                FROM chat_members cm
                LEFT JOIN user_encryption_keys uek ON cm.user_id = uek.user_id
                WHERE cm.chat_id = ?";
        
        $params = [$chatId];
        
        if ($excludeUserId) {
            $sql .= " AND cm.user_id != ?";
            $params[] = $excludeUserId;
        }
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    /**
     * Get user's private key (requires password decryption)
     */
    private function getUserPrivateKey($userId) {
        // In production, this would require the user's password
        // For now, we'll return null to indicate signing is optional
        // The client-side JavaScript will handle the actual signing
        return null;
    }
    
    /**
     * Store encrypted keys for group chat
     */
    private function storeGroupEncryptionKeys($chatId, $encryptedKeys) {
        $sql = "INSERT INTO chat_encryption_keys (chat_id, user_id, encrypted_key)
                VALUES (?, ?, ?)
                ON DUPLICATE KEY UPDATE encrypted_key = VALUES(encrypted_key)";
        
        $stmt = $this->db->prepare($sql);
        
        foreach ($encryptedKeys as $userId => $encryptedKey) {
            $stmt->execute([$chatId, $userId, $encryptedKey]);
        }
    }
    
    /**
     * Decrypt message for recipient
     */
    public function decryptMessage($messageId, $userId, $privateKey) {
        try {
            // Get encrypted message
            $sql = "SELECT * FROM {$this->table} WHERE id = ? AND is_encrypted = 1";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([$messageId]);
            $message = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$message) {
                return ['success' => false, 'message' => 'Message not found'];
            }
            
            // Check if user is chat member
            $sql = "SELECT 1 FROM chat_members WHERE chat_id = ? AND user_id = ?";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([$message['chat_id'], $userId]);
            
            if (!$stmt->fetch()) {
                return ['success' => false, 'message' => 'Access denied'];
            }
            
            // Get encrypted key for this user (for group chats)
            $encryptedKey = $message['encrypted_key'];
            
            if (empty($encryptedKey)) {
                // Group chat - get user's specific encrypted key
                $sql = "SELECT encrypted_key FROM chat_encryption_keys 
                        WHERE chat_id = ? AND user_id = ?";
                $stmt = $this->db->prepare($sql);
                $stmt->execute([$message['chat_id'], $userId]);
                $keyData = $stmt->fetch(PDO::FETCH_ASSOC);
                
                if (!$keyData) {
                    return ['success' => false, 'message' => 'Encryption key not found'];
                }
                
                $encryptedKey = $keyData['encrypted_key'];
            }
            
            // Decrypt the message
            $encryption = new \App\Encryption();
            $decryptedMessage = $encryption::decryptMessage([
                'encrypted_message' => $message['encrypted_content'],
                'encrypted_key' => $encryptedKey,
                'iv' => $message['iv']
            ], $privateKey);
            
            if ($decryptedMessage === false) {
                $this->logEncryptionAction($userId, 'decrypt_failed', 'Failed to decrypt message ' . $messageId);
                return ['success' => false, 'message' => 'Decryption failed'];
            }
            
            // Verify signature if present
            if (!empty($message['sender_signature'])) {
                $sql = "SELECT public_key FROM user_encryption_keys WHERE user_id = ?";
                $stmt = $this->db->prepare($sql);
                $stmt->execute([$message['sender_id']]);
                $senderKey = $stmt->fetch(PDO::FETCH_ASSOC);
                
                if ($senderKey) {
                    $verified = $encryption::verifySignature(
                        $decryptedMessage, 
                        $message['sender_signature'], 
                        $senderKey['public_key']
                    );
                    
                    if (!$verified) {
                        return ['success' => false, 'message' => 'Message signature verification failed'];
                    }
                }
            }
            
            return [
                'success' => true,
                'decrypted_message' => $decryptedMessage,
                'verified' => !empty($message['sender_signature'])
            ];
            
        } catch (Exception $e) {
            error_log("Decryption error: " . $e->getMessage());
            $this->logEncryptionAction($userId, 'decrypt_failed', 'Exception during decryption: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Decryption error'];
        }
    }
    
    /**
     * Log encryption action
     */
    private function logEncryptionAction($userId, $action, $description) {
        try {
            $sql = "INSERT INTO encryption_logs (user_id, action, description, ip_address, user_agent)
                    VALUES (?, ?, ?, ?, ?)";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([
                $userId,
                $action,
                $description,
                $_SERVER['REMOTE_ADDR'] ?? '',
                $_SERVER['HTTP_USER_AGENT'] ?? ''
            ]);
        } catch (Exception $e) {
            error_log("Failed to log encryption action: " . $e->getMessage());
        }
    }
}
?>