Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 40
CRAP
0.00% covered (danger)
0.00%
0 / 1032
OnlineInteractiveClassService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 40
56882.00
0.00% covered (danger)
0.00%
0 / 1032
 __construct
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 3
 __clone
n/a
0 / 0
1
n/a
0 / 0
 getInstance
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 5
 createOrUpdateUsers
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 17
 getMeetingDetails
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 15
 isOnlineHourAssignedToTimetable
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 15
 getClusterMeetings
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 18
 getClusterDetails
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 12
 addUser
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 22
 updateIsActive
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 8
 deleteUsers
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 8
 saveMeeting
0.00% covered (danger)
0.00%
0 / 1
72.00
0.00% covered (danger)
0.00%
0 / 19
 createMeeting
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 31
 updateMeeting
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 17
 searchUsers
0.00% covered (danger)
0.00%
0 / 1
182.00
0.00% covered (danger)
0.00%
0 / 43
 searchMeetings
0.00% covered (danger)
0.00%
0 / 1
506.00
0.00% covered (danger)
0.00%
0 / 74
 addStudents
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 41
 notifyStudents
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 25
 deleteMeeting
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 18
 checkAttendanceAlreadyMarked
0.00% covered (danger)
0.00%
0 / 1
42.00
0.00% covered (danger)
0.00%
0 / 17
 getMeetingAttendance
0.00% covered (danger)
0.00%
0 / 1
132.00
0.00% covered (danger)
0.00%
0 / 75
 deleteStudentsByMeetingId
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 11
 joinMeetingByMeetingId
0.00% covered (danger)
0.00%
0 / 1
72.00
0.00% covered (danger)
0.00%
0 / 24
 hostMeetingByMeetingId
0.00% covered (danger)
0.00%
0 / 1
42.00
0.00% covered (danger)
0.00%
0 / 18
 markAttendedMeeting
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 11
 getMeetingAttendedStudents
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 11
 getStudentsAssignedToMeeting
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 14
 getOnlineMeetingReport
0.00% covered (danger)
0.00%
0 / 1
182.00
0.00% covered (danger)
0.00%
0 / 100
 createUserApi
0.00% covered (danger)
0.00%
0 / 1
42.00
0.00% covered (danger)
0.00%
0 / 23
 createMeetingApi
0.00% covered (danger)
0.00%
0 / 1
56.00
0.00% covered (danger)
0.00%
0 / 49
 updateMeetingApi
0.00% covered (danger)
0.00%
0 / 1
42.00
0.00% covered (danger)
0.00%
0 / 37
 deleteMeetingApi
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 13
 getActiveAppById
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 21
 getApps
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 12
 getAppsForStaff
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 23
 getMeetingAppName
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 16
 getJoinUrlApi
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 19
 getHostUrlApi
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 22
 getAllStaffMeetingCount
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 17
 getAllMeetingBasicDetails
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 24
 searchMeetingsOpt
