<?php

namespace Core\App\Controllers;

use Core\App\Models\TeamMessageModel;
use Core\App\Models\TeamMeetingModel;
use Core\App\Models\AdminActivityModel;
use Core\App\Models\TeamAnnouncementModel;
use Core\App\Models\TeamMemberModel;
use Core\App\Models\TeamGroupMessageModel;
use Core\App\Models\AdminNotificationModel;
use Core\App\Services\MeetingReminderService;
use Core\App\Services\WhatsAppService;
use Core\System\Mailer;

class TeamCommunicationController
{
    private TeamMessageModel      $messages;
    private TeamMeetingModel      $meetings;
    private AdminActivityModel    $activity;
    private TeamAnnouncementModel $announcements;
    private TeamGroupMessageModel $groupMessages;
    private AdminNotificationModel $notifications;

    private int    $adminId;
    private string $adminRole;

    public function __construct()
    {
        $this->requireAuth();
        $this->messages      = new TeamMessageModel();
        $this->meetings      = new TeamMeetingModel();
        $this->activity      = new AdminActivityModel();
        $this->announcements = new TeamAnnouncementModel();
        $this->groupMessages = new TeamGroupMessageModel();
        $this->notifications = new AdminNotificationModel();
    }

    /* =====================================================
     * Auth Guard
     * ===================================================== */
    private function requireAuth(): void
    {
        if (session_status() === PHP_SESSION_NONE) session_start();
        if (empty($_SESSION['admin_id'])) {
            header('Location: /admin/login');
            exit;
        }
        $this->adminId   = (int)$_SESSION['admin_id'];
        $this->adminRole = (string)($_SESSION['admin_role'] ?? 'owner');
    }

