<?php

namespace App\Controllers;

use App\Core\Controller;
use App\Config\Database;
use App\Middleware\AuthMiddleware;
use App\Services\OneSignalService;
use PDO;

class MessageController extends Controller {
    private $db;
    private $oneSignal;

    public function __construct() {
        $this->db = Database::getInstance()->getConnection();
        $this->oneSignal = new OneSignalService();
    }

    private function logDebug($message, $data = []) {
        $logFile = __DIR__ . '/../../public/debug_log.txt';
        $timestamp = date('Y-m-d H:i:s');
        $logEntry = "[$timestamp] $message " . json_encode($data) . "\n";
        file_put_contents($logFile, $logEntry, FILE_APPEND);
    }

    private function resolveUserId($userId, $allowCreate = true) {
        $this->logDebug("Resolving User ID: " . json_encode($userId));
        
        if (!is_string($userId) || strpos($userId, 'guest:') !== 0) {
            return (int)$userId;
        }

        $parts = explode(':', $userId, 4);
        if (count($parts) < 3) {
            return 0;
        }

        $ownerId = (int)$parts[1];
        $visitorHash = $parts[2];
        $anonLabel = isset($parts[3]) && $parts[3] ? $parts[3] : ('Anon' . rand(100, 999));

        if (preg_match('/^user_stable_(\d+)$/', $visitorHash, $m)) {
            $stableUserId = (int)$m[1];
            $stmt = $this->db->prepare("SELECT id FROM users WHERE id = ? LIMIT 1");
            $stmt->execute([$stableUserId]);
            if ($stmt->fetch(PDO::FETCH_ASSOC)) {
                return $stableUserId;
            }
        }

        $stmt = $this->db->prepare("SELECT id, anon_label FROM anonymous_sessions WHERE owner_user_id = ? AND visitor_hash = ?");
        $stmt->execute([$ownerId, $visitorHash]);
        $session = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$session) {
            if (!$allowCreate) {
                return 0;
            }
            $stmt = $this->db->prepare("INSERT INTO anonymous_sessions (owner_user_id, visitor_hash, anon_label) VALUES (?, ?, ?)");
            $stmt->execute([$ownerId, $visitorHash, $anonLabel]);
        } else {
            $anonLabel = $session['anon_label'] ?: $anonLabel;
        }

        $uniqueString = $ownerId . "_" . $visitorHash;
        $shadowUsername = "anon_" . md5($uniqueString);

        $stmt = $this->db->prepare("SELECT id FROM users WHERE username = ?");
        $stmt->execute([$shadowUsername]);
        $shadowUser = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($shadowUser) {
            return (int)$shadowUser['id'];
        }

        if (!$allowCreate) {
            return 0;
        }

        $email = $shadowUsername . '@anon.local';
        $passwordHash = password_hash(bin2hex(random_bytes(16)), PASSWORD_BCRYPT);
        $linkSlug = $shadowUsername;

