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 / 52
CRAP
0.00% covered (danger)
0.00%
0 / 1295
ActivityPointService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 52
41412.00
0.00% covered (danger)
0.00%
0 / 1295
 __construct
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 4
 __clone
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 2
 getInstance
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 5
 getApplicationStatus
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 8
 getApplicationCode
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 8
 getAllAdditionalCreditItemIdByName
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 10
 getAllActivityPointItems
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 13
 getAppliedApplications
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 32
 getAppliedApplicationDocumentsByApplicationId
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 17
 getAppliedApplicationsByStaffId
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 30
 getActivityPointApplicationById
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 38
 getAdditionalCreditApplicationById
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 33
 getAllAdditionalCreditApplications
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 29
 getAllPendingApplicationsAssignedToStaff
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 41
 searchAdditionalCreditApplications
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 50
 getAllSupportingDocumentIdsByApplicationId
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 9
 createAdditionalCreditItems
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 25
 createAchievementDetail
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 11
 createAchievementMonthAndYear
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 11
 createActivityPointApplication
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 41
 addActivityPointApplicationPoints
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 14
 addActivityPointSupportingDocuments
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 16
 createActivityPointWorkFlowInstance
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 19
 createActivityPointApplicationLog
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 14
 UpdateAdditionalCreditApplication
0.00% covered (danger)
0.00%
0 / 1
72.00
0.00% covered (danger)
0.00%
0 / 96
 updateAllRemovedAdditionalCreditItems
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 14
 updateAdditionalCreditApplicationCredits
0.00% covered (danger)
0.00%
0 / 1
210.00
0.00% covered (danger)
0.00%
0 / 48
 updateAdditionalCreditSupportingDocuments
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 15
 editAdditionalCreditApplicationCredit
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 15
 reApplyAdditionalCreditApplication
0.00% covered (danger)
0.00%
0 / 1
56.00
0.00% covered (danger)
0.00%
0 / 27
 updateActivityPointApplicationLog
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 15
 updateAchieveMentmonthandyear
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 15
 updateAchievementDetail
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 15
 deleteAllAdditionalCreditSupportingDocumentsByApplicationCreditId
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 13
 removeAdditionalCreditSupportingDocuments
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 16
 deleteApplication
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 29
 deleteActivityPointApplicationPointsByApplicationId
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 8
 deleteActivityPointSupportingDocumentsByApplicationId
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 24
 deleteActivityPointApplicationLog
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 8
 approveOrRejectApplication
0.00% covered (danger)
0.00%
0 / 1
506.00
0.00% covered (danger)
0.00%
0 / 76
 processActivityPointReject
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 60
 processActivityPointApproval
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 61
 approveActivityPointApplication
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 15
 rejectActivityPointApplication
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 15
 approveAllPointsInsideAnActivityPointApplication
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 19
 rejectAllPointsInsideAnActivityPointApplication
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 19
 getStudentSemesterwiseTotalAdditionalCredit
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 29
 updateStudentSemesterwiseTotalAdditionalCredit
0.00% covered (danger)
0.00%
0 / 1
156.00
0.00% covered (danger)
0.00%
0 / 67
 addStudentSemesterwiseAdditionalCredit