    /* =====================================================
     * CSRF Check
     * ===================================================== */
    private function checkCsrf(): bool
    {
        if (session_status() === PHP_SESSION_NONE) session_start();
        $token = $_POST['_csrf'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
        return $token !== ''
            && !empty($_SESSION['_csrf'])
            && hash_equals((string)$_SESSION['_csrf'], trim($token));
    }

    private function isManager(): bool
    {
        return in_array($this->adminRole, ['owner', 'manager'], true);
    }

    /* =====================================================
     * File Upload Helper
     * Max 5MB — images + common documents only
     * ===================================================== */
    private function handleUpload(string $inputName): ?array
    {
        if (!isset($_FILES[$inputName]) || (int)$_FILES[$inputName]['error'] !== UPLOAD_ERR_OK) {
            return null;
        }

        $file    = $_FILES[$inputName];
        $maxSize = 8 * 1024 * 1024;
        if ((int)$file['size'] > $maxSize || (int)$file['size'] === 0 || !is_uploaded_file($file['tmp_name'])) {
            return null;
        }

        $finfo    = new \finfo(FILEINFO_MIME_TYPE);
        $mimeType = strtolower((string)$finfo->file($file['tmp_name']));
        $origName = (string)($file['name'] ?? 'file');
        $ext      = strtolower((string)pathinfo($origName, PATHINFO_EXTENSION));

        if (in_array($ext, ['php','phtml','phar','cgi','pl','py','js','sh','exe','bat','cmd','com','scr','msi','jar','zip','rar','7z','tar','gz'], true)) {
            return null;
        }

        $imageExts = ['jpg','jpeg','png','gif','webp'];
        $docExts   = ['pdf','doc','docx','xls','xlsx','txt'];
        $imageMimes = ['image/jpeg','image/png','image/gif','image/webp'];
        $docMimes   = [
            'application/pdf',
            'application/msword',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'application/vnd.ms-excel',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'text/plain',
        ];

        if (preg_match('#(zip|rar|7z|gzip|x-7z|x-rar|compressed|octet-stream)#i', $mimeType)) {
            return null;
        }

        $kind = null;
        if (in_array($mimeType, $imageMimes, true) && in_array($ext, $imageExts, true)) {
            $kind = 'image';
            if (@getimagesize($file['tmp_name']) === false) {
                return null;
            }
        } elseif (in_array($mimeType, $docMimes, true) && in_array($ext, $docExts, true)) {
            $kind = 'document';
        } else {
            return null;
        }

        $head = (string)@file_get_contents($file['tmp_name'], false, null, 0, 512);
        if ($head !== '' && preg_match('/<\?(php|=)|<script\b|<html\b|eval\s*\(|base64_decode\s*\(/i', $head)) {
            return null;
        }

        $safeFilename = uniqid('tm_', true) . '.' . $ext;
        $uploadDir    = ROOT_PATH . '/uploads/team/';
        if (!is_dir($uploadDir) && !mkdir($uploadDir, 0755, true) && !is_dir($uploadDir)) {
            return null;
        }

        $destPath = $uploadDir . $safeFilename;
        if (!move_uploaded_file($file['tmp_name'], $destPath)) {
            return null;
        }
        @chmod($destPath, 0644);

        return [
            'path' => '/uploads/team/' . $safeFilename,
            'type' => $mimeType,
            'kind' => $kind,
            'name' => basename($origName),
            'size' => (int)$file['size'],
        ];
    }

    /* =====================================================
     * GET /admin/team/communication
     * Messaging hub — list of conversations
     * ===================================================== */
    public function index(): void
    {
        $this->activity->updatePresence($this->adminId);

        $admins           = $this->messages->getAdminsForChat($this->adminId);
        $unreadTotal      = $this->messages->totalUnread($this->adminId);
        $announcements    = $this->announcements->getAll(10);
        $upcomingMeetings = $this->meetings->getUpcoming();
        $deletionAudit    = $this->isManager() ? $this->messages->getDeletionAudit(18) : [];

        require ROOT_PATH . '/admin/templates/team/communication.php';
    }

    /* =====================================================
     * GET /admin/team/communication/{id}
     * Direct chat with a specific admin
     * ===================================================== */
    public function chat(int $otherId): void
    {
        $this->activity->updatePresence($this->adminId);
        $this->messages->markSeenByConversation($otherId, $this->adminId);

        $admins   = $this->messages->getAdminsForChat($this->adminId);
        $messages = $this->messages->getConversation($this->adminId, $otherId, 60);

        $otherAdmin = null;
        foreach ($admins as $a) {
            if ((int)$a['id'] === $otherId) {
                $otherAdmin = $a;
                break;
            }
        }

        if (!$otherAdmin) {
            http_response_code(404);
            echo 'Admin not found';
            return;
        }

        $unreadTotal      = $this->messages->totalUnread($this->adminId);
        $announcements    = $this->announcements->getAll(10);
        $upcomingMeetings = $this->meetings->getUpcoming();

        require ROOT_PATH . '/admin/templates/team/chat.php';
    }

    /* =====================================================
     * POST /admin/team/messages/send  (AJAX — multipart)
     * ===================================================== */
    public function sendMessage(): void
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!$this->checkCsrf()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'invalid_csrf']);
            return;
        }

        $receiverId = (int)($_POST['receiver_id'] ?? 0);
        $content    = trim((string)($_POST['content'] ?? ''));

        if ($receiverId <= 0) {
            http_response_code(422);
            echo json_encode(['success' => false, 'error' => 'invalid_input']);
            return;
        }

        if ($receiverId === $this->adminId) {
            http_response_code(422);
            echo json_encode(['success' => false, 'error' => 'cannot_message_self']);
            return;
        }

        // Handle optional file attachment
        $attachment = $this->handleUpload('attachment');

        // At least text or file required
        if ($content === '' && $attachment === null) {
            http_response_code(422);
            echo json_encode(['success' => false, 'error' => 'empty_message']);
            return;
        }

        $id  = $this->messages->send($this->adminId, $receiverId, $content, $attachment);
        $msg = $this->messages->getById($id);
        $this->createAdminNotification('team_message', $id, __('team_comm.new_message', 'New message') . ': ' . ($msg['sender_name'] ?? ''), $receiverId);
        $this->notifyMentions($content, $id, 'team_mention');

        echo json_encode(['success' => true, 'message' => $msg], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
    }

    public function pinMessage(int $id): void
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!$this->checkCsrf()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'invalid_csrf']);
            return;
        }

        $otherId = (int)($_POST['other_id'] ?? 0);
        if ($otherId <= 0) {
            http_response_code(422);
            echo json_encode(['success' => false, 'error' => 'invalid_other']);
            return;
        }

        $ok = $this->messages->togglePin($id, $this->adminId, $otherId);
        echo json_encode(['success' => $ok]);
    }

    public function deleteMessage(int $id): void
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!$this->checkCsrf()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'invalid_csrf']);
            return;
        }

        $message = $this->messages->deleteMessage($id, $this->adminId, $this->isManager());
        if (!$message) {
            http_response_code(404);
            echo json_encode(['success' => false, 'error' => 'not_found']);
            return;
        }

        $conversationPartner = (int)$message['sender_id'] === $this->adminId
            ? (int)$message['receiver_id']
            : (int)$message['sender_id'];

        if ($conversationPartner > 0) {
            $this->createAdminNotification(
                'team_message_deleted',
                $id,
                __('team_comm.message_deleted_notification', 'A team message was deleted'),
                $conversationPartner
            );
        }

        $this->createAdminNotification(
            'team_message_deleted',
            $id,
            __('team_comm.message_deleted_manager_alert', 'A message was deleted and kept for manager review')
        );

        echo json_encode(['success' => true, 'message' => $message], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
    }

    /* =====================================================
     * GET /admin/team/messages/poll/{id}  (AJAX)
     * ===================================================== */
    public function pollMessages(int $otherId): void
    {
        header('Content-Type: application/json; charset=utf-8');

        $lastId      = (int)($_GET['last_id'] ?? 0);
        $newMessages = $this->messages->getAfter($this->adminId, $otherId, $lastId);

        $this->messages->markDelivered($otherId, $this->adminId);
        if (!empty($newMessages)) {
            $this->messages->markSeenByConversation($otherId, $this->adminId);
        }

        echo json_encode([
            'success'  => true,
            'messages' => $newMessages,
            'unread'   => $this->messages->totalUnread($this->adminId),
        ]);
    }

    /* =====================================================
     * GET /admin/team/messages/unread-count  (AJAX)
     * ===================================================== */
    public function unreadCount(): void
    {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['unread' => $this->messages->totalUnread($this->adminId)]);
    }

    /* =====================================================
     * GET /admin/team/meetings
     * ===================================================== */
    public function meetings(): void
    {
        $this->activity->updatePresence($this->adminId);
        $this->meetings->markOverdueCompleted();
        $this->meetings->createStartNotificationIfDue();

        $allMeetings   = $this->meetings->getAll(100, $this->adminId);
        $announcements = $this->announcements->getAll(10);
        $unreadTotal   = $this->messages->totalUnread($this->adminId);

        require ROOT_PATH . '/admin/templates/team/meetings.php';
    }

    /* =====================================================
     * GET /admin/team/meetings/create
     * ===================================================== */
    public function createMeeting(): void
    {
        if (!$this->isManager()) {
            header('Location: /admin/team/meetings');
            exit;
        }

        $teamMembers = (new TeamMemberModel())->getAll();
        $meeting     = null;
        $unreadTotal = $this->messages->totalUnread($this->adminId);

        require ROOT_PATH . '/admin/templates/team/meeting_form.php';
    }

    /* =====================================================
     * POST /admin/team/meetings/store
     * ===================================================== */
    public function storeMeeting(): void
    {
        if (!$this->isManager() || !$this->checkCsrf()) {
            header('Location: /admin/team/meetings');
            exit;
        }

        $data = [
            'created_by'       => $this->adminId,
            'title'            => trim((string)($_POST['title'] ?? '')),
            'description'      => trim((string)($_POST['description'] ?? '')),
            'meeting_type'     => $_POST['meeting_type'] ?? 'other',
            'scheduled_at'     => $_POST['scheduled_at'] ?? '',
            'duration_minutes' => (int)($_POST['duration_minutes'] ?? 60),
            'location'         => trim((string)($_POST['location'] ?? '')),
            'has_group_chat'   => !empty($_POST['has_group_chat']),
            'reminder_minutes' => (int)($_POST['reminder_minutes'] ?? 0),
        ];

        $attendeeIds = array_map('intval', (array)($_POST['attendees'] ?? []));

        if ($data['title'] === '' || $data['scheduled_at'] === '') {
            header('Location: /admin/team/meetings/create?error=missing_fields');
            exit;
        }

        $scheduledAtUtc = app_local_to_utc((string)$data['scheduled_at']);
        if ($scheduledAtUtc === null) {
            header('Location: /admin/team/meetings/create?error=invalid_datetime');
            exit;
        }

        $data['scheduled_at'] = $scheduledAtUtc;
        $this->meetings->create($data, $attendeeIds);
        header('Location: /admin/team/meetings?success=meeting_created');
        exit;
    }

    /* =====================================================
     * GET /admin/team/meetings/edit/{id}
     * ===================================================== */
    public function editMeeting(int $id): void
    {
        if (!$this->isManager()) {
            header('Location: /admin/team/meetings');
            exit;
        }

        $meeting = $this->meetings->getById($id);
        if ($meeting && ($meeting['status'] ?? '') === 'completed') {
            header('Location: /admin/team/meetings');
            exit;
        }
        if (!$meeting) {
            header('Location: /admin/team/meetings');
            exit;
        }

        $teamMembers = (new TeamMemberModel())->getAll();
        $unreadTotal = $this->messages->totalUnread($this->adminId);

        require ROOT_PATH . '/admin/templates/team/meeting_form.php';
    }

    /* =====================================================
     * POST /admin/team/meetings/update/{id}
     * ===================================================== */
    public function updateMeeting(int $id): void
    {
        if (!$this->isManager() || !$this->checkCsrf()) {
            header('Location: /admin/team/meetings');
            exit;
        }

        $data = [
            'title'            => trim((string)($_POST['title'] ?? '')),
            'description'      => trim((string)($_POST['description'] ?? '')),
            'meeting_type'     => $_POST['meeting_type'] ?? 'other',
            'scheduled_at'     => $_POST['scheduled_at'] ?? '',
            'duration_minutes' => (int)($_POST['duration_minutes'] ?? 60),
            'location'         => trim((string)($_POST['location'] ?? '')),
            'status'           => $_POST['status'] ?? 'upcoming',
            'has_group_chat'   => !empty($_POST['has_group_chat']),
            'reminder_minutes' => (int)($_POST['reminder_minutes'] ?? 0),
        ];

        $attendeeIds = array_map('intval', (array)($_POST['attendees'] ?? []));

        $scheduledAtUtc = app_local_to_utc((string)$data['scheduled_at']);
        if ($scheduledAtUtc === null) {
            header('Location: /admin/team/meetings/edit/' . $id . '?error=invalid_datetime');
            exit;
        }

        $data['scheduled_at'] = $scheduledAtUtc;
        $this->meetings->update($id, $data, $attendeeIds);
        header('Location: /admin/team/meetings?success=meeting_updated');
        exit;
    }

    /* =====================================================
     * POST /admin/team/meetings/delete/{id}
     * ===================================================== */
    public function deleteMeeting(int $id): void
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!$this->isManager() || !$this->checkCsrf()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'forbidden']);
            return;
        }

        $this->meetings->delete($id);
        echo json_encode(['success' => true], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
    }

    /* =====================================================
     * POST /admin/team/meetings/rsvp/{id}  (AJAX)
     * ===================================================== */
    public function rsvpMeeting(int $id): void
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!$this->checkCsrf()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'invalid_csrf']);
            return;
        }

        $rsvp = $_POST['rsvp'] ?? '';
        if (!in_array($rsvp, ['accepted', 'declined', 'pending'], true)) {
            http_response_code(422);
            echo json_encode(['success' => false, 'error' => 'invalid_rsvp']);
            return;
        }

        $this->meetings->rsvp($id, $this->adminId, $rsvp);
        echo json_encode(['success' => true, 'rsvp' => $rsvp]);
    }

    /* =====================================================
     * GET /admin/team/group-chat/{id}
     * Group chat for a meeting
     * ===================================================== */
    public function groupChat(int $meetingId): void
    {
        $this->activity->updatePresence($this->adminId);

        $meeting = $this->meetings->getById($meetingId);

        if (!$meeting || empty($meeting['has_group_chat'])) {
            header('Location: /admin/team/meetings');
            exit;
        }

        // Only attendees or managers can access
        if (!$this->isManager() && !$this->groupMessages->isAttendee($meetingId, $this->adminId)) {
            header('Location: /admin/team/meetings');
            exit;
        }

        $groupMsgs    = $this->groupMessages->getMessages($meetingId, 60);
        $lastGroupId  = !empty($groupMsgs) ? (int)end($groupMsgs)['id'] : 0;
        $unreadTotal  = $this->messages->totalUnread($this->adminId);

        require ROOT_PATH . '/admin/templates/team/group_chat.php';
    }

    /* =====================================================
     * POST /admin/team/group-chat/send  (AJAX — multipart)
     * ===================================================== */
    public function sendGroupMessage(): void
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!$this->checkCsrf()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'invalid_csrf']);
            return;
        }

        $meetingId = (int)($_POST['meeting_id'] ?? 0);
        $content   = trim((string)($_POST['content'] ?? ''));

        if ($meetingId <= 0) {
            http_response_code(422);
            echo json_encode(['success' => false, 'error' => 'invalid_meeting']);
            return;
        }

        $meeting = $this->meetings->getById($meetingId);
        if (!$meeting) {
            http_response_code(404);
            echo json_encode(['success' => false, 'error' => 'invalid_meeting']);
            return;
        }

        // Access check: must be attendee or manager
        if (!$this->isManager() && !$this->groupMessages->isAttendee($meetingId, $this->adminId)) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'forbidden']);
            return;
        }

        $attachment = $this->handleUpload('attachment');

        if ($content === '' && $attachment === null) {
            http_response_code(422);
            echo json_encode(['success' => false, 'error' => 'empty_message']);
            return;
        }

        $id  = $this->groupMessages->send($meetingId, $this->adminId, $content, $attachment);
        $this->meetings->touchActivity($meetingId);
        $msg = $this->groupMessages->getById($id);
        $this->createAdminNotification('group_message', $meetingId, __('team_comm.new_group_message', 'New group message') . ': ' . ($meeting['title'] ?? ('#' . $meetingId)));
        $this->notifyMentions($content, $meetingId, 'team_group_mention');

        echo json_encode(['success' => true, 'message' => $msg], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
    }

    public function pinGroupMessage(int $id): void
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!$this->checkCsrf()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'invalid_csrf']);
            return;
        }

        $meetingId = (int)($_POST['meeting_id'] ?? 0);
        if ($meetingId <= 0) {
            http_response_code(422);
            echo json_encode(['success' => false, 'error' => 'invalid_meeting']);
            return;
        }

        if (!$this->isManager() && !$this->groupMessages->isAttendee($meetingId, $this->adminId)) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'forbidden']);
            return;
        }

        $ok = $this->groupMessages->togglePin($id, $meetingId);
        echo json_encode(['success' => $ok]);
    }

    /* =====================================================
     * GET /admin/team/group-chat/poll/{id}  (AJAX)
     * ===================================================== */
    public function pollGroupMessages(int $meetingId): void
    {
        header('Content-Type: application/json; charset=utf-8');

        // Access check
        if (!$this->isManager() && !$this->groupMessages->isAttendee($meetingId, $this->adminId)) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'forbidden']);
            return;
        }

        $lastId = (int)($_GET['last_id'] ?? 0);
        $msgs   = $this->groupMessages->getAfter($meetingId, $lastId);

        echo json_encode(['success' => true, 'messages' => $msgs]);
    }

    /* =====================================================
     * GET /admin/team/activity
     * ===================================================== */
    public function activity(): void
    {
        if (!$this->isManager()) {
            header('Location: /admin/team/communication');
            exit;
        }

        $this->activity->updatePresence($this->adminId);

        $onlineAdmins = $this->activity->getOnlineAdmins();
        $allAdmins    = $this->activity->getAllWithPresence();
        $activityLog  = $this->activity->getLog(100);
        $summary      = $this->activity->getSummaryPerAdmin(30);
        $unreadTotal  = $this->messages->totalUnread($this->adminId);

        require ROOT_PATH . '/admin/templates/team/activity.php';
    }


    public function storeAnnouncement(): void
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!$this->isManager() || !$this->checkCsrf()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'forbidden']);
            return;
        }

        $title = trim((string)($_POST['title'] ?? ''));
        $content = trim((string)($_POST['content'] ?? ''));
        $isPinned = !empty($_POST['is_pinned']);

        if ($title === '' || $content === '') {
            http_response_code(422);
            echo json_encode(['success' => false, 'error' => 'missing_fields']);
            return;
        }

        $id = $this->announcements->create($this->adminId, $title, $content, $isPinned);
        echo json_encode(['success' => true, 'id' => $id]);
    }

    public function deleteAnnouncement(int $id): void
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!$this->isManager() || !$this->checkCsrf()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'forbidden']);
            return;
        }

        $row = $this->announcements->getById($id);
        if (!$row) {
            http_response_code(404);
            echo json_encode(['success' => false, 'error' => 'not_found']);
            return;
        }

        $this->announcements->delete($id);
        echo json_encode(['success' => true], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
    }

    public function pinAnnouncement(int $id): void
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!$this->isManager() || !$this->checkCsrf()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'forbidden']);
            return;
        }

        $row = $this->announcements->getById($id);
        if (!$row) {
            http_response_code(404);
            echo json_encode(['success' => false, 'error' => 'not_found']);
            return;
        }

        $this->announcements->togglePin($id);
        echo json_encode(['success' => true], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
    }

    public function completeMeeting(int $id): void
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!$this->isManager() || !$this->checkCsrf()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'forbidden']);
            return;
        }

        $meeting = $this->meetings->getById($id);
        if ($meeting && ($meeting['status'] ?? '') === 'completed') {
            header('Location: /admin/team/meetings');
            exit;
        }
        if (!$meeting) {
            http_response_code(404);
            echo json_encode(['success' => false, 'error' => 'not_found']);
            return;
        }

        $this->meetings->complete($id, $this->adminId, 'manual_manager');
        echo json_encode(['success' => true], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
    }


    public function meetingRoom(int $id): void
    {
        $this->activity->updatePresence($this->adminId);
        $this->meetings->markOverdueCompleted();
        $this->meetings->createStartNotificationIfDue();

        $meeting = $this->meetings->getById($id);
        if (!$meeting) {
            header('Location: /admin/team/meetings');
            exit;
        }

        $isAttendee = false;
        foreach (($meeting['attendees'] ?? []) as $attendee) {
            if ((int)($attendee['admin_id'] ?? 0) === $this->adminId) {
                $isAttendee = true;
                break;
            }
        }

        if (!$this->isManager() && !$isAttendee) {
            header('Location: /admin/team/meetings');
            exit;
        }

        $groupMsgs        = $this->groupMessages->getMessages($id, 100);
        $lastGroupId      = !empty($groupMsgs) ? (int)end($groupMsgs)['id'] : 0;
        $csrfToken        = \Core\System\Csrf::token();
        $currentAdminId   = $this->adminId;
        $currentAdminRole = $this->adminRole;
        $isManager        = $this->isManager();
        $myRsvp           = $this->meetings->getRsvp($id, $this->adminId) ?? 'pending';
        $themeSettings    = (new \Core\App\Models\ThemeSettings())->getAll();

        // Video meeting integration
        $videoEnabled  = \Core\App\Services\VideoMeetingService::isEnabled();
        $videoProvider = $videoEnabled ? \Core\App\Services\VideoMeetingService::getActiveProviderKey() : '';

        require ROOT_PATH . '/admin/templates/team/meeting_room.php';
    }

    /* =====================================================
     * POST /admin/team/presence/heartbeat  (AJAX)
     * Called by JS every 60s — updates presence +
     * triggers meeting reminders (pseudo-cron, no server cron needed)
     * ===================================================== */
    public function heartbeat(): void
    {
        header('Content-Type: application/json; charset=utf-8');
        $this->activity->updatePresence($this->adminId);
        $this->meetings->markOverdueCompleted();
        $this->meetings->createStartNotificationIfDue();
        $this->sendMeetingReminders();
        echo json_encode(['ok' => true, 'team_unread' => $this->messages->totalUnread($this->adminId)], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
    }

    /* =====================================================
     * Meeting Reminders — triggered by heartbeat every 60s
     * Race-condition safe via atomic UPDATE in model
     * Sends: email (all attendees) + WhatsApp (if phone set)
     * Logs: automatically via WhatsAppService::send()
     * ===================================================== */
    private function sendMeetingReminders(): void
    {
        try {
            (new MeetingReminderService())->run('heartbeat');
        } catch (\Throwable $e) {
        }
    }

    private function createAdminNotification(string $type, int $referenceId, string $title, ?int $recipientAdminId = null): void
    {
        $this->notifications->create($type, $referenceId, $title, $recipientAdminId);
    }

    private function notifyMentions(string $content, int $referenceId, string $type): void
    {
        if ($content === '' || !preg_match_all('/@([\p{L}0-9_\-.]{2,50})/u', $content, $m)) {
            return;
        }
        $usernames = array_unique(array_map('mb_strtolower', $m[1]));
        if (empty($usernames)) {
            return;
        }
        try {
            $placeholders = implode(',', array_fill(0, count($usernames), '?'));
            $stmt = \db()->prepare("SELECT id, username FROM admins WHERE LOWER(username) IN ($placeholders)");
            $stmt->execute(array_values($usernames));
            foreach (($stmt->fetchAll(\PDO::FETCH_ASSOC) ?: []) as $admin) {
                if ((int)$admin['id'] === $this->adminId) {
                    continue;
                }
                $this->createAdminNotification($type, $referenceId, __('team_comm.mention_notification', 'You were mentioned') . ': @' . $admin['username'], (int)$admin['id']);
            }
        } catch (\Throwable $e) {
        }
    }
}