        $stmt = $this->db->prepare("INSERT INTO users (email, password_hash, username, display_name, link_slug, status) VALUES (?, ?, ?, ?, ?, 'offline')");
        $stmt->execute([$email, $passwordHash, $shadowUsername, $anonLabel, $linkSlug]);
        return (int)$this->db->lastInsertId();
    }

    public function index($conversationId) {
        $rawUserId = AuthMiddleware::authenticate();
        $userId = $this->resolveUserId($rawUserId);
        
        $this->logDebug("INDEX conversation=$conversationId", ['rawUser' => $rawUserId, 'resolvedUser' => $userId]);

        $cursor = isset($_GET['cursor']) ? (int)$_GET['cursor'] : null;
        $limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 20;

        // Verify participation
        $stmt = $this->db->prepare("SELECT id, user_a_id, user_b_id, is_anonymous, anonymous_alias FROM conversations WHERE id = ?");
        $stmt->execute([$conversationId]);
        $conv = $stmt->fetch();

        if (!$conv) {
             $this->json(['error' => 'Conversation not found'], 404);
        }
        
        $userA = (int)$conv['user_a_id'];
        $userB = (int)$conv['user_b_id'];

        if ($userA !== $userId && $userB !== $userId) {
             $errorMsg = "Access denied. You (ID: $userId) are not in conversation $conversationId (Participants: $userA, $userB).";
             $this->logDebug("INDEX ERROR: $errorMsg");
             $this->json(['error' => $errorMsg], 403);
        }

        $stmtUser = $this->db->prepare("SELECT chat_history_cleared_at FROM users WHERE id = ? LIMIT 1");
        $stmtUser->execute([(int)$userId]);
        $userRow = $stmtUser->fetch(PDO::FETCH_ASSOC);
        $globalClear = ($userRow && $userRow['chat_history_cleared_at']) ? $userRow['chat_history_cleared_at'] : '1970-01-01 00:00:00';

        $stmtClear = $this->db->prepare("SELECT cleared_at FROM conversation_clears WHERE user_id = ? AND conversation_id = ? LIMIT 1");
        $stmtClear->execute([(int)$userId, (int)$conversationId]);
        $clearData = $stmtClear->fetch(PDO::FETCH_ASSOC);
        $clearedAt = $globalClear;
        if ($clearData && $clearData['cleared_at']) {
            if (strtotime($clearData['cleared_at']) > strtotime($globalClear)) {
                $clearedAt = $clearData['cleared_at'];
            }
        }

        $stmt = $this->db->prepare("
            SELECT m.*, u.username, u.display_name, u.avatar_url, m.sender_alias, m.is_anonymous 
            FROM messages m 
            LEFT JOIN users u ON m.sender_id = u.id 
            WHERE conversation_id = ?
        ");
        // Using LEFT JOIN because sender might be deleted or system message
        
        // Wait, index() logic:
        $sql = "
            SELECT m.*, u.username, u.display_name, u.avatar_url, m.sender_alias, m.is_anonymous 
            FROM messages m 
            LEFT JOIN users u ON m.sender_id = u.id 
            WHERE m.conversation_id = ?
              AND m.created_at > ?
        ";
        $params = [$conversationId, $clearedAt];

        if ($cursor) {
            $sql .= " AND m.id < ?";
            $params[] = $cursor;
        }

        $sql .= " ORDER BY m.id DESC LIMIT ?";
        $params[] = $limit;

        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        $messages = $stmt->fetchAll();
        
        // Transform messages to hide identity if anonymous
        foreach ($messages as &$msg) {
            $isMe = ($msg['sender_id'] == $userId);
            
            // 1. Base Sender Data
            $senderId = (int)$msg['sender_id'];
            $senderName = $msg['display_name'] ?? $msg['username'];
            $senderAvatar = $msg['avatar_url'];
            
            // 2. Anonymity Logic
            // If message is anonymous OR conversation is anonymous (safety net)
            // But usually message-level flag is the truth.
            $isAnonMsg = ($msg['is_anonymous'] == 1);
            
            if ($isAnonMsg) {
                // If I am NOT the sender, I see the Alias.
                if (!$isMe) {
                    $senderId = 0; // Hide Real ID
                    // Use Message Alias -> Conversation Alias -> Default
                    $senderName = $msg['sender_alias'] ?? $conv['anonymous_alias'] ?? 'Anonymous';
                    $senderAvatar = 'https://ui-avatars.com/api/?name=Anon&background=000&color=fff';
                } else {
                    // I am the sender.
                    // Remind me that I sent this anonymously.
                    $senderName = ($msg['sender_alias'] ?? $conv['anonymous_alias'] ?? 'Anonymous') . " (You)";
                    // I see my own avatar usually, or maybe placeholder to show "I look like this to them"?
                    // Let's keep my real avatar for me so I know it's me.
                }
            } else {
                // Not Anonymous.
                // If user was deleted (senderName is null), handle gracefully
                if (!$senderName) {
                    $senderName = "Unknown User";
                    $senderAvatar = 'https://ui-avatars.com/api/?name=Unk&background=ccc&color=fff';
                }
            }
            
            // 3. Populate Sender Object
            $msg['sender'] = [
                'id' => $senderId,
                'username' => $senderName,
                'display_name' => $senderName,
                'avatar_url' => $senderAvatar
            ];

            // 4. Update root level fields for backward compatibility (if needed)
            if ($isAnonMsg && !$isMe) {
                $msg['username'] = $senderName;
                $msg['display_name'] = $senderName;
                $msg['avatar_url'] = $senderAvatar;
                $msg['sender_id'] = 0;
            }
        }

        $this->json(array_reverse($messages));
    }

    public function send() {
        try {
            $rawUserId = AuthMiddleware::authenticate();
            $userId = $this->resolveUserId($rawUserId);
            $input = $this->input();
            
            $this->logDebug("SEND input", ['rawUser' => $rawUserId, 'resolvedUser' => $userId, 'input' => $input]);
            
            $newToken = null;
            $newVisitorId = null;
            $tokenOwnerId = null;

            // Check if we need to upgrade token
            if (is_string($rawUserId) && strpos($rawUserId, 'guest:') === 0) {
                 $parts = explode(':', $rawUserId);
                 if (count($parts) >= 4) {
                    $ownerId = $parts[1];
                    $tokenOwnerId = $ownerId;
                    $visitorHash = $parts[2];
                    $anonLabel = $parts[3];
                    
                    // Ensure Session Exists
                    $stmt = $this->db->prepare("SELECT id FROM anonymous_sessions WHERE owner_user_id = ? AND visitor_hash = ?");
                    $stmt->execute([$ownerId, $visitorHash]);
                    if (!$stmt->fetch()) {
                        $stmt = $this->db->prepare("INSERT INTO anonymous_sessions (owner_user_id, visitor_hash, anon_label) VALUES (?, ?, ?)");
                        $stmt->execute([$ownerId, $visitorHash, $anonLabel]);
                    }
                    
                    // Generate Real Token
                    $payload = [
                        'iss' => 'anon-chat',
                        'sub' => $userId,
                        'iat' => time(),
                        'exp' => time() + (60 * 60 * 24 * 30),
                        'scope' => 'anonymous',
                        'alias' => $anonLabel // Store alias in token
                    ];
                    $newToken = \App\Utils\JWT::encode($payload, \App\Utils\Env::get('JWT_SECRET'));
                    $newVisitorId = $userId;
                 }
            }

            $conversationId = isset($input['conversation_id']) ? $input['conversation_id'] : null;
            $recipientId = isset($input['recipient_id']) ? $input['recipient_id'] : null;

            if (!$recipientId && $tokenOwnerId) {
                $recipientId = $tokenOwnerId;
            }
            
            if (!$conversationId && !$recipientId) {
                 $this->logDebug("SEND ERROR: Missing conv_id and recipient_id");
                 $this->json(['error' => 'conversation_id or recipient_id required'], 422);
            }

            $bodyText = isset($input['body_text']) ? $input['body_text'] : null;
            $mediaUrl = isset($input['media_url']) ? $input['media_url'] : null;
            $mediaType = isset($input['media_type']) ? $input['media_type'] : null;

            if (!$bodyText && !$mediaUrl) {
                $this->json(['error' => 'Message must have text or media'], 422);
            }

            $senderAnonLabel = null;
            $isAnonymous = 0;
            $senderAlias = null;

            // Check if sending as anonymous (guest token OR anonymous JWT)
            $authPayload = \App\Middleware\AuthMiddleware::$currentPayload;
            
            if (is_string($rawUserId) && strpos($rawUserId, 'guest:') === 0) {
                $parts = explode(':', $rawUserId);
                if (count($parts) >= 4) {
                    $isAnonymous = 1;
                    $senderAlias = $parts[3]; // "Anon123"
                }
            } elseif (isset($authPayload['scope']) && $authPayload['scope'] === 'anonymous') {
                $isAnonymous = 1;
                $senderAlias = isset($authPayload['alias']) ? $authPayload['alias'] : 'Anonymous';
            }

            if (!$conversationId) {
                // Check if conversation exists between userId and recipientId
                $userA = min($userId, $recipientId);
                $userB = max($userId, $recipientId);
                
                // ARCHITECTURE FIX: Separate Anonymous Conversations from Real Ones
                $stmt = $this->db->prepare("SELECT id FROM conversations WHERE user_a_id = ? AND user_b_id = ? AND is_anonymous = ?");
                $stmt->execute([$userA, $userB, $isAnonymous]);
                $conv = $stmt->fetch();
                
                if ($conv) {
                    $conversationId = $conv['id'];
                } else {
                    // DO NOT CREATE CONVERSATION AUTOMATICALLY IF GUEST
                    // Only allowed if this is an explicit "Start Chat" action.
                    // But for send(), we imply start.
                    
                    // Generate Alias for the Initiator (User A)
                    $generatedAlias = $senderAlias;
                    if (!$generatedAlias && $isAnonymous) {
                        $generatedAlias = "Anonim-" . mt_rand(1000, 9999);
                        $senderAlias = $generatedAlias;
                    }

                    $stmt = $this->db->prepare("INSERT INTO conversations (user_a_id, user_b_id, is_anonymous, anonymous_alias, anonymous_participant_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, NOW(), NOW())");
                    $stmt->execute([$userA, $userB, $isAnonymous, $generatedAlias, $isAnonymous ? $userId : null]);
                    $conversationId = $this->db->lastInsertId();
                }
            }

            // Check access. If denied, try to find correct conversation or create new one
            if ($conversationId) {
                 $stmt = $this->db->prepare("SELECT id, user_a_id, user_b_id, is_anonymous FROM conversations WHERE id = ?");
                 $stmt->execute([$conversationId]);
                 $conv = $stmt->fetch();
                 
                 if ($conv) {
                     $userA = (int)$conv['user_a_id'];
                     $userB = (int)$conv['user_b_id'];
                     $convIsAnon = (int)$conv['is_anonymous'];
                     
                     // Access check: Users match AND anonymity mode matches
                     // Note: $userId is the SENDER. They must be A or B.
                     if (($userA !== $userId && $userB !== $userId)) {
                         // Access denied.
                         $this->logDebug("SEND WARN: Access denied to conv $conversationId. User $userId is not participant ($userA, $userB).");
                         $conversationId = null;
                     } elseif ($convIsAnon !== $isAnonymous) {
                         // Mode mismatch.
                         // BUT: If I am replying to an anonymous chat, I might be the "Real" user.
                         // If I am Real User (isAnonymous=0) replying to Anon Chat (isAnonymous=1), THAT IS ALLOWED.
                         // Only if I am Anon User (isAnonymous=1) replying to Real Chat (isAnonymous=0), that is suspicious/wrong context.
                         
                         if ($isAnonymous == 1 && $convIsAnon == 0) {
                             $this->logDebug("SEND WARN: Anon user trying to post in Real Chat.");
                             $conversationId = null;
                         }
                         // Else: Real User in Anon Chat -> OK.
                     }
                 } else {
                     $conversationId = null;
                 }
            }

            if (!$conversationId) {
                if ($recipientId) {
                    $userA = min($userId, $recipientId);
                    $userB = max($userId, $recipientId);
                    
                    $stmt = $this->db->prepare("SELECT id FROM conversations WHERE user_a_id = ? AND user_b_id = ? AND is_anonymous = ?");
                    $stmt->execute([$userA, $userB, $isAnonymous]);
                    $conv = $stmt->fetch();
                    
                    if ($conv) {
                        $conversationId = $conv['id'];
                    } else {
                        // Generate Alias for the Initiator (User A)
                        $generatedAlias = $senderAlias;
                        if (!$generatedAlias && $isAnonymous) {
                            $generatedAlias = "Anonim-" . mt_rand(1000, 9999);
                            $senderAlias = $generatedAlias;
                        }

                        // Create conversation
                        $stmt = $this->db->prepare("INSERT INTO conversations (user_a_id, user_b_id, is_anonymous, anonymous_alias, anonymous_participant_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, NOW(), NOW())");
                        $stmt->execute([$userA, $userB, $isAnonymous, $generatedAlias, $isAnonymous ? $userId : null]);
                        $conversationId = $this->db->lastInsertId();
                    }
                } else {
                     $this->logDebug("SEND ERROR: Unrecoverable access denied.");
                     $this->json([
                         'error' => 'Access denied to conversation and no recipient_id provided to recover.',
                     ], 403);
                }
            }

            $stmt = $this->db->prepare("SELECT id FROM conversations WHERE id = ?");
            $stmt->execute([$conversationId]);
            $conv = $stmt->fetch();
            
            if (!$conv) {
                 $this->logDebug("SEND ERROR: Conversation $conversationId does not exist (Deleted?)");
                 $this->json(['error' => 'Conversation not found'], 404);
            }

            // Fetch participants again properly to ensure receiver is correct
            $stmt = $this->db->prepare("SELECT user_a_id, user_b_id FROM conversations WHERE id = ?");
            $stmt->execute([$conversationId]);
            $conv = $stmt->fetch();
            $receiverId = ($conv['user_a_id'] == $userId) ? $conv['user_b_id'] : $conv['user_a_id'];

            // MUTUAL BLOCK CHECK
            $stmtBlock = $this->db->prepare("
                SELECT blocker_id
                FROM blocked_users
                WHERE (blocker_id = ? AND blocked_id = ?)
                   OR (blocker_id = ? AND blocked_id = ?)
                LIMIT 1
            ");
            $stmtBlock->execute([(int)$receiverId, (int)$userId, (int)$userId, (int)$receiverId]);
            $blockRecord = $stmtBlock->fetch(PDO::FETCH_ASSOC);
            if ($blockRecord) {
                if ((int)$blockRecord['blocker_id'] === (int)$userId) {
                    $this->json(['error' => 'You blocked this user. Unblock them to send a message.'], 403);
                } else {
                    $this->json(['error' => 'You have been blocked by this user.'], 403);
                }
            }

            // Get sender details with limits info
            $stmt = $this->db->prepare("SELECT display_name, is_seed, seed_label, username, is_plus_subscriber, daily_message_count, last_message_date, message_ad_credits FROM users WHERE id = ?");
            $stmt->execute([$userId]);
            $sender = $stmt->fetch();

            // FIX: Handle case where user might be deleted or not found
            if (!$sender) {
                 $sender = [
                     'display_name' => 'Unknown',
                     'is_seed' => 0,
                     'seed_label' => '',
                     'username' => 'unknown'
                 ];
            }

            // Legacy Shadow User Support
            if (!$isAnonymous && strpos($sender['username'], 'anon_') === 0) {
                $senderAnonLabel = $sender['display_name']; 
            }
            
            $stmt = $this->db->prepare("SELECT sender_id FROM messages WHERE conversation_id = ? ORDER BY id DESC LIMIT 10");
            $stmt->execute([$conversationId]);
            $lastMessages = $stmt->fetchAll(PDO::FETCH_COLUMN);

            $isPlus = (int)($sender['is_plus_subscriber'] ?? 0) === 1;

            // Consecutive Message Protection
            if (!$isPlus) {
                // EXEMPTION: Profile Share is allowed once if it's the first time
                $isProfileShare = ($mediaType === 'profile_share');
                
                $consecutiveLimit = 1; 
                $consecutiveCount = 0;
                foreach ($lastMessages as $msgSenderId) {
                    if ($msgSenderId == $userId) {
                        $consecutiveCount++;
                    } else {
                        break;
                    }
                }

                if ($consecutiveCount >= $consecutiveLimit) {
                    // Check if receiver has EVER replied in this specific conversation
                    $stmt = $this->db->prepare("SELECT COUNT(*) FROM messages WHERE conversation_id = ? AND sender_id = ?");
                    $stmt->execute([$conversationId, $receiverId]);
                    $replyCount = $stmt->fetchColumn();
                    
                    if ($replyCount == 0) {
                        // If it's a profile share, allow it ONCE even if consecutive limit hit
                        if ($isProfileShare) {
                            $stmtCheckShare = $this->db->prepare("SELECT COUNT(*) FROM messages WHERE conversation_id = ? AND sender_id = ? AND media_type = 'profile_share'");
                            $stmtCheckShare->execute([$conversationId, $userId]);
                            $alreadyShared = (int)$stmtCheckShare->fetchColumn();
                            
                            if ($alreadyShared > 0) {
                                $this->json(['error' => 'Profilini zaten paylaştın. Yeni bir mesaj atmak için karşı taraftan mesaj bekle.'], 429);
                            }
                            // Else: allow it to fall through to insertion
                        } else {
                            $this->json(['error' => 'Çok hızlı mesaj gönderiyorsun! Yeni bir mesaj atmak için karşı taraftan mesaj bekle.'], 429);
                        }
                    }
                }

                // Daily Message Limit (30 base + ad credits)
                $today = date('Y-m-d');
                $dailyCount = (int)($sender['daily_message_count'] ?? 0);
                $lastDate = $sender['last_message_date'] ?? null;
                $adCredits = (int)($sender['message_ad_credits'] ?? 0);

                if ($lastDate !== $today) {
                    $dailyCount = 0;
                    $adCredits = 0; // Reset ad credits daily too? Usually yes, start fresh.
                    $stmtReset = $this->db->prepare("UPDATE users SET daily_message_count = 0, last_message_date = ?, message_ad_credits = 0 WHERE id = ?");
                    $stmtReset->execute([$today, $userId]);
                }

                if ($dailyCount >= (30 + $adCredits)) {
                    $this->json(['error' => 'Günlük mesaj limitine ulaştın. Daha fazla mesaj için Plus\'a geçebilir veya reklam izleyebilirsin.', 'limit_reached' => true], 403);
                }
            }

            $stmt = $this->db->prepare("INSERT INTO messages (conversation_id, sender_id, receiver_id, body_text, media_url, media_type, sender_anonymous_label, sender_alias, is_anonymous, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())");
        
        // Ensure alias is consistent if conversation has one?
        if ($isAnonymous && !$senderAlias) {
             $senderAlias = "Anonim-" . mt_rand(1000, 9999);
        }

        // NAVIGATION FIX: Force media_url to be numeric userId for profile share
        if ($mediaType === 'profile_share') {
            $mediaUrl = (string)$userId;
        }

        $stmt->execute([$conversationId, $userId, $receiverId, $bodyText, $mediaUrl, $mediaType, $senderAnonLabel, $senderAlias, $isAnonymous]);
        $messageId = $this->db->lastInsertId();

        // Increment daily count if not Plus
        if (!$isPlus) {
            $stmtInc = $this->db->prepare("UPDATE users SET daily_message_count = daily_message_count + 1 WHERE id = ?");
            $stmtInc->execute([$userId]);
        }

            // Update conversation
            $stmt = $this->db->prepare("UPDATE conversations SET last_message_at = NOW(), last_message_id = ? WHERE id = ?");
            $stmt->execute([$messageId, $conversationId]);

            // Send Push Notification
            $this->sendPush($receiverId, $sender, $conversationId, $bodyText, $senderAlias ?: $senderAnonLabel);

            $this->json([
                'message_id' => $messageId, 
                'conversation_id' => $conversationId, 
                'created_at' => date('Y-m-d H:i:s'),
                'new_token' => $newToken, 
                'visitor_id' => $newVisitorId,
                // Force return user ID so client can update itself
                'force_user_id_update' => $userId
            ]);
        } catch (\Exception $e) {
            $this->logDebug("SEND EXCEPTION: " . $e->getMessage());
            $this->json(['error' => 'Internal Server Error', 'details' => $e->getMessage()], 500);
        }
    }

    private function sendPush($receiverId, $sender, $conversationId, $bodyText, $senderAnonLabel) {
        $stmt = $this->db->prepare("SELECT onesignal_player_id FROM device_players WHERE user_id = ?");
        $stmt->execute([$receiverId]);
        $device = $stmt->fetch();

        if ($device) {
            $title = $sender['display_name'];
            if ($sender['is_seed']) {
                $title = "[" . $sender['seed_label'] . "] " . $sender['display_name'];
            }
            if ($senderAnonLabel) {
                $title = $senderAnonLabel;
            }

            $msg = $bodyText ? $bodyText : 'Sent a photo/video';
            
            $this->oneSignal->sendToUser(
                $device['onesignal_player_id'],
                $title,
                $msg,
                ['conversation_id' => $conversationId, 'type' => 'message']
            );
        }
    }

    public function read() {
        $rawUserId = AuthMiddleware::authenticate();
        // Read should allow lazy create? No, if you haven't sent a message, you probably aren't in the conversation.
        // But if you received a push, you might be opening it.
        // If you received a push, you MUST already exist.
        $userId = $this->resolveUserId($rawUserId, false);
        
        if ($userId === 0) return; // Do nothing if user doesn't exist

        $input = $this->input();
        $conversationId = $input['conversation_id'];

        $stmt = $this->db->prepare("UPDATE messages SET is_read = 1, read_at = NOW() WHERE conversation_id = ? AND receiver_id = ? AND is_read = 0");
        $stmt->execute([$conversationId, $userId]);

        $this->json(['success' => true]);
    }

    public function since() {
        $rawUserId = AuthMiddleware::authenticate();
        $userId = $this->resolveUserId($rawUserId, false);
        
        if ($userId === 0) {
             $this->json([]); // Return empty
             return;
        }

        $conversationId = isset($_GET['conversation_id']) ? $_GET['conversation_id'] : null;
        $afterId = isset($_GET['after_id']) ? (int)$_GET['after_id'] : 0;

        if (!$conversationId) {
             $this->json(['error' => 'Missing conversation_id'], 422);
        }

        // Verify access
        $stmt = $this->db->prepare("SELECT id, user_a_id, user_b_id, is_anonymous, anonymous_alias FROM conversations WHERE id = ? AND (user_a_id = ? OR user_b_id = ?)");
        $stmt->execute([$conversationId, $userId, $userId]);
        $conv = $stmt->fetch();
        if (!$conv) {
             $this->json(['error' => 'Access denied'], 403);
        }

        $stmtUser = $this->db->prepare("SELECT chat_history_cleared_at FROM users WHERE id = ? LIMIT 1");
        $stmtUser->execute([(int)$userId]);
        $userRow = $stmtUser->fetch(PDO::FETCH_ASSOC);
        $globalClear = ($userRow && $userRow['chat_history_cleared_at']) ? $userRow['chat_history_cleared_at'] : '1970-01-01 00:00:00';

        $stmtClear = $this->db->prepare("SELECT cleared_at FROM conversation_clears WHERE user_id = ? AND conversation_id = ? LIMIT 1");
        $stmtClear->execute([(int)$userId, (int)$conversationId]);
        $clearData = $stmtClear->fetch(PDO::FETCH_ASSOC);
        $clearedAt = $globalClear;
        if ($clearData && $clearData['cleared_at']) {
            if (strtotime($clearData['cleared_at']) > strtotime($globalClear)) {
                $clearedAt = $clearData['cleared_at'];
            }
        }

        $stmt = $this->db->prepare("
            SELECT m.*, u.username, u.display_name, u.avatar_url, m.sender_alias, m.is_anonymous 
            FROM messages m 
            LEFT JOIN users u ON m.sender_id = u.id 
            WHERE conversation_id = ? AND m.id > ? AND m.created_at > ?
            ORDER BY m.id ASC
        ");
        $stmt->execute([$conversationId, $afterId, $clearedAt]);
        $messages = $stmt->fetchAll();

        // Transform messages to hide identity if anonymous
        foreach ($messages as &$msg) {
            $isMe = ($msg['sender_id'] == $userId);
            
            // 1. Base Sender Data
            $senderId = (int)$msg['sender_id'];
            $senderName = $msg['display_name'] ?? $msg['username'];
            $senderAvatar = $msg['avatar_url'];
            
            // 2. Anonymity Logic
            $isAnonMsg = ($msg['is_anonymous'] == 1);
            
            if ($isAnonMsg) {
                // If I am NOT the sender, I see the Alias.
                if (!$isMe) {
                    $senderId = 0; // Hide Real ID
                    // Use Message Alias -> Conversation Alias -> Default
                    $senderName = $msg['sender_alias'] ?? $conv['anonymous_alias'] ?? 'Anonymous';
                    $senderAvatar = 'https://ui-avatars.com/api/?name=Anon&background=000&color=fff';
                } else {
                    // I am the sender.
                    $senderName = ($msg['sender_alias'] ?? $conv['anonymous_alias'] ?? 'Anonymous') . " (You)";
                }
            } else {
                // Not Anonymous.
                if (!$senderName) {
                    $senderName = "Unknown User";
                    $senderAvatar = 'https://ui-avatars.com/api/?name=Unk&background=ccc&color=fff';
                }
            }
            
            // 3. Populate Sender Object
            $msg['sender'] = [
                'id' => $senderId,
                'username' => $senderName,
                'display_name' => $senderName,
                'avatar_url' => $senderAvatar
            ];

            // 4. Update root level fields for backward compatibility
            if ($isAnonMsg && !$isMe) {
                $msg['username'] = $senderName;
                $msg['display_name'] = $senderName;
                $msg['avatar_url'] = $senderAvatar;
                $msg['sender_id'] = 0;
            }
        }

        $this->json($messages);
    }

    public function typing() {
        $this->json(['success' => true]);
    }
}