0.00% covered (danger)
0.00%
0 / 1
756.00
0.00% covered (danger)
0.00%
0 / 84
<?php
namespace com\linways\core\ams\professional\service;
use com\linways\base\constant\UserType;
use com\linways\base\util\SecurityUtils;
use com\linways\core\ams\professional\oic\Zoom;
use com\linways\core\ams\professional\oic\MsTeams;
use com\linways\core\ams\professional\oic\Kaltura;
use com\linways\core\ams\professional\dto\oic\User;
use com\linways\core\ams\professional\constant\OicApp;
use com\linways\core\ams\professional\dto\oic\Meeting;
use com\linways\core\ams\professional\service\CommonService;
use com\linways\core\ams\professional\service\StudentService;
use com\linways\core\ams\professional\service\SubjectService;
use com\linways\core\ams\professional\constant\SettingsConstants;
use com\linways\core\ams\professional\dto\notification\Notification;
use com\linways\core\ams\professional\request\oic\SearchUsersRequest;
use com\linways\core\ams\professional\exception\ProfessionalException;
use com\linways\core\ams\professional\request\oic\SearchMeetingsRequest;
use com\linways\core\ams\professional\dto\notification\NotificationRecipient;
use com\linways\core\ams\professional\request\oic\CreateOrUpdateUsersRequest;
use com\linways\core\ams\professional\service\notification\NotificationService;
use com\linways\core\ams\professional\mapper\OnlineInteractiveClassServiceMapper;
use com\linways\core\ams\professional\constant\notification\NotificationContextConstant;
use com\linways\core\ams\professional\constant\notification\NotificationFeatureConstant;
class OnlineInteractiveClassService extends BaseService
{
    // /Condition 1 - Presence of a static member variable
    private static $_instance = null;
    // /Condition 2 - Locked down the constructor
    private function __construct()
    {
        $this->mapper = OnlineInteractiveClassServiceMapper::getInstance()->getMapper();
    }
    // Prevent any oustide instantiation of this class
    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone() {}
    // Prevent any copy of this object
    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (!is_object(self::$_instance)) // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();
        return self::$_instance;
    }
    
    /**
     * Create or Update User accounts
     * @param $request
     * @throws ProfessionalException
     * @return
     */
    public function createOrUpdateUsers(CreateOrUpdateUsersRequest $request)
    {
        $request = $this->realEscapeObject($request);
        try {
            foreach ($request->newUsers as $newUser) {
                if (empty($newUser->id)) {
                    $newUser->id = $this->addUser($newUser);
                } else {
                    $this->updateIsActive($newUser->id, $newUser->isActive);
                }
            }
            if (!empty($request->deletedUsers)) {
                $this->deleteUsers($request->deletedUsers);
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Fetch meetings details
     * @param $meetingId
     * @throws ProfessionalException
     * @return Array $meetingDetails
     */
    public function getMeetingDetails($meetingId)
    {
        try {
            
            if( !$meetingId || empty($meetingId) ){
                throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Meeting id is required");
            }
            
            $query = "SELECT vom.id, vom.name, vom.description, vom.meeting_date, 
                      vom.meetings_details->>'$.join_url' AS join_url, vom.meetings_details
                      FROM v4_oic_meetings vom 
                      WHERE id = '$meetingId";
            $meetingDetails = $this->executeQueryForList($query);
            return $meetingDetails;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Fetch meetings details
     * @param $timetableId
     * @throws ProfessionalException
     * @return Object $result
     */
    public function isOnlineHourAssignedToTimetable($timetableId)
    {
        try {
            if (!$timetableId || empty($timetableId)) {
                throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Timetable id is mandatory");
            }
            // Fetch meeting id, start url and join url
            $query = "SELECT id, JSON_UNQUOTE(JSON_EXTRACT(vom.meetings_details, '$.start_url')) AS start_url, 
                        JSON_UNQUOTE(JSON_EXTRACT(vom.meetings_details, '$.join_url')) AS join_url
                      FROM v4_oic_meetings vom 
                      WHERE v4_timetable_id = '$timetableId'";
            $result = $this->executeQueryForObject($query);
            return $result ? $result : false;
            
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Fetch meetings details
     * @param $clusterId
     * @throws ProfessionalException
     * @return Array $meetings
    */
    public function getClusterMeetings($clusterId)
    {
        try {
            if( !$clusterId || empty($clusterId) ){
                throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Cluster id not found");
            }
            
            $staffId =  $GLOBALS['userId'];
            $query = "SELECT vom.id, vom.name, vom.description, vom.meeting_date
                FROM v4_oic_meetings vom 
                INNER JOIN cluster c ON c.id = vom.cluster_id 
                INNER JOIN cluster_members cm  ON cm.cluster_id = c.id 
                INNER JOIN staffaccounts s ON s.staffID = cm.staff_id 
                WHERE vom.cluster_id = '$clusterId' AND s.staffID = $staffId ORDER BY vom.meeting_date desc";
            $meetings = $this->executeQueryForList($query);
            return $meetings ? $meetings : false;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Fetch cluster details
     * @param $clusterId
     * @throws ProfessionalException
     * @return Object $result
    */
    
    public function getClusterDetails($clusterId)
    {
        try {
            if( !$clusterId || empty($clusterId) ){
                throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Cluster id not found");
            }
            $query = "SELECT c.name FROM cluster c WHERE c.id = '$clusterId";
            $result = $this->executeQueryForObject($query);
            return $result ? $result : false;
            
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Create a new user
     * @param User $user
     * @throws ProfessionalException
     * @return
    */
        
    private function addUser(User $user)
    {
        $user->createdBy = $GLOBALS['userId'];
        try {
            $user->id = SecurityUtils::getRandomString();
            $user->userDetails = $this->createUserApi($user);
            if (!empty($user->userDetails)) { // ZOOM Meetings
                if (!$user->userDetails->status) {
                    throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Cannot create user! Please try again");
                }
                $userDetails = "'" . json_encode($user->userDetails->data) . "'";
            } else {
                // OTHER Meetings
                $userDetails = 'NULL';
            }
            $query = "INSERT INTO oic_users (id,staff_id,user_details,is_active,created_by,created_date,updated_by,updated_date,oic_app_id) 
                      VALUES ('$user->id','$user->staffId',$userDetails,'1','$user->createdBy',UTC_TIMESTAMP(),
                      '$user->createdBy',UTC_TIMESTAMP(),'$user->appId')";
            $this->executeQuery($query);
            return $user->id;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Update users active status
     * @param $id, $isActive
     * @throws ProfessionalException
     * @return
    */
        
    private function updateIsActive($id, $isActive)
    {
        $query = "UPDATE oic_users SET is_active='$isActive' WHERE id='$id'";
        try {
            $this->executeQuery($query);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Delete users
     * @param $userIds
     * @throws ProfessionalException
     * @return
    */
    private function deleteUsers($userIds)
    {
        $query = "UPDATE oic_users SET is_active = 0,updated_by='" . $_SESSION['adminID'] . "',updated_date = UTC_TIMESTAMP() WHERE id IN ('" . implode("','", $userIds) . "')";
        try {
            $this->executeQuery($query);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Create or update an meeting based on meeting id
     * @param Meeting $meeting
     * @throws ProfessionalException
     * @return
    */
    public function saveMeeting(Meeting $meeting)
    {
        $meeting->createdBy = $GLOBALS['userId'];
        $meeting->updatedBy = $GLOBALS['userId'];
        if (empty($meeting->name) || empty($meeting->description) || empty($meeting->meetingDate) || empty($meeting->appId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Please fill required fields");
        }
        if (!(bool) strtotime($meeting->meetingDate)) {
            throw new ProfessionalException(ProfessionalException::INVALID_DATE, "Invalid date given! Please enter a valid date");
        }
        try {
            if (empty($meeting->id)) {
                $this->createMeeting($meeting);
            } else {
                $this->updateMeeting($meeting);
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Create a new meeting
     * @param Meeting $meeting
     * @throws ProfessionalException
     * @return
    */
    
    private function createMeeting(Meeting $meeting)
    {
        try {
            $meeting->meetingDetails = $this->createMeetingApi($meeting); // returns json start and join url
            $meeting->meetingDetails = json_decode(json_encode($meeting->meetingDetails));
            if (!$meeting->meetingDetails->status) {
                throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, $meeting->meetingDetails->message ? $meeting->meetingDetails->message : "Cannot create meeting! Please try again");
            }
            $meeting->meetingDetails = $meeting->meetingDetails->data; // data -> start and join url
            $meeting->id = SecurityUtils::getRandomString();
            $query =
               "INSERT INTO v4_oic_meetings (id, v4_timetable_id, cluster_id, name, description, meetings_details, meeting_date, oic_users_id, created_by, created_date, updated_by, updated_date) 
                VALUES (
                    '$meeting->id',
                    '$meeting->timetableId',
                    '$meeting->clusterId',
                    '$meeting->name',
                    '$meeting->description',
                    '" . str_replace("'", "\'", json_encode($meeting->meetingDetails)) . "',
                    '" . date("Y-m-d H:i:s", strtotime($meeting->meetingDate)) . "',
                    '$meeting->userId',
                    '$meeting->createdBy',
                    UTC_TIMESTAMP(),
                    '$meeting->updatedBy',
                    UTC_TIMESTAMP()
                )";
            $this->executeQuery($query);
            $this->addStudents($meeting);
            
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Update meeting
     * @param Meeting $meeting
     * @throws ProfessionalException
     * @return
    */
    private function updateMeeting(Meeting $meeting)
    {
        try {
            $response = $this->updateMeetingApi($meeting);
            $response = json_decode(json_encode($response));
            if (!$response->status) {
                throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, $response->message ? $response->message : "Cannot update meeting! Please try again");
            }
            
            $meetingDetails = $response->data ? "'" . str_replace("'", "\'", json_encode($response->data)) . "'" : 'meetings_details';
            $query = "UPDATE v4_oic_meetings SET name='$meeting->name',description='$meeting->description',oic_users_id='$meeting->userId',
                      meeting_date='" . date("Y-m-d H:i:s", strtotime($meeting->meetingDate)) . "',updated_by='$meeting->updatedBy',
                      updated_date=UTC_TIMESTAMP(),meetings_details=$meetingDetails 
                      WHERE id='$meeting->id'";
                      
            $this->executeQuery($query);
            
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Search user
     * @param SearchUsersRequest $request
     * @throws ProfessionalException
     * @return Array $userDetails
    */
    public function searchUsers(SearchUsersRequest $request)
    {
        $request = $this->realEscapeObject($request);
        $query = "SELECT oics.id, oics.user_details, oics.is_active, sta.staffName, sta.staffID, sta.staffEmail, 
                    sta.staffAccount, oics.oic_app_id, oa.name 
                  FROM staffaccounts sta 
                  LEFT JOIN oic_users oics ON sta.staffID=oics.staff_id 
                  LEFT JOIN oic_app oa ON oics.oic_app_id=oa.id 
                  WHERE 1=1 AND sta.isResigned=0 ";
                  
        if (!empty($request->id)) {
            $query .= " AND oics.id='" . $request->id . "'";
        }
        
        if (!empty($request->staffId)) {
            $query .= " AND oics.staff_id='$request->staffId'";
        }
        
        if (!empty($request->departmentId)) {
            $query .= " AND sta.deptID='$request->departmentId'";
        }
        
        if (!empty($request->staffName)) {
            $query .= " AND sta.staffName LIKE '%$request->staffName%'";
        }
        
        if (!empty($request->staffAccount)) {
            $query .= " AND sta.staffAccount='$request->staffAccount'";
        }
        
        if ($request->isActive === "1" || $request->isActive === "0") {
            $query .= " AND oics.is_active = '$request->isActive'";
        }
        
        if ($request->appId) {
            $query .= " AND oics.oic_app_id='$request->appId'";
        }
        try {
            $userDetails = $this->executeQueryForList($query, $this->mapper[OnlineInteractiveClassServiceMapper::GET_USER_DETAILS]);
            foreach ($userDetails as $u) {
                foreach ($u->apiDetails as $app) {
                    if (is_array($app->userDetails)) {
                        $app->userDetails = json_decode(json_encode($app->userDetails));
                    }
                }
            }
            return $userDetails;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Search user
     * @param SearchUsersRequest $request
     * @throws ProfessionalException
     * @return Array $response
    */
    public function searchMeetings(SearchMeetingsRequest $request)
    {
        $request = $this->realEscapeObject($request);
        try {
            if ($request->studentId) {
                $durations = json_decode(CommonService::getInstance()->getSettings(SettingsConstants::ONLINE_CLASS, SettingsConstants::ONLINE_CLASS_LIST_STUDENT_DURATIONS));
            } else {
                $durations = json_decode(CommonService::getInstance()->getSettings(SettingsConstants::ONLINE_CLASS, SettingsConstants::ONLINE_CLASS_LIST_DURATIONS));
            }
        } catch (\Throwable $th) {
            $durations->upComingMeetingShowDurationInMinutes = 5;
            $durations->onGoingMeetingShowDurationInMinutes = 40;
        }
        $query = "SELECT om.id, om.name, om.description, om.meetings_details, om.meeting_date, om.oic_users_id, ou.staff_id, sta.staffName, 
                    IF(now() BETWEEN om.meeting_date+INTERVAL -" . $durations->upComingMeetingShowDurationInMinutes . 
                    " MINUTE AND om.meeting_date +INTERVAL +" . $durations->onGoingMeetingShowDurationInMinutes . 
                    " MINUTE,1,0) AS liveNow,IF(now() < om.meeting_date+INTERVAL -" . $durations->upComingMeetingShowDurationInMinutes . 
                    " MINUTE,1,0) AS upComing,IF(now() > om.meeting_date+INTERVAL +" . $durations->onGoingMeetingShowDurationInMinutes . 
                    " MINUTE,1,0) AS expiredMeeting,ou.oic_app_id  
                    FROM v4_oic_meetings om 
                    INNER JOIN oic_users ou ON om.oic_users_id = ou.id 
                    INNER JOIN staffaccounts sta ON sta.staffID=ou.staff_id 
                    WHERE 1=1 ";
        $orderBy = " ORDER BY om.meeting_date ASC";
        
        if (!empty($request->id)) {
            $query .= " AND om.id='$request->id'";
        }
        
        if (!empty($request->userId)) {
            $query .= " AND om.user_id='$request->userId'";
        }
        
        if (!empty($request->staffId)) {
            $query .= " AND ou.staff_id='$request->staffId'";
        }
        
        if (!empty($request->batchId)) {
            $query .= " AND om.id IN (SELECT DISTINCT oic_meetings_id FROM v4_oic_students ost INNER JOIN studentaccount sta ON sta.studentID=ost.student_id WHERE sta.batchID='$request->batchId')";
        }
        
        if (!empty($request->studentId)) {
            $query .= " AND om.id IN (SELECT DISTINCT oic_meetings_id FROM v4_oic_students WHERE student_id='$request->studentId')";
        }
        
        if (!empty($request->fromDate) && !empty($request->toDate)) {
            $query .= " AND DATE_FORMAT(CONVERT_TZ(om.meeting_date,
            '+00:00',
            @@global .time_zone),
            '%Y-%m-%d') BETWEEN '" . date('Y-m-d', strtotime($request->fromDate)) . "' AND '" . date('Y-m-d', strtotime($request->toDate)) . "'";
        } else if (!empty($request->fromDate)) {
            $query .= " AND DATE_FORMAT(CONVERT_TZ(sfrd.remitted_date,
            '+00:00',
            @@global .time_zone),
            '%Y-%m-%d') = '" . date('Y-m-d', strtotime($request->fromDate)) . "'";
        }
        
        if (!empty($request->pssubId)) {
            $query .= " AND om.id IN (SELECT DISTINCT oic_meetings_id FROM v4_oic_students ost INNER JOIN pseudosubjects_students psbs ON psbs.studentID=ost.student_id WHERE psbs.pseudosubjectID='$request->pssubId')";
        }
        if ($request->onGoingMeetingsOnly) {
            $query .= " AND now() BETWEEN om.meeting_date+INTERVAL -" . $durations->upComingMeetingShowDurationInMinutes . " MINUTE AND om.meeting_date +INTERVAL +" . $durations->onGoingMeetingShowDurationInMinutes . " MINUTE";
        }
        try {
            $response = $this->executeQueryForList($query . $orderBy, $this->mapper[OnlineInteractiveClassServiceMapper::GET_MEETING_DETAILS]);
            foreach ($response as $meeting) {
                if (is_array($meeting->meetingDetails)) {
                    $meeting->meetingDetails = json_decode(json_encode($meeting->meetingDetails));
                }
                if ($meeting->meetingDate) {
                    $meetingDate = strtotime($meeting->meetingDate);
                    $meeting->upComing = strtotime(date("Y-m-d H:i:s")) < strtotime("-" . $durations->upComingMeetingShowDurationInMinutes . " minutes", $meetingDate) ? "1" : "0";
                    $meeting->liveNow = strtotime("-" . $durations->upComingMeetingShowDurationInMinutes . " minutes", $meetingDate) < strtotime(date("Y-m-d H:i:s")) && strtotime(date("Y-m-d H:i:s")) < strtotime("+" . $durations->onGoingMeetingShowDurationInMinutes . " minutes", $meetingDate) ? "1" : "0";
                    $meeting->joinTimeLimit = $meeting->liveNow ? $durations->onGoingMeetingShowDurationInMinutes : 0;
                    $meeting->expiredMeeting = strtotime("+" . $durations->onGoingMeetingShowDurationInMinutes . " minutes", $meetingDate) < strtotime(date("Y-m-d H:i:s")) ? "1" : "0";
                }
            }
            return $response;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Add students for a meeting
     * @param $meeting
     * @throws ProfessionalException
     * @return
    */
    private function addStudents($meeting)
    {
        $createdBy = $GLOBALS['userId'];
        $updatedBy = $GLOBALS['userId'];
        $queryValues = [];
        try {
            $query = "INSERT INTO v4_oic_students (oic_meetings_id,student_id,attended,created_by,created_date,updated_by,updated_date) VALUES ";
            $queryForClusterStudentList = $meeting->timetableId
                ?
                "SELECT DISTINCT(s.studentID )
                FROM v4_oic_meetings vom 
                INNER JOIN v4_timetable vt ON vt.id = vom.v4_timetable_id 
                INNER JOIN cluster c ON c.id = vt.cluster_id AND c.trashed IS NULL
                INNER JOIN cluster_groups_relations cgr ON cgr.cluster_id = c.id 
                INNER JOIN `groups` g ON g.id = cgr.groups_id 
                INNER JOIN group_members gm ON gm.groups_id = g.id AND gm.academic_status = 'ACTIVE'
                INNER JOIN student_program_account spa ON spa.id = gm.student_id 
                INNER JOIN studentaccount s ON s.studentID = spa.student_id 
                WHERE vom.v4_timetable_id = '$meeting->timetableId
                      AND vom.id = '$meeting->id
                      AND vom.cluster_id = '$meeting->clusterId"
                :
                "SELECT DISTINCT(s.studentID )
                FROM v4_oic_meetings vom 
                INNER JOIN cluster c ON c.id = vom.cluster_id AND c.trashed IS NULL
                INNER JOIN cluster_groups_relations cgr ON cgr.cluster_id = c.id 
                INNER JOIN `groups` g ON g.id = cgr.groups_id 
                INNER JOIN group_members gm ON gm.groups_id = g.id AND gm.academic_status = 'ACTIVE'
                INNER JOIN student_program_account spa ON spa.id = gm.student_id 
                INNER JOIN studentaccount s ON s.studentID = spa.student_id 
                WHERE vom.id = '$meeting->id' AND vom.cluster_id = '$meeting->clusterId";
            $clusterStudentList = $this->executeQueryForList($queryForClusterStudentList);
            foreach ($clusterStudentList as $student) {
                $queryValues[] = "('$meeting->id','$student->studentID','0','$createdBy',UTC_TIMESTAMP(),'$updatedBy',UTC_TIMESTAMP())";
            }
            if (!empty($queryValues)) {
                $this->executeQuery($query . (implode(",", $queryValues)));
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Notify students regarding the meeting
     * @param $meeting
     * @throws ProfessionalException
     * @return
    */
    private function notifyStudents($meeting)
    {
        $meetingDate = date('d-m-Y H:i:a', strtotime($meeting->meetingDate));
        if (!empty($meeting->pssubId)) {
            $subjectName = SubjectService::getInstance()->getPseudoSubjectDetailsById($meeting->pssubId)->name;
            $studentList = StudentService::getInstance()->getStudentsAssignedToApseudoSub($meeting->pssubId);
        } else {
            $subjectName = SubjectService::getInstance()->getBasicSubjectDetailsBySubjectId($meeting->subjectId)->subjectName;
            $studentList = StudentService::getInstance()->getStudentsByBatchIdSemIdSubjectIdStaffId($meeting->batchId, $meeting->subjectId, $meeting->semesterId, $_SESSION['staffID']);
        }
        if (!empty($studentList)) {
            $notification = new Notification();
            $notification->context = NotificationContextConstant::OIC_MEETING;
            $notification->feature = NotificationFeatureConstant::NOTIFY_MEETING_CREATED;
            $notification->recipientType = UserType::STUDENT;
            $notification->createdBy = $_SESSION['staffID'];
            foreach ($studentList as $student) {
                $recipient = new NotificationRecipient();
                $recipient->recipientId = $student->studentID;
                $recipient->recipientType = UserType::STUDENT;
                $recipient->templateParameters = ["date" => $meetingDate, "subjectName" => $subjectName];
                $notification->recipient[] = $recipient;
            }
        }
        NotificationService::getInstance()->sendNotification($notification);
    }
    /**
     * Delete meeting
     * @param $meetingId
     * @throws ProfessionalException
     * @return
    */
    public function deleteMeeting($meetingId)
    {
        $meetingId = $this->realEscapeString($meetingId);
        try {
            $meetingRequest = new SearchMeetingsRequest();
            $meetingRequest->id = $meetingId;
            $meetingRequest->staffId = $GLOBALS['userId'];
            $meeting = $this->searchMeetings($meetingRequest)[0];
            if (empty($meeting)) {
                throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Permission denied");
            }
            $this->checkAttendanceAlreadyMarked($meetingId);
            // $response = $this->deleteMeetingApi($meeting->meetingDetails->id);
            $this->deleteStudentsByMeetingId($meetingId);
            $query = "DELETE FROM v4_oic_meetings WHERE id='$meetingId'";
            $this->executeQuery($query);
            // $query_timetable = "UPDATE batch_timetable SET oicMeetingId=null WHERE oicMeetingId='$meetingId'";
            // $this->executeQuery($query_timetable);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Check if at least one attendance has been marked against a meeting
     * @param $meetingId
     * @throws ProfessionalException
     * @return
    */
    private function checkAttendanceAlreadyMarked($meetingId)
    {
        if( !$meetingId || empty($meetingId) ){
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Meeting id is required");
        }
        
        // CHECK ATTENDANCE ONLY FOR MEETINGS CREATED AGAINST TIMETABLE
        $query = "SELECT vos.student_id, COUNT(*) as count 
                  FROM v4_oic_meetings vom
                  INNER JOIN v4_oic_students vos ON vos.oic_meetings_id = vom.id 
                  WHERE vos.attended = '1' AND vos.oic_meetings_id = '$meetingId' AND vom.v4_timetable_id != '0' ";
        try {
            $response = $this->executeQueryForList($query);
            if (!($response) || (int)($response[0]->count) > 0) {
                throw new ProfessionalException(ProfessionalException::ATTENDANCE_ALREADY_MARKED, "Unable to delete the meeting, attendance has already been marked");
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Get drafted/marked attendance entry for a meeting
     * @param $meetingId
     * @throws ProfessionalException
     * @return Array
    */
    public function getMeetingAttendance($meetingId)
    {
        if( !$meetingId || empty($meetingId) ){
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Meeting id is required");
        }
        
        try {
            
            // CHECK IF MEETING IS CREATED AGIANST A TIMETABLE, IF NOT THROW EXCEPTION
            $queryForClusterMeeting = 
            "SELECT *
            FROM v4_oic_meetings vom 
            WHERE vom.id = '$meetingId'";
    
            $clusterMeeting = $this->executeQueryForList($queryForClusterMeeting);
            
            if($clusterMeeting[0]->v4_timetable_id == 0){
                throw new ProfessionalException(ProfessionalException::ATTENDANCE_NOT_RECORDED, "Attendance record does not exist as this meeting is not created against a timetable");
            }
            // CHECK ATTENDANCE ENTRY IN DRAFTED
            // GET STUDENT DETAILS IF ATTENDANCE HAS BEEN DRAFTED 
            $queryForAttendanceDrafted = 
            "SELECT s.studentID , s.studentName , vad.is_absent, 'DRAFTED' as status
            FROM v4_oic_meetings vom 
            INNER JOIN v4_attendance_draft vad ON 
            vad.time_table_id = vom.v4_timetable_id
            INNER JOIN v4_attendance_drafted_user vadu
            ON vadu.staff_id = vad.staff_id AND vadu.time_table_id = vad.time_table_id  
            INNER JOIN staffaccounts staff ON
            staff.staffID = vadu.staff_id  
            INNER JOIN student_program_account spa ON
            spa.id = vad.student_program_id 
            INNER JOIN studentaccount s ON
            s.studentID = spa.student_id 
            WHERE vom.id = '$meetingId";
            
            $attendanceDraftedStudents = $this->executeQueryForList($queryForAttendanceDrafted);
                
            if (!empty($attendanceDraftedStudents) && count($attendanceDraftedStudents) > 0) {
                return $attendanceDraftedStudents;
            }
                
            // CHECK ATTENDANCE ENTRY IN MARKED
            // GET STUDENT DETAILS IF ATTENDANCE HAS BEEN CONFIRMED
            $queryForAttendanceMarked = 
            "SELECT s.studentID , s.studentName , va.is_absent, 'CONFIRMED' as status
            FROM v4_oic_meetings vom 
            INNER JOIN v4_attendance va ON 
            va.time_table_id = vom.v4_timetable_id
            INNER JOIN v4_attendance_marked_user vamu 
            ON vamu.staff_id = va.staff_id AND vamu.time_table_id = va.time_table_id  
            INNER JOIN staffaccounts staff ON
            staff.staffID = vamu.staff_id 
            INNER JOIN student_program_account spa ON
            spa.id = va.student_program_id 
            INNER JOIN studentaccount s ON
            s.studentID = spa.student_id 
            WHERE vom.id = '$meetingId";
            
            $attendanceMarkedStudents = $this->executeQueryForList($queryForAttendanceMarked);
                
            if (!empty($attendanceMarkedStudents) && count($attendanceMarkedStudents) > 0) {
                return $attendanceMarkedStudents;
            }
            
            // IF ATTENDANCE NEITHER DRAFTED NOR CONFIRMED, FETCH ATTENDED COLUMN FROM v4_oic_students
            $queryForMeetingAttended = 
            "SELECT
                s.studentID,
                s.studentName,
                CASE
                    WHEN vos.attended = 0 THEN 1
                    WHEN vos.attended = 1 THEN 0
                END AS is_absent,
                'Not Recorded. Below data is based on students who joined the meeting.' as status
            FROM
                v4_oic_students vos
            INNER JOIN studentaccount s ON
                s.studentID = vos.student_id
            INNER JOIN student_program_account spa ON
                spa.student_id = s.studentID
            where
                vos.oic_meetings_id = '$meetingId";
            
            $meetingAttended = $this->executeQueryForList($queryForMeetingAttended);
            
            if (!empty($meetingAttended) && count($meetingAttended) > 0) {
                return $meetingAttended;
            }
            
            throw new ProfessionalException(ProfessionalException::ATTENDANCE_NOT_RECORDED, "Attendance for this hour has not been recorded.");
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Delete students associated with a meeting
     * @param $meetingId
     * @throws ProfessionalException
     * @return
    */
    private function deleteStudentsByMeetingId($meetingId)
    {
        if( !$meetingId || empty($meetingId) ){
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Meeting id is required");
        }
        
        $query = "DELETE FROM v4_oic_students WHERE oic_meetings_id='$meetingId'";
        try {
            $this->executeQuery($query);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Join by meeting id
     * @param $meetingId
     * @throws ProfessionalException
     * @return
    */
    public function joinMeetingByMeetingId($meetingId)
    {
        $studentId = $GLOBALS['userId'];
        if (empty($meetingId)) {
            if (empty($studentId)) {
                throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Invalid request! Please send a valid request");
            }
        }
        try {
            $searchMeetingsRequest = new SearchMeetingsRequest();
            $searchMeetingsRequest->id = $meetingId;
            $searchMeetingsRequest->studentId = $studentId;
            // $searchMeetingsRequest->onGoingMeetingsOnly = "1";
            $meeting = $this->searchMeetingsOpt($searchMeetingsRequest);
            $meeting = $meeting[0];
            if (empty($meeting) || $meeting->upComing === "1") {
                throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Permission Denied");
            }
            if ($meeting->liveNow === "1" && !empty($studentId)) {
                $this->markAttendedMeeting($studentId, $meetingId);
            }
            return $this->getJoinUrlApi($meeting);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Host meeting
     * @param $meetingId
     * @throws ProfessionalException
     * @return
    */
    public function hostMeetingByMeetingId($meetingId)
    {
        if (empty($meetingId) || empty($_SESSION['staffID'])) {
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Invalid request! Please send a valid request");
        }
        try {
            $searchMeetingsRequest = new SearchMeetingsRequest();
            $searchMeetingsRequest->id = $meetingId;
            $searchMeetingsRequest->staffId = $_SESSION['staffID'];
            // $searchMeetingsRequest->onGoingMeetingsOnly = "1";
            $meeting = $this->searchMeetings($searchMeetingsRequest);
            $meeting = $meeting[0];
            if (empty($meeting) || $meeting->upComing === "1") {
                throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Permission Denied");
            }
            return $this->getHostUrlApi($meeting);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Mark meeting attendance
     * @param $studentId, $meetingId
     * @throws ProfessionalException
     * @return
    */
    
    public function markAttendedMeeting($studentId, $meetingId)
    {
        if (empty($studentId) || empty($meetingId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Invalid request! Please send a valid request");
        }
        
        $query = "UPDATE v4_oic_students SET attended = 1 WHERE student_id='$studentId' AND oic_meetings_id='$meetingId'";
        try {
            $this->executeQuery($query);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Fetch meeting attended students
     * @param $studentId, $meetingId
     * @throws ProfessionalException
     * @return
    */
    
    public function getMeetingAttendedStudents($meetingId)
    {
        if( !$meetingId || empty($meetingId) ){
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Meeting id is required");
        }
        
        $query = "SELECT * FROM v4_oic_students WHERE oic_meetings_id='$meetingId' AND attended=1";
        try {
            return $this->executeQueryForList($query);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Fetch students in a meeting
     * @param $meetingId
     * @throws ProfessionalException
     * @return
    */
    
    public function getStudentsAssignedToMeeting($meetingId)
    {
        if (empty($meetingId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Meeting id is required");
        }
        
        $query = "SELECT sa.studentID,sa.rollNo,sa.studentName,os.attended
                  FROM v4_oic_students os 
                  INNER JOIN studentaccount sa ON sa.studentID= os.student_id 
                  WHERE os.oic_meetings_id='$meetingId' ORDER BY sa.studentName";
        try {
            return $this->executeQueryForList($query);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Fetch meeting report
     * @param $request
     * @throws ProfessionalException
     * @return
    */
    
    public function getOnlineMeetingReport($request)
    {
        $request = $this->realEscapeObject($request);
        try {
            $durations = json_decode(CommonService::getInstance()->getSettings(SettingsConstants::ONLINE_CLASS, SettingsConstants::ONLINE_CLASS_LIST_DURATIONS));
        } catch (\Throwable $th) {
            $durations->upComingMeetingShowDurationInMinutes = 5;
            $durations->onGoingMeetingShowDurationInMinutes = 40;
        }
        $whereCond = '';
        $limit = '';
        $dates = [];
        if ($request->schoolIds) {
            $whereCond .= " AND d.school_id IN ('" . implode("','", $request->schoolIds) . "')";
        }
        if ($request->programIds) {
            $whereCond .= " AND p.id IN ('" . implode("','", $request->programIds) . "')";
        }
        if ($request->batchId) {
            $whereCond .= " AND gBatch.id = '$request->batchId'";
        } else if ($request->batchIds) {
            $whereCond .= " AND gBatch.id IN ('" . implode("','", $request->batchIds) . "')";
        }
        if ($request->termId) {
            $whereCond .= " AND  at.id = '$request->termId'";
        } else if ($request->termIds) {
            $whereCond .= " AND  at.id IN ('" . implode("','", $request->termIds) . "')";
        }
        if ($request->dateRange) {
            $dates = explode(' to ', $request->dateRange);
            if (empty($dates[1])) {
                $dates[1] = $dates[0];
            }
            $whereCond .= " AND DATE_FORMAT(CONVERT_TZ(vom.meeting_date, '+00:00', @@global.time_zone), 
                            '%Y-%m-%d') BETWEEN '" . date('Y-m-d', strtotime($dates[0])) . "' 
                            AND '" . date('Y-m-d', strtotime($dates[1])) . "'";
        }        
        
        // if(!empty($request->orderBy)){
        //     $orderBy = $request->orderBy;
        // }
        // $query = "SELECT distinct om.id,om.name,om.description,om.meetings_details,om.meeting_date,om.oic_users_id,ou.staff_id,sta.staffName,sub.subjectID,sub.subjectName,b.batchName,b.batchID,sbs.semID,sbs.sbsID,sbs.isPseudosubject,sum(attended) as attended, IF(now() BETWEEN om.meeting_date+INTERVAL -".$durations->upComingMeetingShowDurationInMinutes." MINUTE AND om.meeting_date +INTERVAL +".$durations->onGoingMeetingShowDurationInMinutes." MINUTE,1,0) AS liveNow,IF(now() < om.meeting_date+INTERVAL -".$durations->upComingMeetingShowDurationInMinutes." MINUTE,1,0) AS upComing,IF(now() > om.meeting_date+INTERVAL +".$durations->onGoingMeetingShowDurationInMinutes." MINUTE,1,0) AS expiredMeeting,ou.oic_app_id FROM v4_oic_meetings om INNER JOIN oic_users ou ON om.oic_users_id = ou.id INNER JOIN staffaccounts sta ON sta.staffID=ou.staff_id INNER JOIN v4_oic_students ost on ost.oic_meetings_id = om.id  INNER JOIN studentaccount sa ON sa.studentID=ost.student_id INNER JOIN batches b on b.batchID = sa.batchID inner join sbs_relation sbs on sbs.batchID= sa.batchID and sbs.staffID= sta.staffID and sbs.semID=b.semID inner join subjects sub on sbs.subjectID = sub.subjectID  WHERE 1=1 ";
        $query = "SELECT
                    DISTINCT vom.id,
                    vom.name,
                    vom.description,
                    vom.meetings_details,
                    GROUP_CONCAT(DISTINCT gBatch.name ORDER BY gBatch.name SEPARATOR ', ') as batchName,
                    vom.meeting_date,
                    vom.oic_users_id,
                    ou.staff_id,
                    sta.staffName,
                    sum(attended) as attended,
                    IF(now() BETWEEN vom.meeting_date + INTERVAL -" . $durations->upComingMeetingShowDurationInMinutes . " MINUTE AND vom.meeting_date + INTERVAL + " . $durations->onGoingMeetingShowDurationInMinutes . " MINUTE,
                    1,
                    0) AS liveNow ,
                    IF(now() < vom.meeting_date + INTERVAL -" . $durations->upComingMeetingShowDurationInMinutes . " MINUTE,
                    1,
                    0) AS upComing ,
                    IF(now() > vom.meeting_date + INTERVAL + " . $durations->onGoingMeetingShowDurationInMinutes . " MINUTE,
                    1,
                    0) AS expiredMeeting,
                    ou.oic_app_id
                FROM
                    v4_oic_meetings vom
                INNER JOIN oic_users ou ON
                    vom.oic_users_id = ou.id
                INNER JOIN staffaccounts sta ON 
                    sta.staffID = ou.staff_id
                INNER JOIN v4_oic_students ost ON
                    ost.oic_meetings_id = vom.id
                INNER JOIN studentaccount sa ON
                    sa.studentID = ost.student_id
                INNER JOIN cluster c ON
                    c.id = vom.cluster_id 
                INNER JOIN cluster_groups_relations cgr ON 
                    cgr.cluster_id = c.id
                INNER JOIN `groups` gSub ON -- FETCH STUDENT SUBJECTS
                    gSub.id = cgr.groups_id
                INNER JOIN group_members gm ON -- FETCH STUDENTS IN SUBJECTS
                    gm.groups_id = gSub.id AND gm.academic_status = 'ACTIVE'
                INNER JOIN academic_term at ON -- FETCH STUDENT SEMESTER
                    at.id = gSub.academic_term_id 
                INNER JOIN groups_relations gr ON
                    gr.child_groups_id = gSub.id
                INNER JOIN `groups` gBatch ON -- FETCH STUDENT BATCH
                    gBatch.id = gr.parent_groups_id 
                INNER JOIN program p ON -- FETCH BATCH PROGRAM
                    p.id = gBatch.program_id
                INNER JOIN program_department_relation pdr ON -- FETCH BATCH SCHOOL
                    pdr.program_id = gBatch.program_id
                INNER JOIN department d ON
                    d.deptID = pdr.department_id
                WHERE 1=1 $whereCond GROUP BY vom.id ORDER BY vom.meeting_date DESC";
        // $orderBy = " ORDER BY om.meeting_date $orderBy";
        // if(!empty($request->staffId))
        // {
        //     $query .= " AND ou.staff_id='$request->staffId'";
        // }
        // if($request->onGoingMeetingsOnly)
        // {
        //     $query .= " AND now() BETWEEN om.meeting_date+INTERVAL -".$durations->upComingMeetingShowDurationInMinutes." MINUTE AND om.meeting_date +INTERVAL +".$durations->onGoingMeetingShowDurationInMinutes." MINUTE";
        // }
        try {
            $response = $this->executeQueryForList($query, $this->mapper[OnlineInteractiveClassServiceMapper::GET_MEETING_DETAILS]);
            foreach ($response as $meeting) {
                if (is_array($meeting->meetingDetails)) {
                    $meeting->meetingDetails = json_decode(json_encode($meeting->meetingDetails));
                }
                // if($meeting->meetingDate){
                //     $meetingDate = strtotime($meeting->meetingDate);
                //     $meeting->upComing = strtotime(date("Y-m-d H:i:s")) < strtotime("-".$durations->upComingMeetingShowDurationInMinutes." minutes",$meetingDate)?"1":"0";
                //     $meeting->liveNow = strtotime("-".$durations->upComingMeetingShowDurationInMinutes." minutes",$meetingDate) < strtotime(date("Y-m-d H:i:s")) && strtotime(date("Y-m-d H:i:s")) < strtotime("+".$durations->onGoingMeetingShowDurationInMinutes." minutes",$meetingDate)?"1":"0";
                //     $meeting->expiredMeeting = strtotime("+".$durations->onGoingMeetingShowDurationInMinutes." minutes",$meetingDate) < strtotime(date("Y-m-d H:i:s"))?"1":"0";
                // }
            }
            return $response;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**************************************api services ************************************/
    
    /**
     * Create user via API
     * @param User $user
     * @throws ProfessionalException
     * @return
    */
    
    private function createUserApi(User $user)
    {
        try {
            $app = $this->getActiveAppById($user->appId);
            switch ($app->name) {
                case OicApp::ZOOM:
                    $createZoomUser = new Zoom($app->API_KEY, $app->API_SECRET_KEY, $app->ACCOUNT_ID);
                                                            
                    return $createZoomUser->createUser($user);
                    break;
                case OicApp::OTHER:
                    return null;
                    break;
                case OicApp::MS_TEAMS:
                    $createMsTeamsUser = new MsTeams($app->CLIENT_ID, $app->CLIENT_SECRET_KEY, $app->TENANT_ID, $app->DOMAIN);
                    return $createMsTeamsUser->createUser($user);
                    break;
                case OicApp::KALTURA:
                    return null;
                    break;
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Create meeting via API
     * @param Meeting $meeting
     * @throws ProfessionalException
     * @return
    */
    
    private function createMeetingApi(Meeting $meeting)
    {
        try {
            $app = $this->getActiveAppById($meeting->appId);
            switch ($app->name) {
                case OicApp::ZOOM:
                    $getUserRequest = new SearchUsersRequest();
                    $getUserRequest->id = $meeting->userId;
                    $getUserRequest->appId = $meeting->appId;
                    $user = $this->searchUsers($getUserRequest)[0];
                    $meeting->apiUserId = $user->apiDetails[0]->userDetails->id;
                    $createZoomMeeting = new Zoom($app->API_KEY, $app->API_SECRET_KEY, $app->ACCOUNT_ID);
                    $response = $createZoomMeeting->createMeeting($meeting);
                    $meetingDetails = new \Stdclass();
                    $meetingDetails->data = $response;
                    $meetingDetails->status = $response['status'];
                    return $meetingDetails;
                    break;
                case OicApp::OTHER:
                    if (empty($meeting->joinUrl)) {
                        throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "URL is mandatory! Please provide a valid URL");
                    }
                    $meetingDetails = (object) [
                        "data" => (object) ["start_url" => "https://" . str_replace(["http://", "https://"], "", $meeting->joinUrl), "join_url" => "https://" . str_replace(["http://", "https://"], "", $meeting->joinUrl)],
                        "status" => true
                    ];
                    return $meetingDetails;
                    break;
                case OicApp::MS_TEAMS:
                    $getUserRequest = new SearchUsersRequest();
                    $getUserRequest->id = $meeting->userId;
                    $getUserRequest->appId = $meeting->appId;
                    $user = $this->searchUsers($getUserRequest)[0];
                    $meeting->apiUserId = $user->apiDetails[0]->userDetails->id;
                    $createMeeting = new MsTeams($app->CLIENT_ID, $app->CLIENT_SECRET_KEY, $app->TENANT_ID);
                    return $createMeeting->createMeeting($meeting);
                    break;
                case OicApp::KALTURA:
                    $getUserRequest = new SearchUsersRequest();
                    $getUserRequest->id = $meeting->userId;
                    $getUserRequest->appId = $meeting->appId;
                    $user = $this->searchUsers($getUserRequest)[0];
                    // $meeting->apiUserId = $user->apiDetails[0]->userDetails->id;
                    $createMeeting = new Kaltura();
                    return $createMeeting->createMeeting($meeting);
                    break;
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Update meeting via API
     * @param Meeting $meeting
     * @throws ProfessionalException
     * @return
    */
    
    private function updateMeetingApi(Meeting $meeting)
    {
        try {
            $app = $this->getActiveAppById($meeting->appId); // Zoom API credentials
            switch ($app->name) {
                case OicApp::ZOOM:
                    $getUserRequest = new SearchUsersRequest();
                    $getUserRequest->id = $meeting->userId;
                    $getUserRequest->appId = $meeting->appId;
                    $user = $this->searchUsers($getUserRequest)[0]; // staff name, etc -> api details -> userDetails
                    // $meeting->apiUserId = $user->userDetails->id;
                    // $meeting->apiUserId = $user->apiDetails[0]->userDetails->id;
                    $meeting->apiUserId = $user->userDetails->id;
                    $updateZoomMeeting = new Zoom($app->API_KEY, $app->API_SECRET_KEY, $app->ACCOUNT_ID);
                    $response = $updateZoomMeeting->updateMeeting($meeting);
                    $response->data = "";
                    return $response;
                    break;
                case OicApp::OTHER:
                    if (empty($meeting->joinUrl)) {
                        throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "URL is mandatory! Please provide a valid URL");
                    }
                    $meetingDetails = (object) [
                        "data" => (object) ["start_url" => "https://" . str_replace(["http://", "https://"], "", $meeting->joinUrl), "join_url" => "https://" . str_replace(["http://", "https://"], "", $meeting->joinUrl)],
                        "status" => true
                    ];
                    return $meetingDetails;
                    break;
                case OicApp::MS_TEAMS:
                    return
                        (object) [
                            "status" => true,
                            "data" => ""
                        ];
                    break;
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Delete meeting via API
     * @param $meetingId
     * @throws ProfessionalException
     * @return
    */
    
    private function deleteMeetingApi($meetingId)
    {
        try {
            $app = $this->getActiveApp();
            switch ($app->name) {
                case OicApp::ZOOM:
                    $deleteZoomMeeting = new Zoom($app->API_KEY, $app->API_SECRET_KEY, $app->ACCOUNT_ID);
                    return $deleteZoomMeeting->deleteMeeting($meetingId);
                    break;
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Fetch active meeting app by app id
     * @param $id
     * @throws ProfessionalException
     * @return Object $app
    */
    
    private function getActiveAppById($id) // Get api credentials of zoom
    {
        $id = $this->realEscapeString($id);
        if (empty($id)) {
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Invalid Request! Please try again");
        }
        $query = "SELECT oa.id,oa.name,oa.is_active,oaa.id as attr_id,oaa.name as attr_name,oaa.value as attr_value 
                  FROM oic_app oa 
                  LEFT JOIN oic_app_attr oaa ON oa.id=oaa.oic_app_id 
                  WHERE is_active=1 ";
        $query .= " AND oa.id='$id'";
        try {
            $app = $this->executeQueryForObject($query, false, $this->mapper[OnlineInteractiveClassServiceMapper::GET_APP_DETAILS]);
            foreach ($app->attr as $appAttr) {
                $app->{$appAttr->name} = $appAttr->value;
            }
            unset($app->attr);
            return $app;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Fetch active meeting app by app id
     * @param $isActive
     * @throws ProfessionalException
     * @return Array $app
    */
    
    public function getApps($isActive = true)
    {
        $isActive = $isActive ? "1" : "0";
        $query = "SELECT id,name,is_active 
                  FROM oic_app 
                  WHERE is_active = '$isActive'";
        try {
            $app = $this->executeQueryForList($query, $this->mapper[OnlineInteractiveClassServiceMapper::GET_APP_DETAILS]);
            return $app;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Fetch active meeting apps associated with a staff
     * @param $staffId
     * @throws ProfessionalException
     * @return Array $app
    */
    
    public function getAppsForStaff($staffId)
    {
        $query = " SELECT
                        oics.oic_app_id as id,
                        oa.name as name
                    FROM
                        staffaccounts sta
                    LEFT JOIN oic_users oics ON
                        sta.staffID = oics.staff_id
                    LEFT JOIN oic_app oa ON
                        oics.oic_app_id = oa.id
                    WHERE
                        1 = 1
                        AND sta.isResigned = 0
                        AND oics.is_active = 1
                        AND oa.is_active = 1
                        AND sta.staffID = '$staffId";
        try {
            $app = $this->executeQueryForList($query);
            return $app;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Fetch meeting app name
     * @param $meetingId
     * @throws ProfessionalException
     * @return Array $app
    */
    
    public function getMeetingAppName($meetingId)
    {
        if( !$meetingId || empty($meetingId) ){
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Meeting id is required");
        }
        
        $query = "SELECT oa.id AS appId, oa.name AS appName 
                  FROM v4_oic_meetings vom
                  INNER JOIN oic_users oc ON oc.id = vom.oic_users_id 
                  INNER JOIN oic_app oa ON oa.id = oc.oic_app_id AND oa.is_active = 1
                  WHERE vom.id = '$meetingId";
        try {
            
            $app = $this->executeQueryForList($query);
            return $app[0];
            
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Fetch join url via API
     * @param Meeting $meeting
     * @throws
     * @return
    */
        
    public function getJoinUrlApi(Meeting $meeting)
    {
        $app = $this->getActiveAppById($meeting->appId);
        switch ($app->name) {
            case OicApp::ZOOM:
                return $meeting->meetingDetails->join_url;
                break;
            case OicApp::OTHER:
                return $meeting->meetingDetails->join_url;
                break;
            case OicApp::MS_TEAMS:
                return $meeting->meetingDetails->joinWebUrl;
                break;
            case OicApp::KALTURA:
                $user = StudentService::getInstance()->getStudentDetailsById($_SESSION['studentID']);
                $kaltura = new Kaltura();
                return $kaltura->getJoinUrl($meeting->meetingDetails->eventId, $user);
                break;
        }
    }
    /**
     * Fetch host url via API
     * @param Meeting $meeting
     * @throws
     * @return
    */
     
    public function getHostUrlApi(Meeting $meeting)
    {
        $app = $this->getActiveAppById($meeting->appId);
        switch ($app->name) {
            case OicApp::ZOOM:
                return $meeting->meetingDetails->start_url;
                break;
            case OicApp::OTHER:
                return $meeting->meetingDetails->start_url;
                break;
            case OicApp::MS_TEAMS:
                return $meeting->meetingDetails->joinUrl;
                break;
            case OicApp::KALTURA:
                $getUserRequest = new SearchUsersRequest();
                $getUserRequest->id = $meeting->userId;
                $getUserRequest->appId = $meeting->appId;
                $user = $this->searchUsers($getUserRequest)[0];
                // $meeting->apiUserId = $user->apiDetails[0]->userDetails->id;
                $kaltura = new Kaltura();
                return $kaltura->getHostUrl($meeting->meetingDetails->eventId, $user, $meeting->userId);
                break;
        }
    }
    /**
     * Fetch staff meeting count
     * @param $request
     * @throws
     * @return
    */
     
    public function getAllStaffMeetingCount($request)
    {
        $request = $this->realEscapeObject($request);
        try {
            
            $sql = "SELECT sta.staffID,sta.staffAccount,om.oic_users_id,sta.staffName,sta.staffCode,count(distinct om.id) as meetingCount 
                    FROM v4_oic_meetings om 
                    INNER JOIN oic_users ou ON om.oic_users_id = ou.id 
                    INNER JOIN sbs_relation sbs ON sbs.staffID = ou.staff_id 
                    INNER JOIN batches bat ON bat.batchID = sbs.batchID AND bat.semID = sbs.semID
                    INNER JOIN studentaccount st ON st.batchID = sbs.batchID     
                    INNER JOIN v4_oic_students oics ON st.studentID = oics.student_id AND oics.oic_meetings_id = om.id
                    INNER JOIN staffaccounts sta ON sta.staffID = sbs.staffID
                    GROUP BY ou.staff_id ORDER BY ou.staff_id;";
                    
            return $this->executeQueryForList($sql);
            
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    
    /**
     * Fetch basic meeting details
     * @param $request
     * @throws ProfessionalException
     * @return 
    */
     
    public function getAllMeetingBasicDetails($request)
    {
        $request = $this->realEscapeObject($request);
        $where = [];
        $request->staffId ? $where[] = "ou.staff_id = $request->staffId" : null;
        $request->sbsIds ? $where[] = "sbs.sbsID in (" . implode(',', $request->sbsIds) . ")" : null;
        try {
            $sql = "SELECT om.id,om.name,om.description,om.meeting_date as meetingDate,
                    meetings_details,ou.staff_id,is_active,st.studentID,st.studentAccount,st.studentName,
                    group_concat(DISTINCT st.batchID) batches,CONCAT('[',group_concat(DISTINCT (JSON_OBJECT(oics.student_id,oics.attended))),']') AS attended,
                    count(DISTINCT oics.student_id) AS studentCount,group_concat(DISTINCT sbs.sbsID) AS sbsIds,bat.batchID,
                    group_concat(distinct bat.batchName)
                    FROM v4_oic_meetings om 
                    INNER JOIN oic_users ou ON om.oic_users_id = ou.id 
                    INNER JOIN sbs_relation sbs ON sbs.staffID = ou.staff_id 
                    INNER JOIN batches bat ON bat.batchID = sbs.batchID and bat.semID = sbs.semID
                    INNER JOIN studentaccount st ON st.batchID = sbs.batchID     
                    INNER JOIN v4_oic_students oics ON st.studentID = oics.student_id AND oics.oic_meetings_id = om.id
                    INNER JOIN staffaccounts sta ON sta.staffID = sbs.staffID
                    " . ((!empty($where)) ? " WHERE " . implode(' AND ', $where) : "") . " GROUP BY om.id ORDER BY om.meeting_date DESC;";
            
            return $this->executeQueryForList($sql);
            
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Search meeetings (optimised)
     * @param SearchMeetingsRequest $request
     * @throws ProfessionalException
     * @return Array $response
    */
     
    public function searchMeetingsOpt(SearchMeetingsRequest $request)
    {
        $request = $this->realEscapeObject($request);
        try {
            if ($request->studentId) {
                $durations = json_decode(CommonService::getInstance()->getSettings(SettingsConstants::ONLINE_CLASS, SettingsConstants::ONLINE_CLASS_LIST_STUDENT_DURATIONS));
            } else {
                $durations = json_decode(CommonService::getInstance()->getSettings(SettingsConstants::ONLINE_CLASS, SettingsConstants::ONLINE_CLASS_LIST_DURATIONS));
            }
        } catch (\Throwable $th) {
            $durations->upComingMeetingShowDurationInMinutes = 5;
            $durations->onGoingMeetingShowDurationInMinutes = 40;
        }
        $selectColmn = "SELECT om.id,om.v4_timetable_id,om.cluster_id,om.name,om.description,om.meetings_details,om.meeting_date,om.oic_users_id, IF(now() BETWEEN om.meeting_date+INTERVAL -" . $durations->upComingMeetingShowDurationInMinutes . " MINUTE AND om.meeting_date +INTERVAL +" . $durations->onGoingMeetingShowDurationInMinutes . " MINUTE,1,0) AS liveNow ,IF(now() < om.meeting_date+INTERVAL -" . $durations->upComingMeetingShowDurationInMinutes . " MINUTE,1,0) AS upComing ,IF(now() > om.meeting_date+INTERVAL +" . $durations->onGoingMeetingShowDurationInMinutes . " MINUTE,1,0) AS expiredMeeting ";
        $from = " FROM v4_oic_meetings om ";
        $where = " WHERE 1=1 ";
        $orderBy = " ORDER BY om.meeting_date ASC";
        if (!empty($request->id)) // Meeting id
        {
            $where .= " AND om.id='$request->id'";
        }
        // if(!empty($request->userId))
        // {   
        //     $where .= " AND om.user_id='$request->userId'";
        // }
        if (!empty($request->clusterId)) {
            $where .= " AND om.cluster_id='$request->clusterId'";
        }
        if (!empty($request->userId) && $request->userType == UserType::STAFF) {
            $selectColmn .= " ,ou.oic_app_id,ou.staff_id,sta.staffName ";
            $from .= " INNER JOIN oic_users ou ON om.oic_users_id = ou.id INNER JOIN staffaccounts sta ON sta.staffID=ou.staff_id ";
            $where .= " AND ou.staff_id='$request->userId";
        }
        if (!empty($request->batchId) || ((!empty($request->userId)) && ($request->userType == UserType::STUDENT)) || !empty($request->pssubId)) {
            $selectColmn .= "";
            $from .= " INNER JOIN v4_oic_students ost ON ost.oic_meetings_id = om.id ";
            if ($request->batchId) {
                $from .= str_contains($from, "studentaccount") ? "" : " INNER JOIN studentaccount sta ON sta.studentID=ost.student_id ";
                $where .= " AND sta.batchID='$request->batchId";
            }
            if ($request->userId && $request->userType == UserType::STUDENT) {
                $from .= str_contains($from, "studentaccount") ? "" : " INNER JOIN studentaccount sta ON sta.studentID = ost.student_id ";
                $where .= " AND ost.student_id ='$request->userId";
                $selectColmn .= " ,staf.staffName ";
                $from .= " INNER JOIN oic_users ou ON om.oic_users_id = ou.id INNER JOIN staffaccounts staf ON staf.staffID=ou.staff_id ";
            }
            if ($request->pssubId) {
                $from .= " INNER JOIN pseudosubjects_students psbs ON psbs.studentID=ost.student_id ";
                $where .= " AND psbs.pseudosubjectID='$request->pssubId";
            }
            //$query .= " AND om.id IN (SELECT DISTINCT oic_meetings_id FROM v4_oic_students ost INNER JOIN studentaccount sta ON sta.studentID=ost.student_id WHERE sta.batchID='$request->batchId')";
        }
        if (!empty($request->fromDate) && !empty($request->toDate)) {
            $where .= " AND DATE_FORMAT(CONVERT_TZ(om.meeting_date,
            '+00:00',
            @@global .time_zone),
            '%Y-%m-%d') BETWEEN '" . date('Y-m-d', strtotime($request->fromDate)) . "' AND '" . date('Y-m-d', strtotime($request->toDate)) . "'";
        } else if (!empty($request->fromDate)) {
            $where .= " AND DATE_FORMAT(CONVERT_TZ(sfrd.remitted_date,
            '+00:00',
            @@global .time_zone),
            '%Y-%m-%d') = '" . date('Y-m-d', strtotime($request->fromDate)) . "'";
        }
        // if(!empty($request->pssubId))
        // {
        //     $query .= " AND om.id IN (SELECT DISTINCT oic_meetings_id FROM v4_oic_students ost INNER JOIN pseudosubjects_students psbs ON psbs.studentID=ost.student_id WHERE psbs.pseudosubjectID='$request->pssubId')";
        // }
        if ($request->onGoingMeetingsOnly) {
            $where .= " AND now() BETWEEN om.meeting_date+INTERVAL -" . $durations->upComingMeetingShowDurationInMinutes . " MINUTE AND om.meeting_date +INTERVAL +" . $durations->onGoingMeetingShowDurationInMinutes . " MINUTE";
        }
        // $having = " HAVING ";
        // $having .= " expiredMeeting = 1 ";
        switch ($request->meetingType) {
            case 1:
                $having .= " liveNow = 1 ";
                break;
            case 2:
                $having .= " upComing = 1 ";
                break;
            case 3:
                $having .= " expiredMeeting = 1 ";
                break;
        }
        try {
            $query = $selectColmn . $from . $where . $having . $orderBy;
            $response = $this->executeQueryForList($query, $this->mapper[OnlineInteractiveClassServiceMapper::GET_MEETING_DETAILS]);
            foreach ($response as $meeting) {
                if (is_array($meeting->meetingDetails)) {
                    $meeting->meetingDetails = json_decode(json_encode($meeting->meetingDetails));
                }
                // if($meeting->meetingDate){
                //     $meetingDate = strtotime($meeting->meetingDate);
                //     $meeting->upComing = strtotime(date("Y-m-d H:i:s")) < strtotime("-".$durations->upComingMeetingShowDurationInMinutes." minutes",$meetingDate)?"1":"0";
                //     $meeting->liveNow = strtotime("-".$durations->upComingMeetingShowDurationInMinutes." minutes",$meetingDate) < strtotime(date("Y-m-d H:i:s")) && strtotime(date("Y-m-d H:i:s")) < strtotime("+".$durations->onGoingMeetingShowDurationInMinutes." minutes",$meetingDate)?"1":"0";
                //     $meeting->joinTimeLimit = $meeting->liveNow?$durations->onGoingMeetingShowDurationInMinutes:0;
                //     $meeting->expiredMeeting = strtotime("+".$durations->onGoingMeetingShowDurationInMinutes." minutes",$meetingDate) < strtotime(date("Y-m-d H:i:s"))?"1":"0";
                // }
            }
            return $response;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
}