company_id !== 1 || $request->role !== 'admin') { $response->status(403)->json(['error' => 'Forbidden: Super Admin privileges required.']); return false; } return true; } /** * Get platform statistics and companies list * GET /api/admin/stats */ public function getStats(Request $request, Response $response): void { if (!$this->verifySuperAdmin($request, $response)) { return; } try { // Overall stats $companiesCount = Database::selectOne("SELECT COUNT(*) as count FROM companies")['count'] ?? 0; $sessionsCount = Database::selectOne("SELECT COUNT(*) as count FROM whatsapp_sessions")['count'] ?? 0; $connectedSessions = Database::selectOne("SELECT COUNT(*) as count FROM whatsapp_sessions WHERE status = 'connected'")['count'] ?? 0; // Detailed list of all companies and their current subscriptions $companies = Database::select(" SELECT c.id, c.name, c.status as company_status, c.created_at, cs.plan_id, cs.status as sub_status, cs.starts_at, cs.ends_at, cs.payment_method, cs.receipt_reference, p.name as plan_name, (SELECT COUNT(*) FROM whatsapp_sessions WHERE company_id = c.id) as sessions_count, (SELECT COUNT(*) FROM whatsapp_sessions WHERE company_id = c.id AND status = 'connected') as active_sessions, COALESCE(cu.request_count, 0) as request_usage, COALESCE(cu.voice_count, 0) as voice_usage, COALESCE(cu.ocr_count, 0) as ocr_usage FROM companies c LEFT JOIN company_subscriptions cs ON c.id = cs.company_id AND cs.status IN ('active', 'trialing', 'pending_approval') LEFT JOIN subscription_plans p ON cs.plan_id = p.id LEFT JOIN company_subscription_usage cu ON cu.company_id = c.id AND cu.billing_start <= CURRENT_DATE() AND cu.billing_end >= CURRENT_DATE() ORDER BY c.created_at DESC "); $pendingApprovals = []; $activeCompanies = []; foreach ($companies as $c) { if ($c['sub_status'] === 'pending_approval') { $pendingApprovals[] = $c; } else { $activeCompanies[] = $c; } } // 4. Fetch all available plans to allow admin to upgrade them manually $plans = Database::select("SELECT id, name, price FROM subscription_plans ORDER BY price ASC"); $response->json([ 'status' => 'success', 'data' => [ 'stats' => [ 'total_companies' => (int)$companiesCount, 'total_sessions' => (int)$sessionsCount, 'connected_sessions' => (int)$connectedSessions ], 'companies' => $activeCompanies, 'pending_approvals' => $pendingApprovals, 'plans' => $plans ] ]); } catch (\Exception $e) { error_log("[SuperAdminController Error] " . $e->getMessage()); $response->status(500)->json(['error' => 'Failed to fetch platform stats: ' . $e->getMessage()]); } } /** * Subscribe or upgrade a company to a plan * POST /api/admin/companies/subscribe */ public function subscribeCompany(Request $request, Response $response): void { if (!$this->verifySuperAdmin($request, $response)) { return; } $body = $request->getBody(); $targetCompanyId = isset($body['company_id']) ? (int)$body['company_id'] : null; $planId = isset($body['plan_id']) ? (int)$body['plan_id'] : null; $durationDays = isset($body['duration_days']) ? (int)$body['duration_days'] : 30; if (!$targetCompanyId || !$planId) { $response->status(400)->json(['error' => 'Missing company_id or plan_id']); return; } // Verify company exists $companyExists = Database::selectOne("SELECT id FROM companies WHERE id = ?", [$targetCompanyId]); if (!$companyExists) { $response->status(404)->json(['error' => 'Company not found']); return; } // Verify plan exists $planExists = Database::selectOne("SELECT id FROM subscription_plans WHERE id = ?", [$planId]); if (!$planExists) { $response->status(404)->json(['error' => 'Subscription plan not found']); return; } try { // Subscribe the company $subId = CompanySubscription::subscribeCompany($targetCompanyId, $planId, $durationDays, 'manual_admin', 'admin_' . $request->user_id); // Clean active subscription cache for the company if (class_exists('App\Core\Cache')) { \App\Core\Cache::delete("company_subscription:{$targetCompanyId}"); \App\Core\Cache::delete("company_subscription_{$targetCompanyId}"); } $response->json([ 'status' => 'success', 'message' => 'Subscription updated successfully', 'subscription_id' => $subId ]); } catch (\Exception $e) { error_log("[SuperAdminController Error] " . $e->getMessage()); $response->status(500)->json(['error' => 'Failed to update subscription: ' . $e->getMessage()]); } } /** * Approve a pending billing request * POST /api/admin/companies/approve-billing */ public function approveBilling(Request $request, Response $response): void { $body = $request->getBody(); $targetCompanyId = isset($body['company_id']) ? (int)$body['company_id'] : null; if (!$targetCompanyId) { $response->status(400)->json(['error' => 'Missing company_id']); return; } try { // Find the pending subscription $pending = Database::selectOne("SELECT * FROM company_subscriptions WHERE company_id = ? AND status = 'pending_approval' ORDER BY id DESC LIMIT 1", [$targetCompanyId]); if (!$pending) { $response->status(404)->json(['error' => 'No pending approval found for this company']); return; } // Deactivate other active/trialing subscriptions Database::execute("UPDATE company_subscriptions SET status = 'expired' WHERE company_id = ? AND status IN ('active', 'trialing')", [$targetCompanyId]); // Mark the pending one as active Database::execute("UPDATE company_subscriptions SET status = 'active' WHERE id = ?", [$pending['id']]); // Clear active subscription cache for the company if (class_exists('App\Core\Cache')) { \App\Core\Cache::delete("company_subscription_{$targetCompanyId}"); } $response->json([ 'status' => 'success', 'message' => 'Billing approved and subscription activated successfully' ]); } catch (\Exception $e) { $response->status(500)->json(['error' => 'Failed to approve billing: ' . $e->getMessage()]); } } /** * Export WhatsApp chats to a public text file * POST /api/admin/export-chats */ public function exportChats(Request $request, Response $response): void { if (!$this->verifySuperAdmin($request, $response)) { return; } // Get the WhatsApp session for the Super Admin company (ID 1) $session = \App\Models\WhatsAppSession::findByCompany(1); if (!$session) { $response->status(404)->json(['error' => 'Super Admin WhatsApp session not found']); return; } try { $sessionId = $session['id']; // 1. Fetch all logged messages for this session $messages = Database::select( "SELECT * FROM messages_log WHERE session_id = ? ORDER BY id ASC", [$sessionId] ); // 2. Fetch all contacts for Company 1 to map phone numbers to names $contactsList = Database::select( "SELECT name, phone FROM contacts WHERE company_id = 1" ); $contactNames = []; foreach ($contactsList as $c) { try { $decryptedPhone = Security::decrypt($c['phone']); $contactNames[$decryptedPhone] = $c['name']; } catch (\Exception $e) { // Skip decryption failure for this specific contact } } // 3. Construct the formatted chat log $outputText = "==================================================\n"; $outputText .= "سجل محادثات منصة نبيه - جلسة: " . $session['session_key'] . "\n"; $outputText .= "تاريخ التصدير: " . date('Y-m-d H:i:s') . "\n"; $outputText .= "==================================================\n\n"; if (empty($messages)) { $outputText .= "لا يوجد سجل رسائل متاح في قاعدة البيانات لهذه الجلسة.\n"; } else { // Group messages by decrypted contact phone number $chats = []; foreach ($messages as $msg) { try { $phone = Security::decrypt($msg['contact_phone']); } catch (\Exception $e) { $phone = 'unknown_' . ($msg['contact_phone_hash'] ?? $msg['id']); } if (!isset($chats[$phone])) { $chats[$phone] = []; } $chats[$phone][] = $msg; } // Format each chat group foreach ($chats as $phone => $chatMsgs) { $name = isset($contactNames[$phone]) ? $contactNames[$phone] : 'عميل غير مسمى'; $outputText .= "--------------------------------------------------\n"; $outputText .= "المحادثة مع: {$name} ({$phone})\n"; $outputText .= "--------------------------------------------------\n"; foreach ($chatMsgs as $msg) { $fromMe = $msg['direction'] === 'outbound'; $sender = $fromMe ? 'المنصة (نبيه)' : $name; try { $body = Security::decrypt($msg['message_body']); } catch (\Exception $e) { $body = '[خطأ في فك تشفير الرسالة]'; } $dateStr = $msg['created_at']; if ($msg['message_type'] === 'text') { $outputText .= "[{$dateStr}] {$sender}: {$body}\n"; } else if ($msg['message_type'] === 'audio') { $outputText .= "[{$dateStr}] {$sender}: [رسالة صوتية] " . ($body ? "- {$body}" : "") . "\n"; } else if ($msg['message_type'] === 'image') { $outputText .= "[{$dateStr}] {$sender}: [صورة] " . ($body ? "- {$body}" : "") . "\n"; } else { $outputText .= "[{$dateStr}] {$sender}: [" . $msg['message_type'] . "] " . ($body ? "- {$body}" : "") . "\n"; } } $outputText .= "\n\n"; } } // 4. Write the file to public directory $publicDir = __DIR__ . '/../../public'; $filePath = $publicDir . '/whatsapp_chats_history.txt'; file_put_contents($filePath, $outputText); $response->json([ 'status' => 'success', 'message' => 'Chat history exported successfully from database', 'download_url' => '/whatsapp_chats_history.txt' ]); } catch (\Exception $e) { error_log("[SuperAdminController Export Error] " . $e->getMessage()); $response->status(500)->json([ 'error' => 'Failed to export chats from database: ' . $e->getMessage() ]); } } }