get('id'); $actorRole = $actorRole ?? session()->get('role') ?? 'guest'; $request = service('request'); $ipAddress = $ipAddress ?? ($request->hasHeader('X-Forwarded-For') ? trim(explode(',', $request->getHeaderLine('X-Forwarded-For'))[0]) : $request->getIPAddress()); $userAgent = $userAgent ?? ($request->hasHeader('User-Agent') ? $request->getHeaderLine('User-Agent') : null); $validTargetTypes = ['admin', 'doctor', 'patient']; $targetUserType = in_array($targetType, $validTargetTypes, true) ? $targetType : null; $data = [ 'ip' => $ipAddress, 'action' => $action, 'description' => $description, 'activity_user_id' => $actorId, 'activity_user_type' => $actorRole, 'target_user_id' => $targetId, 'target_user_type' => $targetUserType, 'activity_page' => $request->getPath(), 'activity_at' => date('Y-m-d H:i:s'), ]; try { $result = $this->insert($data); if (!$result) { log_message('error', 'Failed to insert activity log: ' . json_encode($data) . ' - Errors: ' . json_encode($this->errors())); } return (bool) $result; } catch (\Exception $e) { log_message('error', 'Exception in ActivityLogModel::log: ' . $e->getMessage()); return false; } } public function getFilteredLogs(array $filters = [], int $limit = 200): array { $builder = $this->select('activity_logs.*, COALESCE(NULLIF(CONCAT(users.first_name, " ", users.last_name), " "), users.email, "Guest") AS actor_name') ->join('users', 'users.id = activity_logs.activity_user_id', 'left'); // Apply filters if (!empty($filters['search'])) { $search = $filters['search']; $builder->groupStart() ->like('users.first_name', $search) ->orLike('users.last_name', $search) ->orLike('users.email', $search) ->orLike('activity_logs.description', $search) ->groupEnd(); } if (!empty($filters['action'])) { $builder->where('activity_logs.action', $filters['action']); } if (!empty($filters['user_type'])) { $builder->where('activity_logs.activity_user_type', $filters['user_type']); } if (!empty($filters['date_from'])) { $builder->where('activity_logs.activity_at >=', $filters['date_from'] . ' 00:00:00'); } if (!empty($filters['date_to'])) { $builder->where('activity_logs.activity_at <=', $filters['date_to'] . ' 23:59:59'); } if (!empty($filters['ip'])) { $builder->where('activity_logs.ip', $filters['ip']); } return $builder->orderBy('activity_logs.activity_at', 'DESC') ->findAll($limit); } public function getAvailableActions(): array { $rows = $this->select('action') ->distinct() ->orderBy('action', 'ASC') ->findAll(); return array_column($rows, 'action'); } public function getActionSummary(): array { $rows = $this->select('action, COUNT(*) AS count') ->groupBy('action') ->orderBy('count', 'DESC') ->findAll(); return array_column($rows, 'count', 'action'); } public function getRoleSummary(): array { $rows = $this->select('activity_user_type AS actor_role, COUNT(*) AS count') ->groupBy('activity_user_type') ->orderBy('count', 'DESC') ->findAll(); return array_column($rows, 'count', 'actor_role'); } public function getSummary(string $period = '7_days'): array { $period = $period === '30_days' ? '30_days' : '7_days'; $startDate = $period === '30_days' ? date('Y-m-d H:i:s', strtotime('-30 days')) : date('Y-m-d H:i:s', strtotime('-7 days')); return [ 'total_actions' => (int) $this->where('activity_at >=', $startDate)->countAllResults(false), 'by_action' => $this->getActionSummaryForPeriod($startDate), 'by_role' => $this->getRoleSummaryForPeriod($startDate), 'most_active_users' => $this->getMostActiveUsers($startDate), 'unique_ips' => $this->getUniqueIPs($startDate), ]; } protected function getActionSummaryForPeriod(string $startDate): array { $rows = $this->select('action, COUNT(*) AS count') ->where('activity_at >=', $startDate) ->groupBy('action') ->orderBy('count', 'DESC') ->findAll(); return array_column($rows, 'count', 'action'); } protected function getRoleSummaryForPeriod(string $startDate): array { $rows = $this->select('activity_user_type AS actor_role, COUNT(*) AS count') ->where('activity_at >=', $startDate) ->groupBy('activity_user_type') ->orderBy('count', 'DESC') ->findAll(); return array_column($rows, 'count', 'actor_role'); } protected function getMostActiveUsers(string $startDate, int $limit = 10): array { return $this->select('COALESCE(NULLIF(TRIM(CONCAT(users.first_name, " ", users.last_name)), ""), users.email, "Guest") AS actor, COUNT(activity_logs.id) AS count') ->join('users', 'users.id = activity_logs.activity_user_id', 'left') ->where('activity_logs.activity_at >=', $startDate) ->groupBy('activity_logs.activity_user_id') ->orderBy('count', 'DESC') ->findAll($limit); } protected function getUniqueIPs(string $startDate, int $limit = 10): array { return $this->select('ip, COUNT(*) AS count') ->where('activity_at >=', $startDate) ->groupBy('ip') ->orderBy('count', 'DESC') ->findAll($limit); } public function getCriticalActions(int $limit = 50): array { $criticalActions = ['admin_login', 'admin_password_change', 'system_error', 'security_breach']; return $this->select('activity_logs.*, COALESCE(NULLIF(TRIM(CONCAT(users.first_name, " ", users.last_name)), ""), users.email, "System") AS actor_name, users.email AS actor_email') ->join('users', 'users.id = activity_logs.activity_user_id', 'left') ->whereIn('action', $criticalActions) ->orderBy('activity_at', 'DESC') ->findAll($limit); } public function clearAll(): bool { return $this->truncate(); } }