0.00% covered (danger)
0.00%
0 / 1
42.00
0.00% covered (danger)
0.00%
0 / 31
 removeStudentSemesterwiseAdditionalCredit
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 17
 createWorkFlowStates
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 28
 createWorkFlowType
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 20
<?php
namespace com\linways\core\ams\professional\service;
use com\linways\core\ams\professional\constant\CreatedWorkFlows;
use com\linways\core\ams\professional\constant\Messages;
use com\linways\core\ams\professional\constant\StatusConstants;
use com\linways\core\ams\professional\constant\UserType;
use com\linways\core\ams\professional\constant\AdditionalCredit;
use com\linways\core\ams\professional\constant\ActivityPoint;
use com\linways\core\ams\professional\dto\AdditionalCreditApplication;
use com\linways\core\ams\professional\dto\AdditionalCreditLog;
use com\linways\core\ams\professional\exception\ProfessionalException;
use com\linways\core\ams\professional\logging\AMSLogger;
use com\linways\core\ams\professional\logging\entities\AdditionalCreditItem;
use com\linways\core\ams\professional\logging\entities\Staff;
use com\linways\core\ams\professional\logging\Events;
use com\linways\core\ams\professional\mapper\AdditionalCreditServiceMapper;
use com\linways\core\ams\professional\request\AddAdditionalCreditItemsRequest;
use com\linways\core\ams\professional\request\AddAchievementDetailRequest;
use com\linways\core\ams\professional\request\AddAchievementMonthAndYearRequest;
use com\linways\core\ams\professional\request\AdditionalCreditApplicationLogRequest;
use com\linways\core\ams\professional\request\ApproveAdditionalCreditApplicationRequest;
use com\linways\core\ams\professional\request\ApproveAllAdditionalCreditsRequest;
use com\linways\core\ams\professional\request\ApproveOrRejectAdditionalCreditsApplicationRequest;
use com\linways\core\ams\professional\request\CreateAdditionalCreditApplicationCreditsRequest;
use com\linways\core\ams\professional\request\CreateAdditionalCreditApplicationRequest;
use com\linways\core\ams\professional\request\DeleteAdditionalCreditApplicationRequest;
use com\linways\core\ams\professional\request\ReApplyAdditionalCreditApplicationRequest;
use com\linways\core\ams\professional\request\RejectAdditionalCreditApplicationRequest;
use com\linways\core\ams\professional\request\RejectAllAdditionalCreditsRequest;
use com\linways\core\ams\professional\request\RemoveResourceRequest;
use com\linways\core\ams\professional\request\SearchAdditionalCreditApplicationRequest;
use com\linways\core\ams\professional\request\UpdateAdditionalCreditApplicationCreditsRequest;
use com\linways\core\ams\professional\request\UpdateAdditionalCreditApplicationLogRequest;
use com\linways\core\ams\professional\request\UpdateAdditionalCreditApplicationRequest;
use com\linways\workflow\api\constants\WorkflowActions;
use com\linways\workflow\api\request\MoveToNextStateApiRequest;
use com\linways\workflow\api\Workflow;
use com\linways\workflow\api\WorkflowInstance;
use com\linways\core\ams\professional\request\SearchAdditionalCreditRequest;
use com\linways\core\ams\professional\dto\ActivityPointLog;
use com\linways\workflow\core\dto\Workflow as WorkflowDTO;
use com\linways\workflow\core\request\WorkflowSearchRequest;
use stdClass;
use com\linways\workflow\core\service\WorkflowService;
use com\linways\core\ams\professional\request\DeleteActivityPointApplicationRequest;
class ActivityPointService extends BaseService
{
    // /Condition 1 - Presence of a static member variable
    private static $_instance = null;
    private $mapper = [];
    private $logger = null;
    // /Condition 2 - Locked down the constructor
    private function __construct()
    {
        $this->logger = AMSLogger::getLogger();
        $this->mapper = AdditionalCreditServiceMapper::getInstance()->getMapper();
    }
    // Prevent any outside 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;
    }
    /***************************************************************************************************************
     *                                                                                                             *
     *                                  FETCHING APPLICATION DETAILS SECTIONS                                      *
     *                                                                                                             *
     ***************************************************************************************************************/
    /**
     * @param $applicationId
     * @return string
     * @throws ProfessionalException
     */
    public function getApplicationStatus($applicationId)
    {
        $sql = "SELECT status FROM additional_credit_application WHERE id = $applicationId";
        try {
            return $this->executeQueryForObject($sql)->status;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param $applicationId
     * @return string
     * @throws ProfessionalException
     */
    public function getApplicationCode($applicationId)
    {
        $sql = "SELECT application_type_code FROM activity_point_application WHERE id = $applicationId";
        try {
            return $this->executeQueryForObject($sql)->application_type_code;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param $name
     * @return Object
     * @throws ProfessionalException
     */
    public function getAllAdditionalCreditItemIdByName($name)
    {
        $name = $this->realEscapeString($name);
        $sql = "SELECT id FROM additional_credit_items 
                WHERE  REPLACE(UPPER(name),' ','') =REPLACE(UPPER('$name'),' ','')";
        try {
            return $this->executeQueryForObject($sql)->id;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @return Array
     * @throws ProfessionalException
     */
    public function getAllActivityPointItems()
    {
        $sql = "SELECT 
                    id, name, description, activity_points
                FROM
                    student_feedback_type
                WHERE
                    is_activity = 1";
        try {
            return $this->executeQueryForList($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @return Array
     * @throws ProfessionalException
     */
    public function getAppliedApplications($studentId)
    {
        $sql = "SELECT 
                    apa.id AS applicationId,
                    apap.id AS appPointsId,
                    apasd.id AS documentId,
                    apa.status,
                    apap.activity_point,
                    sft.name as activityName,
                    apasd.resource_id as documentResourceId,
                    sa.staffName,
                    apa.created_at,
                    apa.remarks
                FROM
                    activity_point_application apa
                        INNER JOIN
                    activity_point_application_points apap ON apa.id = apap.application_id
                        INNER JOIN
                    activity_point_application_supporting_documents apasd ON apasd.activity_point_application_id = apa.id
                        LEFT JOIN
                    student_feedback_type sft ON apap.activity_point_id = sft.id
                        LEFT JOIN
                    staffaccounts sa ON apa.staff_id = sa.staffID
                WHERE
                    apa.student_id = '$studentId
                group BY apa.id 
                Order by apa.created_at DESC";
        try {
            return $this->executeQueryForList($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @return Array
     * @throws ProfessionalException
     */
    public function getAppliedApplicationDocumentsByApplicationId($applicationId)
    {
        $sql = "SELECT 
                    id,
                    activity_point_application_id as applicationId,
                    resource_id,
                    created_by,
                    created_at
                FROM
                    activity_point_application_supporting_documents apasd
                WHERE
                    apasd.activity_point_application_id = '$applicationId";
        try {
            return $this->executeQueryForList($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @return Array
     * @throws ProfessionalException
     */
    public function getAppliedApplicationsByStaffId($staffId)
    {
        $sql = "SELECT 
                    apa.id AS applicationId,
                    apap.id AS appPointsId,
                    apa.status,
                    apap.activity_point,
                    sft.name as activityName,
                    sa.studentName,
                    apa.student_id,
                    sa.regNo,
                    apa.created_at,
                    apap.activity_point_id as actPointId
                FROM
                    activity_point_application apa
                        INNER JOIN
                    activity_point_application_points apap ON apa.id = apap.application_id
                        LEFT JOIN
                    student_feedback_type sft ON apap.activity_point_id = sft.id
                        LEFT JOIN
                    studentaccount sa ON sa.studentID = apa.student_id
                WHERE
                    apa.staff_id = '$staffId
                    group by applicationId
                    order by apa.created_at DESC";
        try {
            return $this->executeQueryForList($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @return Object
     * @throws ProfessionalException
     */
    public function getActivityPointApplicationById($applicationId)
    {
        $sql = "SELECT 
                    apa.id AS applicationId,
                    apap.id AS appPointsId,
                    apasd.id AS documentId,
                    apa.status,
                    apap.activity_point,
                    sft.name as activityName,
                    apasd.resource_id as documentResourceId,
                    sa.staffName,
                    std.studentName,
                    std.regNo,
                    apa.created_at,
                    apa.workflow_instance_id as workflowInstanceId,
                    appl.log,
                    apa.remarks
                FROM
                    activity_point_application apa
                        INNER JOIN
                    activity_point_application_points apap ON apa.id = apap.application_id
                        INNER JOIN
                    activity_point_application_supporting_documents apasd ON apasd.activity_point_application_id = apa.id
                        INNER JOIN
                    activity_point_application_log appl ON appl.application_id = apa.id
                        LEFT JOIN
                    student_feedback_type sft ON apap.activity_point_id = sft.id
                        LEFT JOIN
                    staffaccounts sa ON apa.staff_id = sa.staffID
                        LEFT JOIN
                    studentaccount std ON apa.student_id = std.studentID
                WHERE
                    apa.id = '$applicationId";
        try {
            return $this->executeQueryForObject($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param $applicationId
     * @return Object|AdditionalCreditApplication
     * @throws ProfessionalException
     */
    public function getAdditionalCreditApplicationById($applicationId)
    {
        $applicationId = $this->realEscapeString($applicationId);
        if (empty($applicationId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_ADDITIONAL_CREDIT_APPLICATION_ID, "Invalid application credit details given");
        }
        $sql = "SELECT aca.*,
                acac.id as application_credit_id,acac.updated_date as credit_updated_date,
                acac.created_date as credit_created_date,acac.credit,acac.updated_by as credit_updated_by,
                acac.created_by as credit_created_by,acac.remarks as credit_remarks,acac.processed_by as credit_processed_by,
                acac.status as credit_status,
                aci.id as credit_item_id,aci.name as credit_item_name,
                acasd.id as supporting_document_id,acasd.additional_credit_application_credit_id,acasd.resource_id,
                acasd.created_by as document_created_by,acasd.created_date as document_created_date,
                acasd.updated_date as document_updated_date,acasd.updated_by as document_updated_by,
                acal.id as log_id,acal.updated_by as log_updated_by,acal.updated_date as log_updated_date,
                acal.created_date as log_created_date,acal.created_by as log_created_by,acal.log,
                applicationProcessedStaff.staffName as application_processed_staff_name,
                creditProcessedStaff.staffName as credit_processed_staff_name
                FROM additional_credit_application aca
                INNER JOIN application_types at ON at.code = aca.application_type_code
                LEFT JOIN additional_credit_application_credits acac on aca.id = acac.application_id
                LEFT JOIN additional_credit_items aci ON aci.id = acac.additional_credit_item_id
                LEFT JOIN additional_credit_application_supporting_documents acasd ON acac.id = acasd.additional_credit_application_credit_id
                LEFT JOIN additional_credit_application_log acal ON aca.id = acal.application_id
                LEFT JOIN staffaccounts as creditProcessedStaff ON creditProcessedStaff.staffID = acac.processed_by
                LEFT JOIN staffaccounts as applicationProcessedStaff ON applicationProcessedStaff.staffID = aca.processed_by
                WHERE aca.id = $applicationId";
        try {
            return $this->executeQueryForObject($sql, false, $this->mapper[AdditionalCreditServiceMapper::GET_ADDITIONAL_CREDIT_APPLICATION]);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Returns all additional credit application
     * @return Object|AdditionalCreditApplication
     * @throws ProfessionalException
     */
    public function getAllAdditionalCreditApplications()
    {
        $sql = "SELECT aca.*,
                acac.id as application_credit_id,acac.updated_date as credit_updated_date,
                acac.created_date as credit_created_date,acac.credit,acac.updated_by as credit_updated_by,
                acac.created_by as credit_created_by,acac.remarks as credit_remarks,acac.processed_by as credit_processed_by,
                acac.status as credit_status,
                aci.id as credit_item_id,aci.name as credit_item_name,
                acasd.id as supporting_document_id,acasd.additional_credit_application_credit_id,acasd.resource_id,
                acasd.created_by as document_created_by,acasd.created_date as document_created_date,
                acasd.updated_date as document_updated_date,acasd.updated_by as document_updated_by,
                acal.id as log_id,acal.updated_by as log_updated_by,acal.updated_date as log_updated_date,
                acal.created_date as log_created_date,acal.created_by as log_created_by,
                applicationProcessedStaff.staffName as application_processed_staff_name,
                creditProcessedStaff.staffName as credit_processed_staff_name
                FROM additional_credit_application aca
                INNER JOIN application_types at on at.code = aca.application_type_code
                LEFT JOIN additional_credit_application_credits acac on aca.id = acac.application_id
                LEFT JOIN additional_credit_items aci ON aci.id = acac.additional_credit_item_id
                LEFT JOIN additional_credit_application_supporting_documents acasd ON acac.id = acasd.additional_credit_application_credit_id
                LEFT JOIN additional_credit_application_log acal ON aca.id = acal.application_id
                LEFT JOIN staffaccounts as creditProcessedStaff ON creditProcessedStaff.staffID = acac.processed_by
                LEFT JOIN staffaccounts as applicationProcessedStaff ON applicationProcessedStaff.staffID = aca.processed_by
                WHERE 1=1";
        try {
            return $this->executeQueryForObject($sql, false, $this->mapper[AdditionalCreditServiceMapper::GET_ADDITIONAL_CREDIT_APPLICATION]);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Returns all pending application list
     * @param $staffId
     * @return Object|AdditionalCreditApplication[]
     * @throws ProfessionalException
     */
    public function getAllPendingApplicationsAssignedToStaff($staffId, $applicationCode)
    {
        $sql = "SELECT aca.id,aca.semester_id,aca.student_id,aca.status,aca.created_by,aca.updated_by,aca.remarks,aca.remarks,
                aca.processed_by,
                DATE_FORMAT(CONVERT_TZ(aca.created_date,'+00:00',@@global.time_zone),'%d-%m-%Y %h:%i %p') as created_date,
                DATE_FORMAT(CONVERT_TZ(aca.updated_date,'+00:00',@@global.time_zone),'%d-%m-%Y %h:%i %p') as updated_date,
                acac.id as application_credit_id,
                DATE_FORMAT(CONVERT_TZ(acac.updated_date,'+00:00',@@global.time_zone),'%d-%m-%Y %h:%i %') as credit_updated_date,
                DATE_FORMAT(CONVERT_TZ(acac.created_date,'+00:00',@@global.time_zone),'%d-%m-%Y %h:%i %p') as credit_created_date,
                acac.credit,acac.updated_by as credit_updated_by,
                acac.created_by as credit_created_by,acac.remarks as credit_remarks,acac.processed_by as credit_processed_by,
                acac.status as credit_status,
                aci.id as credit_item_id,aci.name as credit_item_name,
                acasd.id as supporting_document_id,acasd.additional_credit_application_credit_id,acasd.resource_id,
                acasd.created_by as document_created_by,acasd.created_date as document_created_date,
                acasd.updated_date as document_updated_date,acasd.updated_by as document_updated_by,
                acal.id as log_id,acal.updated_by as log_updated_by,acal.updated_date as log_updated_date,
                acal.created_date as log_created_date,acal.created_by as log_created_by,acal.log,
                applicationProcessedStaff.staffName as application_processed_staff_name,
                creditProcessedStaff.staffName as credit_processed_staff_name,
                s.semName,s2.studentName,s2.regNo,b.batchID,b.batchName,am.monthandyear as achievement_date,ad.detail as achievement_details
                FROM additional_credit_application aca
                INNER JOIN application_types at ON at.code = \"" . $applicationCode . "\" 
                INNER JOIN studentaccount s2 on aca.student_id = s2.studentID
                INNER JOIN batches b ON b.batchID = s2.batchID
                INNER JOIN batch_tutor bt ON bt.batchID  =b.batchID
                LEFT JOIN additional_credit_application_credits acac on aca.id = acac.application_id
                LEFT JOIN additional_credit_items aci ON aci.id = acac.additional_credit_item_id
                LEFT JOIN additional_credit_application_supporting_documents acasd ON acac.id = acasd.additional_credit_application_credit_id
                LEFT JOIN additional_credit_application_log acal ON aca.id = acal.application_id
                LEFT JOIN staffaccounts as creditProcessedStaff ON creditProcessedStaff.staffID = acac.processed_by
                LEFT JOIN staffaccounts as applicationProcessedStaff ON applicationProcessedStaff.staffID = aca.processed_by
                LEFT JOIN semesters s ON s.semID  =aca.semester_id
                LEFT JOIN achievement_monthandyear am on am.id = acac.achievement_monthandyear_id
                LEFT JOIN achievement_details ad on ad.id = acac.achievement_details_id
                WHERE 1=1 AND bt.staffID = $staffId and aca.application_type_code = at.code ORDER BY aca.created_date DESC";
        try {
            return $this->executeQueryForList($sql, $this->mapper[AdditionalCreditServiceMapper::GET_PENDING_APPLICATIONS_LIST]);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * TODO:  Add pagination queries and search filter queries
     * Searching additional credit applications
     * @param SearchAdditionalCreditApplicationRequest $request
     * @return Object|AdditionalCreditApplication[]
     * @throws ProfessionalException
     */
    public function searchAdditionalCreditApplications(SearchAdditionalCreditApplicationRequest $request)
    {
        $request = $this->realEscapeObject($request);
        $sql = "SELECT aca.id,aca.semester_id,aca.student_id,aca.status,aca.created_by,aca.updated_by,aca.remarks,aca.remarks,
                aca.processed_by,
                DATE_FORMAT(CONVERT_TZ(aca.created_date,'+00:00',@@global.time_zone),'%d-%m-%Y %h:%i %p') as created_date,
                DATE_FORMAT(CONVERT_TZ(aca.updated_date,'+00:00',@@global.time_zone),'%d-%m-%Y %h:%i %p') as updated_date,
                acac.id as application_credit_id,
                DATE_FORMAT(CONVERT_TZ(acac.updated_date,'+00:00',@@global.time_zone),'%d-%m-%Y %h:%i %p') as credit_updated_date,
                DATE_FORMAT(CONVERT_TZ(acac.created_date,'+00:00',@@global.time_zone),'%d-%m-%Y %h:%i %p') as credit_created_date,
                acac.credit,acac.updated_by as credit_updated_by,
                acac.created_by as credit_created_by,acac.remarks as credit_remarks,acac.processed_by as credit_processed_by,
                acac.status as credit_status,
                aci.id as credit_item_id,aci.name as credit_item_name,
                acasd.id as supporting_document_id,acasd.additional_credit_application_credit_id,acasd.resource_id,
                acasd.created_by as document_created_by,acasd.created_date as document_created_date,
                acasd.updated_date as document_updated_date,acasd.updated_by as document_updated_by,
                acal.id as log_id,acal.updated_by as log_updated_by,acal.updated_date as log_updated_date,
                acal.created_date as log_created_date,acal.created_by as log_created_by,acal.log,
                applicationProcessedStaff.staffName as application_processed_staff_name,
                creditProcessedStaff.staffName as credit_processed_staff_name,
                s.semName,lr.storage_object,ad.id as achievement_details_id,ad.detail as achievement_details,am.id as achievement_date_id,am.monthandyear as achievement_date
                FROM additional_credit_application aca
                INNER JOIN application_types at on at.code = aca.application_type_code and at.code = \"" . $request->applicationTypeCode . "\"
                LEFT JOIN additional_credit_application_credits acac on aca.id = acac.application_id
                LEFT JOIN additional_credit_items aci ON aci.id = acac.additional_credit_item_id
                LEFT JOIN additional_credit_application_supporting_documents acasd ON acac.id = acasd.additional_credit_application_credit_id
                LEFT JOIN additional_credit_application_log acal ON aca.id = acal.application_id
                LEFT JOIN staffaccounts as creditProcessedStaff ON creditProcessedStaff.staffID = acac.processed_by
                LEFT JOIN staffaccounts as applicationProcessedStaff ON applicationProcessedStaff.staffID = aca.processed_by
                LEFT JOIN semesters s ON s.semID  =aca.semester_id
                LEFT JOIN lin_resource lr on acasd.resource_id = lr.id
                LEFT JOIN achievement_details ad on ad.id = acac.achievement_details_id
                LEFT JOIN achievement_monthandyear am on am.id = acac.achievement_monthandyear_id
                WHERE 1=1 ";
        if (!empty($request->studentId)) {
            $sql .= " AND aca.student_id  =$request->studentId";
        }
        if (!empty($request->applicationId)) {
            $sql .= " AND aca.id = $request->applicationId";
        }
        if (!empty($request->status)) {
            $sql .= " AND aca.status = '$request->status'";
        }
        $sql .= " ORDER BY aca.created_date DESC";
        try {
            return $this->executeQueryForList($sql, $this->mapper[AdditionalCreditServiceMapper::GET_ADDITIONAL_CREDIT_APPLICATION]);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Returns all the activity point application supporting document ids
     * @param $applicationId
     * @return Object
     * @throws ProfessionalException
     */
    private function getAllSupportingDocumentIdsByApplicationId($applicationId)
    {
        $sql = "SELECT apasd.resource_id as id FROM activity_point_application_supporting_documents apasd
                WHERE apasd.activity_point_application_id = $applicationId";
        try {
            return $this->executeQueryForList($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /***************************************************************************************************************
     *                                                                                                             *
     *                                              CREATE SECTIONS                                                *
     *                                                                                                             *
     ***************************************************************************************************************/
    /**
     * @param AddAdditionalCreditItemsRequest $request
     * @return Object
     * @throws ProfessionalException
     */
    public function createAdditionalCreditItems(AddAdditionalCreditItemsRequest $request)
    {
        $request = $this->realEscapeObject($request);
        $sql = "INSERT INTO additional_credit_items (name, created_by, created_date, updated_by, updated_date) 
                VALUES ('$request->name',$request->createdBy,UTC_TIMESTAMP(),$request->updatedBy,UTC_TIMESTAMP())";
        try {
            $id = $this->executeQueryForObject($sql, true);
            $this->logger->info(Events::ADDITIONAL_CREDIT_ITEM_CREATION, [
                "userId" => $request->createdBy,
                "userType" => UserType::STUDENT,
                "additionCreditItem" => new AdditionalCreditItem(["id" => $id]),
                "status" => StatusConstants::SUCCESS
            ]);
            return $id;
        } catch (\Exception $e) {
            AMSLogger::log_error($this->logger,Events::ADDITIONAL_CREDIT_ITEM_CREATION, [
                "userId" => $request->createdBy,
                "userType" => UserType::STUDENT,
                "itemName" => $request->name,
                "status" => StatusConstants::FAILED,
                "error" => $e->getCode(),
                "message" => $e->getMessage()
            ]);
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param AddAchievementDetailRequest $request
     * @return Object
     * @throws ProfessionalException
     */
    public function createAchievementDetail(AddAchievementDetailRequest $request)
    {
        $request = $this->realEscapeObject($request);
        $sql = "INSERT INTO achievement_details (detail, created_by, created_date, updated_by, updated_date) 
                VALUES ('$request->detail',$request->createdBy,UTC_TIMESTAMP(),$request->updatedBy,UTC_TIMESTAMP())";
        try {
            $id = $this->executeQueryForObject($sql, true);
            return $id;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param AddAchievementMonthAndYearRequest $request
     * @return Object
     * @throws ProfessionalException
     */
    public function createAchievementMonthAndYear(AddAchievementMonthAndYearRequest $request)
    {
        $request = $this->realEscapeObject($request);
        $sql = "INSERT INTO achievement_monthandyear (monthandyear, created_by, created_date, updated_by, updated_date) 
                VALUES ('$request->monthAndYear',$request->createdBy,UTC_TIMESTAMP(),$request->updatedBy,UTC_TIMESTAMP())";
        try {
            $id = $this->executeQueryForObject($sql, true);
            return $id;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param createActivityPointApplication $request
     * @return Object
     * @throws ProfessionalException
     */
    public function createActivityPointApplication($request)
    {
        $request = $this->realEscapeObject($request);
        $status = StatusConstants::PENDING;
        if (empty($request->semID)) {
            throw new ProfessionalException(ProfessionalException::INVALID_SEMESTER_ID, "Semester details not given");
        }
        if (empty($request->studentID)) {
            throw new ProfessionalException(ProfessionalException::INVALID_STUDENT_ID, "Invalid operation performed");
        }
        try {
            /**
             * Creating workflow instance
             */
            $workFlowInstanceId = $this->createActivityPointWorkFlowInstance($request->createdBy);
            if(!$workFlowInstanceId){
                throw new ProfessionalException(ProfessionalException::INAVLID_ACTIVITY_POINT_APPLICATION, "Worflow Instance not found!");
            }
            /**
             * Creating additional credit application
             */
            $sql = "INSERT INTO activity_point_application 
                    (student_id, semester_id, workflow_instance_id, application_type_code,staff_id
                        ,status,created_by) 
                    VALUES ('$request->studentID','$request->semID','$workFlowInstanceId',\"" . $request->applicationTypeCode . "\", '$request->staffId',  
                            '$status','$request->createdBy')";
            $request->id = $this->executeQueryForObject($sql, true);
            $this->addActivityPointApplicationPoints($request);
            $activityPointLog = new ActivityPointLog();
            $activityPointLog->status = "PENDING";
            $activityPointLog->updatedBy = $request->updatedBy;
            $activityPointLog->createdBy = $request->createdBy;
            $activityPointLog->actionTakenTime = date("Y-m-d h:i A");
            $activityPointLog->appliedStudentName = StudentService::getInstance()->getStudentNameById($request->createdBy);
            $activityPointLog->message = Messages::ACTIVITY_POINT_REQUEST_CREATED;
            $activityPointLog->staffId = $request->staffId;;
            $activityPointLog->appliedBy = $request->createdBy;
            $createActivityPointLog = new stdClass();
            $createActivityPointLog->log = json_encode([$activityPointLog]);
            $createActivityPointLog->applicationId = $request->id;
            $createActivityPointLog->createdBy = $request->createdBy;
            $createActivityPointLog->updatedBy = $request->updatedBy;
            $this->createActivityPointApplicationLog($createActivityPointLog);
            /**
             * Ams Logger
             */
            // $this->logger->info(Events::ACTIVITY_POINT_APPLICATION, [
            //     "userId" => $request->createdBy,
            //     "userType" => UserType::STUDENT,
            //     "additionCreditApplication" => new AdditionalCreditApplication(["id" => $request->id]),
            //     "status" => StatusConstants::SUCCESS
            // ]);
        } catch (\Exception $e) {
            // AMSLogger::log_error($this->logger,Events::ACTIVITY_POINT_APPLICATION, [
            //     "userId" => $request->createdBy,
            //     "userType" => UserType::STUDENT,
            //     "status" => StatusConstants::FAILED,
            //     "request" => $request,
            //     "error" => $e->getCode(),
            //     "message" => $e->getMessage()
            // ]);
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
        return $request->id;
    }
    /**
     * @param CreateAdditionalCreditApplicationRequest $request
     * @return void
     * @throws ProfessionalException
     */
    public function addActivityPointApplicationPoints($request)
    {
        $request = $this->realEscapeObject($request);
        $status = StatusConstants::PENDING;
        try {
            
            $sql = "INSERT INTO activity_point_application_points (application_id, activity_point_id,activity_point,status, 
                        processed_by, created_by) 
                    VALUES ($request->id,$request->activityPointItemId,$request->activityPointValue,'$status',
                        $request->staffId$request->createdBy)";
            $this->executeQuery($sql);
            $this->addActivityPointSupportingDocuments($request);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param CreateAdditionalCreditApplicationCreditsRequest $request
     * @throws ProfessionalException
     */
    public function addActivityPointSupportingDocuments( $request)
    {
        $request = $this->realEscapeObject($request);
        if (empty($request->id)) {
            throw new ProfessionalException(ProfessionalException::INTERNAL_SERVICE_FAILURE, "Internal server error occurred. Contact system administrator");
        }
        try {
            foreach ($request->supportingDocuments as $supportDocuments) {
                $sql = "INSERT INTO activity_point_application_supporting_documents (activity_point_application_id, 
                        resource_id,created_by) 
                        VALUES ($request->id,'$supportDocuments->resourceId',$request->createdBy)";
                $this->executeQuery($sql);
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param $createdUser
     * @return int|void
     * @throws ProfessionalException
     */
    protected function createActivityPointWorkFlowInstance($createdUser)
    {
        $codeOrId = CreatedWorkFlows::ACTIVITY_POINT_APPLICATION_WORKFLOW;
        $requesterUserType = UserType::STUDENT;
        try {
            $workflowSearchRequest = new WorkflowSearchRequest();
            $workflowSearchRequest->code = $codeOrId;
            $workflowList = WorkflowService::getInstance()->searchWorkflow($workflowSearchRequest);
            if(empty($workflowList->workflowList))
                return;
            // if(empty($workflowList->workflowList)){
            //     $workflow = new WorkflowDTO();
            //     $workflow->code = CreatedWorkFlows::ACTIVITY_POINT_APPLICATION_WORKFLOW;
            //     $workflow->name = "ACTIVITY POINT APPLICATION WORKFLOW";
            //     $worlflowTypeId = $this->createWorkFlowType($workflow);
            //     $workflow->workflowTypeId = 4;
            //     $workflow->createdBy = 1;
            //     $workflow->updatedBy = 1;
            //     $workflowId =  WorkflowService::getInstance()->createWorkflow($workflow);
            //     $this->createWorkFlowStates($workflowId);
            // }
            $workflow = new Workflow($codeOrId);
            if (empty($workflow->getId())) {
                return fault("Activity Point application creation failed. Faculties not assigned for this application approval. Contact your class tutor/mentor");
            }
            $workflowInstance = new WorkflowInstance(WorkflowActions::CREATE, $codeOrId, $createdUser, $requesterUserType);
            return $workflowInstance->getId();
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Creating additional credit application log.
     * TODO : This can be used to show the application tracking and history in future
     * @param AdditionalCreditApplicationLogRequest $request
     * @throws ProfessionalException
     */
    public function createActivityPointApplicationLog($request)
    {
        $request->applicationId = $this->realEscapeString($request->applicationId);
        if (empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_ADDITIONAL_CREDIT_APPLICATION_ID, "Invalid additional credit application details given");
        }
        $request->log = addslashes($request->log);
        $sql = "INSERT INTO activity_point_application_log (application_id, log, created_by) 
                VALUES ($request->applicationId,'$request->log',$request->createdBy)";
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /***************************************************************************************************************
     *                                                                                                             *
     *                                          UPDATE APPLICATION SECTIONS                                        *
     *                                                                                                             *
     ***************************************************************************************************************/
    /**
     * Update Additional Credit Application
     * Step 1:
     *
     * Finding removed additional credit application credit items and updating other details
     * @param UpdateAdditionalCreditApplicationRequest $request
     * @throws ProfessionalException
     */
    public function UpdateAdditionalCreditApplication(UpdateAdditionalCreditApplicationRequest $request)
    {
        $status = "";
        $request = $this->realEscapeObject($request);
        try {
            /**
             * Checking additional credit application available in the database, if available finding removed application
             * credit items
             */
            $applicationDetails = $this->getAdditionalCreditApplicationById($request->id);
            if (empty($applicationDetails)) {
                throw new ProfessionalException(ProfessionalException::INVALID_ADDITIONAL_CREDIT_APPLICATION_ID, "Application doesn't found");
            }
//            TODO: Use this code when multiple additional credit item added to the application in the future
//            DONT REMOVE THIS CODE, THIS MAY USE FULL IN FUTURE
//            $removedCreditItems = [];
//            foreach ($applicationDetails->applicationCredits as $credit) {
//                $isFound = false;
//                foreach ($request->additionalCreditApplicationCredits as $updatedCredit) {
//                    if ($credit->additionalCreditItemId == $updatedCredit->additionalCreditItemId) {
//                        $isFound = true;
//                    }
//                }
//                if (!$isFound) {
//                    array_push($removedCreditItems, $credit->additionalCreditItemId);
//                }
//            }
//            /**
//             * Removing user removed additional credit items from the application
//             */
//            $this->updateAllRemovedAdditionalCreditItems($removedCreditItems, $request->id);
            /**
             * Updating existing application credit item id and credit values
             */
            $this->updateAdditionalCreditApplicationCredits($applicationDetails, $request->additionalCreditApplicationCredits, $request);
            if ($applicationDetails->status === StatusConstants::REJECTED) {
                $status = StatusConstants::RE_APPLIED;
            } elseif ($applicationDetails->status === StatusConstants::RE_APPLIED) {
                $status = StatusConstants::RE_APPLIED;
            } else {
                $status = StatusConstants::PENDING;
            }
            /**
             * Updating basic details of the additional credit application
             */
            $sql = "UPDATE additional_credit_application 
                    SET semester_id  =$request->semesterId,status='$status',
                    updated_date = UTC_TIMESTAMP(),updated_by=$request->updatedBy 
                    WHERE id = $request->id";
            $this->executeQuery($sql);
            $applicationTypeCode = $this->getApplicationCode($request->id);
            /**
             * If student edited the application after tutor reject, we need to change the application status to Re-Applied
             */
            if ($applicationDetails->status === StatusConstants::REJECTED) {
                $reApplyRequest = new ReApplyAdditionalCreditApplicationRequest();
                $reApplyRequest->applicationId = $request->id;
                $reApplyRequest->userType = $request->userType;
                $reApplyRequest->reAppliedBy = $request->updatedBy;
                $this->reApplyAdditionalCreditApplication($reApplyRequest);
                /**
                 * Adding database log
                 */
                $log = json_decode($applicationDetails->applicationLog->log);
                $additionalCreditLog = new AdditionalCreditLog();
                $additionalCreditLog->status = StatusConstants::RE_APPLIED;
                $additionalCreditLog->updatedBy = $request->updatedBy;
                $additionalCreditLog->createdBy = $request->createdBy;
                $additionalCreditLog->actionTakenTime = date("Y-m-d h:i A");
                $additionalCreditLog->appliedStudentName = StudentService::getInstance()->getStudentNameById($request->createdBy);
                $additionalCreditLog->updatedByUserType = $request->userType;
                if ($applicationTypeCode == AdditionalCredit::ACHIEVEMENTS) {
                    $additionalCreditLog->message = Messages::ACHIEVEMENT_APPLICATION_RE_APPLIED;
                } else {
                    $additionalCreditLog->message = Messages::ADDITIONAL_CREDIT_APPLICATION_RE_APPLIED;
                }
                $additionalCreditLog->appliedBy = $request->processedBy;
                $additionalCreditLog->remarks = $request->remarks;
                array_push($log, $additionalCreditLog);
                $updateLog = new UpdateAdditionalCreditApplicationLogRequest();
                $updateLog->log = json_encode($log);
                $updateLog->applicationId = $applicationDetails->id;
                $updateLog->updatedBy = $request->updatedBy;
                $this->updateActivityPointApplicationLog($updateLog);
                $this->logger->info(Events::ADDITIONAL_CREDIT_APPLICATION_RE_SUBMISSION, [
                    "userId" => $request->createdBy,
                    "userType" => UserType::STUDENT,
                    "request" => $request,
                    "status" => StatusConstants::SUCCESS
                ]);
            } else {
                /**
                 * Adding the additional credit application log
                 */
                $log = json_decode($applicationDetails->applicationLog->log);
                $additionalCreditLog = new AdditionalCreditLog();
                $additionalCreditLog->status = StatusConstants::PENDING;
                $additionalCreditLog->updatedBy = $request->updatedBy;
                $additionalCreditLog->createdBy = $request->createdBy;
                $additionalCreditLog->actionTakenTime = date("Y-m-d h:i A");
                $additionalCreditLog->appliedStudentName = StudentService::getInstance()->getStudentNameById($request->createdBy);
                $additionalCreditLog->updatedByUserType = $request->userType;
                if ($applicationTypeCode == AdditionalCredit::ACHIEVEMENTS) {
                    $additionalCreditLog->message = Messages::ACHIEVEMENT_APPLICATION_UPDATED;
                } else {
                    $additionalCreditLog->message = Messages::ADDITIONAL_CREDIT_APPLICATION_UPDATED;
                }
                $additionalCreditLog->appliedBy = $request->processedBy;
                $additionalCreditLog->remarks = $request->remarks;
                array_push($log, $additionalCreditLog);
                $updateLog = new UpdateAdditionalCreditApplicationLogRequest();
                $updateLog->log = json_encode($log);
                $updateLog->applicationId = $applicationDetails->id;
                $updateLog->updatedBy = $request->updatedBy;
                $this->updateActivityPointApplicationLog($updateLog);
                $this->logger->info(Events::ADDITIONAL_CREDIT_APPLICATION_UPDATION, [
                    "userId" => $request->createdBy,
                    "userType" => UserType::STUDENT,
                    "request" => $request,
                    "status" => StatusConstants::SUCCESS
                ]);
            }
        } catch (\Exception $e) {
            AMSLogger::log_error($this->logger,Events::ADDITIONAL_CREDIT_APPLICATION_UPDATION, [
                "userId" => $request->createdBy,
                "userType" => UserType::STUDENT,
                "status" => StatusConstants::FAILED,
                "request" => $request,
                "error" => $e->getCode(),
                "message" => $e->getMessage()
            ]);
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Update Additional Credit Application
     * Step 2:
     *
     * Updating additional credit application credits by removing user removed credit items from the application
     * @param $removedItems
     * @param $applicationId
     * @throws ProfessionalException
     */
    private function updateAllRemovedAdditionalCreditItems($removedItems, $applicationId)
    {
        if (empty($applicationId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_ADDITIONAL_CREDIT_APPLICATION_ID, "Application doesn't exist");
        }
        try {
            $sql = "DELETE acac.*,acasd.* FROM additional_credit_application_credits acac,
                    INNER JOIN additional_credit_application_supporting_documents as acasd 
                        ON acasd.additional_credit_application_credit_id = acac.id
                    WHERE acac.application_id= $applicationId AND acac.id IN (" . implode(",", $removedItems) . ")";
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Update Additional Credit Application
     * STEP 3:
     * @param AdditionalCreditApplication $applicationDetails
     * @param UpdateAdditionalCreditApplicationCreditsRequest[] $credits
     * @param UpdateAdditionalCreditApplicationRequest $request
     * @throws ProfessionalException
     */
    private function updateAdditionalCreditApplicationCredits($applicationDetails, array $credits, UpdateAdditionalCreditApplicationRequest $request)
    {
        if (empty($applicationDetails->id)) {
            throw new ProfessionalException(ProfessionalException::INVALID_ADDITIONAL_CREDIT_APPLICATION_ID, "Invalid application details given");
        }
        if ($applicationDetails->status === StatusConstants::REJECTED) {
            $status = StatusConstants::RE_APPLIED;
        } elseif ($applicationDetails->status === StatusConstants::RE_APPLIED) {
            $status = StatusConstants::RE_APPLIED;
        } else {
            $status = StatusConstants::PENDING;
        }
        try {
            $removedSupportingDocuments = [];
            foreach ($credits as $credit) {
                $isCreditFound = 0;
                /*
                 * Finding user removed files from the previously added supporting documents, and then it will be removed from the database
                 */
                foreach ($applicationDetails->applicationCredits as $applicationCredit) {
                    if ($credit->id === $applicationCredit->id) {
                        $isCreditFound = 1;
                        foreach ($applicationCredit->additionalCreditSupportingDocuments as $supportingDocument) {
                            $isNotFound = 1;
                            foreach ($credit->previouslyUploadedFiles as $previouslyUploadedFile) {
                                if ($previouslyUploadedFile['resourceId'] === $supportingDocument->resourceId) {
                                    $isNotFound = 0;
                                    break;
                                }
                            }
                            if ($isNotFound) {
                                array_push($removedSupportingDocuments, $supportingDocument);
                            }
                        }
                    }
                }
                if ($isCreditFound) {
                    $sql = "UPDATE additional_credit_application_credits SET credit =$credit->credit,
                         status='$status', additional_credit_item_id =$credit->additionalCreditItemId,
                         updated_date =UTC_TIMESTAMP(),updated_by = $credit->updatedBy
                         WHERE id= $credit->id AND application_id = $applicationDetails->id";
                    $this->executeQuery($sql);
                    if (count($removedSupportingDocuments) > 0) {
                        /*
                         * Removing user removed support documents from the database
                         */
                        $this->removeAdditionalCreditSupportingDocuments($removedSupportingDocuments, $request);
                    }
                    /*
                     * Adding user newly added support documents to the database
                     */
                    $this->updateAdditionalCreditSupportingDocuments($credit->id, $credit->supportingDocuments);
                }
//                else{
//                    TODO: Use when multiple additional credit items implementing in the application form
//                }
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param $applicationCreditId
     * @param AddAdditionalCreditSupportDocuments[] $documents
     * @return void
     * @throws ProfessionalException
     */
    public function updateAdditionalCreditSupportingDocuments($applicationCreditId, array $documents)
    {
        if (empty($applicationCreditId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_ADDITIONAL_CREDIT_APPLICATION_CREDIT_ID, "Invalid application credit details given");
        }
        try {
            foreach ($documents as $document) {
                $sql = "INSERT INTO additional_credit_application_supporting_documents (additional_credit_application_credit_id, 
                        resource_id, updated_by, updated_date, created_by, created_date) 
                        VALUES ($applicationCreditId,'$document->resourceId',$document->updatedBy,UTC_TIMESTAMP(),$document->createdBy,UTC_TIMESTAMP())";
                $document->id = $this->executeQueryForObject($sql, true);
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Update Additional Credit Application
     * STEP 4:
     * Updating additional credit application credits
     * @param UpdateAdditionalCreditApplicationCreditsRequest $requests []
     * @throws ProfessionalException
     */
    public function editAdditionalCreditApplicationCredit(UpdateAdditionalCreditApplicationCreditsRequest $requests)
    {
        $status = StatusConstants::PENDING;
        try {
            foreach ($requests as $request) {
                $sql = "UPDATE additional_credit_application_credits 
                        SET status ='$status',updated_by = $request->updatedBy,updated_date = UTC_TIMESTAMP(),
                            credit=$request->credit
                        WHERE id = $request->id AND application_id =$request->applicationId";
                $this->executeQuery($sql);
                $this->deleteAllAdditionalCreditSupportingDocumentsByApplicationCreditId($request->id);
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param ReApplyAdditionalCreditApplicationRequest $request
     * @throws ProfessionalException
     */
    public function reApplyAdditionalCreditApplication(ReApplyAdditionalCreditApplicationRequest $request)
    {
        try {
            $applicationDetails = $this->getAdditionalCreditApplicationById($request->applicationId);
            if (empty($applicationDetails)) {
                throw new ProfessionalException(ProfessionalException::APPLICATION_NOT_FOUND, "Application doesn't exist");
            }
            $workflowInstance = new WorkflowInstance(WorkflowActions::GET, $applicationDetails->workflowInstanceId, $request->reAppliedBy, $request->userType);
            if (empty($workflowInstance)) {
                throw new ProfessionalException(ProfessionalException::INTERNAL_SERVICE_FAILURE, "Error occurred while re-applying application. Contact the system administrator");
            }
            /**
             * this will work only if workflow state is rejected. Otherwise workflow stated will not change
             */
            if (strtoupper($workflowInstance->getCurrentState()->name) === strtoupper(StatusConstants::REJECTED)) {
                $possibleNextStates = $workflowInstance->getAllPossibleNextStates();
                foreach ($possibleNextStates as $state) {
                    if (strtoupper($state->name) === StatusConstants::RE_APPLIED) {
                        $moveToNextStateApiRequest = new MoveToNextStateApiRequest();
                        $moveToNextStateApiRequest->approverId = $request->reAppliedBy;
                        $moveToNextStateApiRequest->approverType = $request->userType;
                        $moveToNextStateApiRequest->nextStateId = $state->id;
                        $workflowInstance->moveToNextState($moveToNextStateApiRequest);
                        break;
                    }
                }
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Updating activity point application log by application id,only json an audit field will be updated
     * @param Object $request
     * @throws ProfessionalException
     */
    public function updateActivityPointApplicationLog($request)
    {
        $request->applicationId = $this->realEscapeString($request->applicationId);
        if (empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INAVLID_ACTIVITY_POINT_APPLICATION, "Invalid additional credit application details given");
        }
        $request->log = addslashes($request->log);
        $sql = "UPDATE activity_point_application_log SET log='$request->log', 
                updated_by=$request->updatedBy
                WHERE application_id = $request->applicationId";
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Updating monthandyear value in achievement_monthandyear table
     * @param id
     * @param string
     * @param int
     * @throws ProfessionalException
     */
    public function updateAchieveMentmonthandyear($monthandyearId, $monthandyear, $updatedBy)
    {
        $monthandyearId = $this->realEscapeString($monthandyearId);
        $monthandyear = $this->realEscapeString($monthandyear);
        if (empty($monthandyearId) || empty($monthandyear)) {
            throw new ProfessionalException(ProfessionalException::INVALID_ACHIEVEMENT_DETAILS, "Invalid achievement application details given");
        }
        $sql = "UPDATE achievement_monthandyear SET monthandyear='$monthandyear', 
                updated_by=$updatedBy,updated_date = UTC_TIMESTAMP()
                WHERE id = $monthandyearId";
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Updating details value in achievements_details table
     * @param id
     * @param string
     * @param int
     * @throws ProfessionalException
     */
    public function updateAchievementDetail($detailId, $detail, $updatedBy)
    {
        $detailId = $this->realEscapeString($detailId);
        $monthandyear = $this->realEscapeString($detail);
        if (empty($detailId) || empty($detail)) {
            throw new ProfessionalException(ProfessionalException::INVALID_ACHIEVEMENT_DETAILS, "Invalid achievement application details given");
        }
        $sql = "UPDATE achievement_details SET detail='$detail', 
               updated_by=$updatedBy,updated_date = UTC_TIMESTAMP()
               WHERE id = $detailId";
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /***************************************************************************************************************
     *                                                                                                             *
     *                                              DELETE SECTIONS                                                *
     *                                                                                                             *
     ***************************************************************************************************************/
    /**
     * Update Additional Credit Application
     * STEP 5:
     * Deleting all additional credit supporting documents
     * @param integer $additionalCreditId
     * @throws ProfessionalException
     */
    public function deleteAllAdditionalCreditSupportingDocumentsByApplicationCreditId($additionalCreditId)
    {
        $supportingDocuments = $this->getAllSupportingDocumentIdsByApplicationId($additionalCreditId);
        /**
         * Removing supporting Documents from the additional credit application
         */
        $sql = "DELETE FROM additional_credit_application_supporting_documents
                WHERE additional_credit_application_credit_id=$additionalCreditId";
        try {
            $this->executeQuery($sql);
            /**
             * Deleting supporting documents from the lin_resources
             */
            foreach ($supportingDocuments as $document) {
                ResourceService::getInstance()->removeResource($document->id);
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Deleting supporting documents by ids
     * @param AdditionalCreditSupportingDocuments[] $documents
     * @param UpdateAdditionalCreditApplicationRequest $request
     * @throws ProfessionalException
     */
    private function removeAdditionalCreditSupportingDocuments(array $documents, UpdateAdditionalCreditApplicationRequest $request)
    {
        try {
            foreach ($documents as $document) {
                $sql = "DELETE FROM additional_credit_application_supporting_documents WHERE id =$document->id";
                $this->executeQuery($sql);
                /**
                 * Removing files from the backend storage
                 */
                $removeRequest = new RemoveResourceRequest();
                $removeRequest->resourceId = $document->resourceId;
                $removeRequest->secretKey = $request->secretKey;
                $removeRequest->accessKey = $request->accessKey;
                $removeRequest->userType = $request->userType;
                ResourceService::getInstance()->removeResource($removeRequest);
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Deleting additional credit applications by application id. This wil delete entire application details without
     * checking the application is approved or not
     * @param DeleteAdditionalCreditApplicationRequest $request
     * @throws ProfessionalException
     */
    public function deleteApplication(DeleteActivityPointApplicationRequest $request)
    {
        $request = $this->realEscapeObject($request);
        if (empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_ADDITIONAL_CREDIT_APPLICATION_ID, "Invalid additional credit application or application doesn't exist");
        }
        $sql = "DELETE FROM activity_point_application WHERE  id = $request->applicationId";
        try {
            $this->deleteActivityPointApplicationLog($request->applicationId);
            $this->deleteActivityPointSupportingDocumentsByApplicationId($request);
            $this->deleteActivityPointApplicationPointsByApplicationId($request->applicationId);
            $this->executeQuery($sql);
            $this->logger->info(Events::ACTIVITY_POINT_APPLICATION_DELETION, [
                "userId" => $request->updatedBy,
                "userType" => UserType::STUDENT,
                "request" => $request,
                "status" => StatusConstants::SUCCESS
            ]);
        } catch (\Exception $e) {
            AMSLogger::log_error($this->logger,Events::ACTIVITY_POINT_APPLICATION_DELETION, [
                "userId" => $request->updatedBy,
                "userType" => UserType::STUDENT,
                "status" => StatusConstants::FAILED,
                "request" => $request,
                "error" => $e->getCode(),
                "message" => $e->getMessage()
            ]);
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Deleting activity point application points by application id
     * @param $applicationId
     * @throws ProfessionalException
     */
    private function deleteActivityPointApplicationPointsByApplicationId($applicationId)
    {
        $sql = "DELETE FROM activity_point_application_points WHERE application_id = $applicationId";
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Deleting additional credit suporting documents by application id
     * @param DeleteAdditionalCreditApplicationRequest $request
     * @throws ProfessionalException
     */
    private function deleteActivityPointSupportingDocumentsByApplicationId(DeleteActivityPointApplicationRequest $request)
    {
        $request = $this->realEscapeObject($request);
        $applicationId = $request->applicationId;
        if (empty($applicationId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_ADDITIONAL_CREDIT_APPLICATION_ID, "Invalid additional credit application details given");
        }
        try {
            $applicationDocuments = $this->getAllSupportingDocumentIdsByApplicationId($applicationId);
            $sql = "DELETE  FROM  activity_point_application_supporting_documents 
                    WHERE activity_point_application_id  = $applicationId";
            $this->executeQuery($sql);
            foreach ($applicationDocuments as $document) {
                if ($document->id) {
                    $removeResourceRequest = new RemoveResourceRequest();
                    $removeResourceRequest->accessKey = $request->accessKey;
                    $removeResourceRequest->secretKey = $request->secretKey;
                    $removeResourceRequest->resourceId = $document->id;
                    ResourceService::getInstance()->removeResource($removeResourceRequest);
                }
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * Deleting additional credit application log
     * @param $applicationId
     * @throws ProfessionalException
     */
    private function deleteActivityPointApplicationLog($applicationId)
    {
        $sql = "DELETE FROM activity_point_application_log WHERE application_id=$applicationId";
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /***************************************************************************************************************
     *                                                                                                             *
     *                                 APPLICATION APPROVE/REJECT SECTIONS                                         *
     *                                                                                                             *
     ***************************************************************************************************************/
    /**
     * @param Object $request
     * @throws ProfessionalException
     */
    public function approveOrRejectApplication($request)
    {
        $request = $this->realEscapeObject($request);
        if (empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_ADDITIONAL_CREDIT_APPLICATION_ID, "Invalid application or application doesn't exist");
        }
        if (empty($request->status)) {
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Couldn't process application request");
        }
        try {
            $isTutor = StaffService::getInstance()->staffIsOrWasTutor($request->processedBy);
            if (!$isTutor) {
                throw new ProfessionalException(ProfessionalException::UNAUTHENTICATED_OPERATION_PERFORMED, "Un-authenticated operation performed");
            }
            $applicationDetails = null;
            /**
             * Fetching additional credit application details
             */
            $applicationDetails = $this->getActivityPointApplicationById($request->applicationId);
            if (empty($applicationDetails)) {
                throw new ProfessionalException(ProfessionalException::ADDITIONAL_CREDIT_APPLICATION_NOT_FOUND, "Application doesn't exist");
            }
            $user = $request->processedBy;
            $userType = $request->processedUserType;
            $workFlowObject = new WorkflowInstance(WorkflowActions::GET, $applicationDetails->workflowInstanceId, $user, $userType);
            $currentState = $workFlowObject->getCurrentState()->name;
            switch (strtoupper($currentState)) {
                /**
                 * If current state is approved state, then tutor can move this application to rejected state
                 */
                case StatusConstants::APPROVED:
                    if ($request->status === StatusConstants::REJECTED) {
                        $possibleNextStates = $workFlowObject->getAllPossibleNextStates();
                        foreach ($possibleNextStates as $state) {
                            if (strtoupper($state->name) === StatusConstants::REJECTED) {
                                $this->processActivityPointReject($applicationDetails, $state, $workFlowObject, $request);
                                break;
                            }
                        }
                    } else {
                        throw new ProfessionalException(ProfessionalException::APPLICATION_ALREADY_APPROVED, "Application already approved by " . $applicationDetails->processedStaffName);
                    }
                    break;
                /**
                 * If current state is rejected, then next state is approved state
                 */
                case StatusConstants::REJECTED:
                    /**
                     * Checking requested transition state is approved state,else throwing errors
                     */
                    if ($request->status === StatusConstants::APPROVED) {
                        $possibleNextStates = $workFlowObject->getAllPossibleNextStates();
                        foreach ($possibleNextStates as $state) {
                            if (strtoupper($state->name) === StatusConstants::APPROVED) {
                                $this->processActivityPointApproval($applicationDetails, $state, $workFlowObject, $request);
                                break;
                            }
                        }
                    } else {
                        throw new ProfessionalException(ProfessionalException::APPLICATION_ALREADY_REJECTED, "Application already rejected by " . $applicationDetails->processedStaffName);
                    }
                    break;
                /**
                 * If current state is applied or re-applied state, then tutor can moe this
                 * application to rejected/approved state only
                 */
                case StatusConstants::APPLIED:
                case StatusConstants::RE_APPLIED:
                    if ($request->status === StatusConstants::REJECTED) {
                        $possibleNextStates = $workFlowObject->getAllPossibleNextStates();
                        foreach ($possibleNextStates as $state) {
                            if (strtoupper($state->name) === StatusConstants::REJECTED) {
                                $this->processActivityPointReject($applicationDetails, $state, $workFlowObject, $request);
                                break;
                            }
                        }
                    } else if ($request->status === StatusConstants::APPROVED) {
                        $possibleNextStates = $workFlowObject->getAllPossibleNextStates();
                        foreach ($possibleNextStates as $state) {
                            if (strtoupper($state->name) === StatusConstants::APPROVED) {
                                $this->processActivityPointApproval($applicationDetails, $state, $workFlowObject, $request);
                                break;
                            }
                        }
                    }
                    break;
                default:
                    throw new ProfessionalException(ProfessionalException::INVALID_OPERATION, "You performed an invalid operation. You cannot do this action try again with valid credentials");
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param $applicationDetails
     * @param $state
     * @param $workFlowObject
     * @param ApproveOrRejectAdditionalCreditsApplicationRequest $request
     * @throws ProfessionalException
     */
    private function processActivityPointReject($applicationDetails, $state, $workFlowObject, $request)
    {
        try {
            $moveToNextStateApiRequest = new MoveToNextStateApiRequest();
            $moveToNextStateApiRequest->approverId = $request->processedBy;
            $moveToNextStateApiRequest->approverType = $request->processedUserType;
            $moveToNextStateApiRequest->nextStateId = $state->id;
            $workFlowObject->moveToNextState($moveToNextStateApiRequest);
            $userType = $request->processedUserType;
            $approveAllCreditRequest = new stdClass();
            $approveAllCreditRequest->applicationId = $request->applicationId;
            $approveAllCreditRequest->processedBy = $request->processedBy;
            $approveAllCreditRequest->remarks = $request->remarks;
            $approveAllCreditRequest->updatedBy = $request->updatedBy;
            $this->rejectAllPointsInsideAnActivityPointApplication($approveAllCreditRequest);
            $approveApplication = new stdClass();
            $approveApplication->applicationId = $request->applicationId;
            $approveApplication->remarks = $request->remarks;
            $approveApplication->processedBy = $request->processedBy;
            $approveApplication->updatedBy = $request->updatedBy;
            $this->rejectActivityPointApplication($approveApplication);
            
            $applicationTypeCode = $this->getApplicationCode($request->applicationId);
            /**
             * Adding the activity point application log
             */
            $log = json_decode($applicationDetails->log);
            $additionalCreditLog = new ActivityPointLog();
            $additionalCreditLog->status = StatusConstants::REJECTED;
            $additionalCreditLog->updatedBy = $request->updatedBy;
            $additionalCreditLog->createdBy = $request->createdBy;
            $additionalCreditLog->processedByStaffName = StaffService::getInstance()->getStaffNameById($request->processedBy);
            $additionalCreditLog->actionTakenTime = date("Y-m-d h:i A");
            $additionalCreditLog->updatedByUserType = $userType;
            if ($applicationTypeCode == ActivityPoint::ACTIVITY_POINT) {
                $additionalCreditLog->message = ActivityPoint::ACTIVITY_POINT_APPLICATION_REJECTED;
            } else {
                $additionalCreditLog->message = ActivityPoint::ACTIVITY_POINT_APPLICATION_REJECTED;
            }
            $additionalCreditLog->appliedBy = $request->processedBy;
            $additionalCreditLog->remarks = $request->remarks;
            array_push($log, $additionalCreditLog);
            $updateLog = new stdClass();
            $updateLog->log = json_encode($log);
            $updateLog->applicationId = $request->applicationId;
            $updateLog->updatedBy = $request->processedBy;
            $this->updateActivityPointApplicationLog($updateLog);
            $this->logger->info(Events::ACTIVITY_POINT_APPLICATION_REJECTION, [
                "userId" => $request->updatedBy,
                "userType" => UserType::STAFF,
                "request" => $request,
                "status" => StatusConstants::SUCCESS
            ]);
        } catch (\Exception $e) {
            AMSLogger::log_error($this->logger,Events::ACTIVITY_POINT_APPLICATION_REJECTION, [
                "userId" => $request->updatedBy,
                "userType" => UserType::STUDENT,
                "status" => StatusConstants::FAILED,
                "request" => $request,
                "error" => $e->getCode(),
                "message" => $e->getMessage()
            ]);
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param $applicationDetails
     * @param $state
     * @param $workFlowObject
     * @param ApproveOrRejectAdditionalCreditsApplicationRequest $request
     * @throws ProfessionalException
     */
    private function processActivityPointApproval($applicationDetails, $state, $workFlowObject, $request)
    {
        $userType = $request->processedUserType;
        try {
            $moveToNextStateApiRequest = new MoveToNextStateApiRequest();
            $moveToNextStateApiRequest->approverId = $request->processedBy;
            $moveToNextStateApiRequest->approverType = $request->processedUserType;
            $moveToNextStateApiRequest->nextStateId = $state->id;
            $workFlowObject->moveToNextState($moveToNextStateApiRequest);
            
            $approveAllCreditRequest = new stdClass();
            $approveAllCreditRequest->applicationId = $request->applicationId;
            $approveAllCreditRequest->processedBy = $request->processedBy;
            $approveAllCreditRequest->remarks = $request->remarks;
            $approveAllCreditRequest->updatedBy = $request->updatedBy;
            $this->approveAllPointsInsideAnActivityPointApplication($approveAllCreditRequest);
            $approveApplication = new stdClass();
            $approveApplication->applicationId = $request->applicationId;
            $approveApplication->remarks = $request->remarks;
            $approveApplication->processedBy = $request->processedBy;
            $approveApplication->updatedBy = $request->updatedBy;
            $this->approveActivityPointApplication($approveApplication);
            
            $applicationTypeCode = $this->getApplicationCode($request->applicationId);
            /**
             * Adding the additional point application log
             */
            $log = json_decode($applicationDetails->log);
            $additionalCreditLog = new ActivityPointLog();
            $additionalCreditLog->status = StatusConstants::APPROVED;
            $additionalCreditLog->updatedBy = $request->updatedBy;
            $additionalCreditLog->createdBy = $request->createdBy;
            $additionalCreditLog->processedByStaffName = StaffService::getInstance()->getStaffNameById($request->processedBy);
            $additionalCreditLog->processedByStaffName = $request->updatedBy;
            $additionalCreditLog->actionTakenTime = date("Y-m-d h:i A");
            $additionalCreditLog->updatedByUserType = $userType;
            if ($applicationTypeCode == ActivityPoint::ACTIVITY_POINT) {
                $additionalCreditLog->message = ActivityPoint::ACTIVITY_POINT_APPLICATION_APPROVED;
            } else {
                $additionalCreditLog->message = ActivityPoint::ACTIVITY_POINT_APPLICATION_APPROVED;
            }
            $additionalCreditLog->appliedBy = $request->processedBy;
            $additionalCreditLog->remarks = $request->remarks;
            array_push($log, $additionalCreditLog);
            $updateLog = new stdClass();
            $updateLog->log = json_encode($log);
            $updateLog->applicationId = $request->applicationId;
            $updateLog->updatedBy = $request->processedBy;
            $this->updateActivityPointApplicationLog($updateLog);
            $this->logger->info(Events::ACTIVITY_POINT_APPLICATION_APPROVAL, [
                "userId" => new Staff($request->updatedBy),
                "userType" => UserType::STAFF,
                "request" => $request,
                "status" => StatusConstants::SUCCESS
            ]);
        } catch (\Exception $e) {
            AMSLogger::log_error($this->logger,Events::ACTIVITY_POINT_APPLICATION_APPROVAL, [
                "userId" => new Staff($request->updatedBy),
                "userType" => UserType::STUDENT,
                "status" => StatusConstants::FAILED,
                "request" => $request,
                "error" => $e->getCode(),
                "message" => $e->getMessage()
            ]);
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param Object $request
     * @throws ProfessionalException
     */
    public function approveActivityPointApplication($request)
    {
        $request = $this->realEscapeObject($request);
        if (empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INAVLID_ACTIVITY_POINT_APPLICATION, "Invalid application or application doesn't exist");
        }
        $status = StatusConstants::APPROVED;
        $sql = "UPDATE activity_point_application 
                SET status= '$status',remarks='$request->remarks', updated_by =$request->updatedBy,
                    processed_by=$request->processedBy WHERE id =$request->applicationId";
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param Object $request
     * @throws ProfessionalException
     */
    public function rejectActivityPointApplication($request)
    {
        $request = $this->realEscapeObject($request);
        if (empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INAVLID_ACTIVITY_POINT_APPLICATION, "Invalid application or application doesn't exist");
        }
        $status = StatusConstants::REJECTED;
        $sql = "UPDATE activity_point_application 
                SET status= '$status',remarks='$request->remarks', updated_by =$request->updatedBy,
                processed_by=$request->processedBy WHERE id =$request->applicationId";
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param Object $request
     * @throws ProfessionalException
     */
    public function approveAllPointsInsideAnActivityPointApplication( $request)
    {
        $request = $this->realEscapeObject($request);
        if (empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INAVLID_ACTIVITY_POINT_APPLICATION, "Invalid application or application doesn't exist");
        }
        $status = StatusConstants::APPROVED;
        if (empty($request->processedBy) || empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Invalid approval request sent");
        }
        $sql = "UPDATE activity_point_application_points 
                SET status ='$status',remarks ='$request->remarks', updated_by = $request->updatedBy,
                processed_by = $request->processedBy
                WHERE application_id  =$request->applicationId";
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param RejectAllAdditionalCreditsRequest $request
     * @throws ProfessionalException
     */
    public function rejectAllPointsInsideAnActivityPointApplication($request)
    {
        $request = $this->realEscapeObject($request);
        if (empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INAVLID_ACTIVITY_POINT_APPLICATION, "Invalid application or application doesn't exist");
        }
        $status = StatusConstants::REJECTED;
        if (empty($request->processedBy) || empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Invalid approval request sent");
        }
        $sql = "UPDATE activity_point_application_points 
                SET status ='$status',remarks ='$request->remarks',updated_by = $request->updatedBy,
                processed_by = $request->processedBy
                WHERE application_id  = $request->applicationId";
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /***************************************************************************************************************
     *                                                                                                             *
     *                                SEMESTER WISE ADDITIONAL CREDIT SECTIONS                                     *
     *                                                                                                             *
     ***************************************************************************************************************/
    /**
     * @param SearchAdditionalCreditRequest $request
     * @return Object|semwiseAdditionalCredits[]
     * @throws ProfessionalException
     */
    public function getStudentSemesterwiseTotalAdditionalCredit(SearchAdditionalCreditRequest $request)
    {
        $request = $this->realEscapeObject($request);
        $sqlConditions = "";
        if (empty($request->studentId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Invalid student or student doesn't exist");
        }
        if (!empty($request->semId)) {
            $sqlConditions .= " AND sac.semester_id = '$request->semId'";
        }
        $sql = "SELECT
            sac.semester_id AS semId,
            s.semName,
            sac.student_id AS studentId,
            sac.additional_credit AS credit,
            s.orderNo
        FROM
            semester_additional_credit sac
        INNER JOIN semesters s ON
            s.semID = sac.semester_id
        WHERE
            sac.student_id = '$request->studentId'
            $sqlConditions
        ORDER BY s.orderNo";
        try {
            return $this->executeQueryForList($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param $request
     * @throws ProfessionalException
     */
    public function updateStudentSemesterwiseTotalAdditionalCredit($request)
    {
        $request = $this->realEscapeObject($request);
        $sql = "";
        $sqlUpdate = "";
        if (empty($request->studentId) || empty($request->semId) || empty($request->staffId) || empty($request->status) || empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Invalid request sent");
        }
        try {
            $applicationIdArrayJson = '["' . $request->applicationId . '"]';
            $sql = "SELECT
                id,
                additional_credit AS additionalCredit
            FROM
                semester_additional_credit
            WHERE
                student_id = '$request->studentId'
                AND semester_id = '$request->semId'";
            $additionalCreditDetails = $this->executeQueryForObject($sql);
            if ($additionalCreditDetails->id) {
                if ($request->status == StatusConstants::APPROVED) {
                    $sqlUpdate = "UPDATE
                        semester_additional_credit sac
                    INNER JOIN additional_credit_application aca ON
                        aca.semester_id = sac.semester_id
                        AND aca.student_id = sac.student_id
                    INNER JOIN additional_credit_application_credits acac ON
                        acac.application_id = aca.id
                    SET sac.additional_credit =    sac.additional_credit+acac.credit,
                        sac.application_ids = JSON_ARRAY_APPEND(sac.application_ids,'$','$request->applicationId'),
                        sac.updated_by = '$request->staffId'
                    WHERE
                        aca.student_id = '$request->studentId'
                        AND aca.semester_id = '$request->semId'
                        AND acac.status = '$request->status'
                        AND acac.application_id = '$request->applicationId'
                        AND NOT JSON_CONTAINS(application_ids,'$applicationIdArrayJson')";
                } else if ($request->status == StatusConstants::REJECTED) {
                    $sqlUpdate = "UPDATE
                        semester_additional_credit sac
                    INNER JOIN additional_credit_application aca ON
                        aca.semester_id = sac.semester_id
                        AND aca.student_id = sac.student_id
                    INNER JOIN additional_credit_application_credits acac ON
                        acac.application_id = aca.id
                    SET sac.additional_credit =    sac.additional_credit-acac.credit,
                        sac.application_ids = JSON_REMOVE(application_ids,JSON_UNQUOTE(JSON_SEARCH(application_ids,'one','$request->applicationId'))),
                        sac.updated_by = '$request->staffId'
                    WHERE
                        aca.student_id = '$request->studentId'
                        AND aca.semester_id = '$request->semId'
                        AND acac.status = '$request->status'
                        AND acac.application_id = '$request->applicationId'
                        AND JSON_CONTAINS(application_ids,'$applicationIdArrayJson')";
                }
                $this->executeQuery($sqlUpdate);
                $additionalCreditDetailsAfterUpdation = $this->executeQueryForObject($sql);
                if ($additionalCreditDetailsAfterUpdation->additionalCredit <= 0) {
                    $request->semAdditionalCreditDetailsId = $additionalCreditDetailsAfterUpdation->id;
                    $this->removeStudentSemesterwiseAdditionalCredit($request);
                }
            } else if ($request->status == StatusConstants::APPROVED) {
                $this->addStudentSemesterwiseAdditionalCredit($request);
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param $request
     * @throws ProfessionalException
     */
    public function addStudentSemesterwiseAdditionalCredit($request)
    {
        $request = $this->realEscapeObject($request);
        $approvedStatusConstants = StatusConstants::APPROVED;
        if (empty($request->studentId) || empty($request->semId) || empty($request->staffId) || empty($request->applicationId)) {
            throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Invalid request sent");
        }
        $applicationIdArrayJson = '["' . $request->applicationId . '"]';
        $sql = "INSERT INTO semester_additional_credit(
            semester_id,
            student_id,
            additional_credit,
            application_ids,
            created_by
        )
        SELECT
            $request->semId,
            $request->studentId,
            acac.credit,
            '$applicationIdArrayJson',
            $request->staffId
        FROM
            additional_credit_application_credits acac 
        WHERE
            acac.status = '$approvedStatusConstants'
            AND acac.application_id = '$request->applicationId'";
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    /**
     * @param $request
     * @throws ProfessionalException
     */
    public function removeStudentSemesterwiseAdditionalCredit($request)
    {
        $request = $this->realEscapeObject($request);
        if (empty($request->semAdditionalCreditDetailsId)) {
            if (empty($request->studentId) || empty($request->semId)) {
                throw new ProfessionalException(ProfessionalException::INVALID_REQUEST, "Invalid request sent");
            } else {
                $sql = "DELETE FROM semester_additional_credit WHERE semester_id = '$request->semId' AND student_id = '$request->studentId'";
            }
        } else {
            $sql = "DELETE FROM semester_additional_credit WHERE id = '$request->semAdditionalCreditDetailsId'";
        }
        try {
            $this->executeQuery($sql);
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    public function createWorkFlowStates($workflowId){
        try {
            $sql = "SELECT 
                        name
                    FROM
                        workflow_state
                    WHERE
                        workflow_id = $workflowId
                    ";
            $result = $this->executeQueryForList($sql);
            if(empty($result)){
                $sql = "INSERT INTO `workflow_state` (`name`, `is_initial`, `is_final`, `workflow_id`, `is_active`, `created_by`, `created_date`, `updated_by`, `updated_date`)
                      VALUES ('Applied', '1', '0', '$workflowId', '1', '1', now(), '1', now())";
                $this->executeQuery($sql);
                $sql = "INSERT INTO `workflow_state` (`name`, `is_initial`, `is_final`, `workflow_id`, `is_active`, `created_by`, `created_date`, `updated_by`, `updated_date`)
                      VALUES ('Approved', '0', '1', '$workflowId', '1', '1', now(), '1', now())";
            
                $this->executeQuery($sql);
                $sql = "INSERT INTO `workflow_state` (`name`, `is_initial`, `is_final`, `workflow_id`, `is_active`, `created_by`, `created_date`, `updated_by`, `updated_date`)
                      VALUES ('Rejected', '0', '0', '$workflowId', '1', '1', now(), '1', now())";
                $this->executeQuery($sql);
                $sql = "INSERT INTO `workflow_state` (`name`, `is_initial`, `is_final`, `workflow_id`, `is_active`, `created_by`, `created_date`, `updated_by`, `updated_date`)
                      VALUES ('Re-Applied', '0', '0', '$workflowId', '1', '1', now(), '1', now())
                    ";
            
                $this->executeQuery($sql);
            }
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
    public function createWorkFlowType($workflow){
        try{
            $sql = "SELECT 
                        id
                    FROM
                        workflow_type
                    WHERE
                        code = '$workflow->code'
                    ";
            $workFlowTypeId = $this->executeQueryForObject($sql);
            $workFlowTypeId = $workFlowTypeId->id;
            if(empty($workFlowTypeId)){
                $sql = "INSERT INTO `workflow_type` (`name`, `code`, `is_active`, `created_by`, `created_date`, `updated_by`, `updated_date`) 
                    VALUES ('$workflow->name', '$workflow->code', '1', '1', now(), '1', now())";
                
                $workFlowTypeId = $this->executeQueryForObject($sql, true);
                
            }
            return $workFlowTypeId;
        }catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(), $e->getMessage());
        }
    }
}