<?php

/**
 * SP Engage API
 * @author Celenia Software
 */

use Sugarcrm\Sugarcrm\Security\HttpClient\ExternalResourceClient;
use Sugarcrm\Sugarcrm\Security\HttpClient\RequestException;

require_once('modules/SP_GeneralSettings/SP_GeneralSettings.php');
require_once('modules/SP_TokenStorage/SP_TokenStorage.php');
require_once('include/SugarQuery/SugarQuery.php');
require_once('include/SugarDateTime.php');
require_once('include/api/SugarApi.php');
require_once('SPRecord.php');

/**
 * SP Engage API.
 */
class SP_EngageApi extends SugarApi
{
    /**
     * Register the REST API
     * @return array
     */
    public function registerApiRest()
    {
        return array(

            /**
             * Declaration of the SP API method - getAccessToken.
             */
            "getAccessToken"        => array(
                'reqType'   => 'GET',
                'path'      => array('sp_api', 'getaccesstoken'),
                'pathVars'  => array('', ''),
                'method'    => 'getAccessToken',
                'shortHelp' => 'Get the access token',
                'longHelp'  => 'Get the access token from the token storage'
            ),

            /**
             * Declaration of the SP API method - getMailTemplateList
             */
            "getMailTemplateList"   => array(
                'reqType'   => 'GET',
                'path'      => array('sp_api', 'getmailtemplatelist'),
                'pathVars'  => array('', ''),
                'method'    => 'getMailTemplateList',
                'shortHelp' => 'Get the mail template list',
                'longHelp'  => 'Get the mail template list'
            ),

            /**
             * Declaration of the SP API method - getMailTemplateList
             */
            "getMetadataList"       => array(
                'reqType'   => 'GET',
                'path'      => array('sp_api', 'getmetadatalist'),
                'pathVars'  => array('', ''),
                'method'    => 'getMetadataList',
                'shortHelp' => 'Get the metadata list',
                'longHelp'  => 'Get the metadata list'
            ),

            /**
             * Declaration of the SP API method - getMailTemplateList
             */
            "getMailPreview"        => array(
                'reqType'   => 'GET',
                'path'      => array('sp_api', 'getmailpreview'),
                'pathVars'  => array('', ''),
                'method'    => 'getMailPreview',
                'shortHelp' => 'Get the mail preview',
                'longHelp'  => 'Get the mail preview'
            ),

            "scheduleMail"          => array(
                'reqType'   => 'GET',
                'path'      => array('sp_api', 'schedulemail'),
                'pathVars'  => array('', ''),
                'method'    => 'scheduleMail',
                'shortHelp' => 'Schedule mailing',
                'longHelp'  => 'Schedule mailing'
            ),

            "addRecipientsToScheduledMail"  => array(
                'reqType'   => 'GET',
                'path'      => array('sp_api', 'addrecipientstoscheduledmail'),
                'pathVars'  => array('', ''),
                'method'    => 'addRecipientsToScheduledMail',
                'shortHelp' => 'Add recipients to scheduled mailing',
                'longHelp'  => 'Add recipients to scheduled mailing'
            ),

            "commitMail"            => array(
                'reqType'   => 'GET',
                'path'      => array('sp_api', 'commitmail'),
                'pathVars'  => array('', ''),
                'method'    => 'commitMail',
                'shortHelp' => 'Commit mailing',
                'longHelp'  => 'Commit mailing'
            ),

            "addMailing"            => array(
                'reqType'   => 'GET',
                'path'      => array('sp_api', 'addmailing'),
                'pathVars'  => array('', ''),
                'method'    => 'addMailing',
                'shortHelp' => 'Add mailing',
                'longHelp'  => 'Add mailing'
            ),

            /**
             * Declaration of the SP API method - getGeneralSettings.
             */
            "getGeneralSettings"    => array(
                'reqType'   => 'GET',
                'path'      => array('sp_api', 'getgeneralsettings'),
                'pathVars'  => array('', ''),
                'method'    => 'getGeneralSettings',
                'shortHelp' => 'Get the general settings',
                'longHelp'  => 'Get the general settings'
            ),
            
            "loadCampaignRecordList"    => array(
                'reqType'   => 'GET',
                'path'      => array('sp_api', 'loadcampaignrecordlist'),
                'pathVars'  => array('', ''),
                'method'    => 'loadCampaignRecordList',
                'shortHelp' => 'Load recs from a campaign',
                'longHelp'  => 'Load recs from a campaign'
            ),
            
            "createSPCampaign"          => array(
                'reqType'   => 'POST',
                'path'      => array('sp_api', 'createspcampaign'),
                'pathVars'  => array('', ''),
                'method'    => 'createSPCampaign',
                'shortHelp' => 'Create SP Campaign',
                'longHelp'  => 'Create SP Campaign'
            ),
            
            "createSPCampaignMember"    => array(
                'reqType'   => 'POST',
                'path'      => array('sp_api', 'createspcampaignmember'),
                'pathVars'  => array('', ''),
                'method'    => 'createSPCampaignMember',
                'shortHelp' => 'Create SP Campaign Member',
                'longHelp'  => 'Create SP Campaign Member'
            ),

            "getTotalRecordsOfCampaigns"    => array(
                'reqType'   => 'GET',
                'path'      => array('sp_api', 'gettotalrecordsofcampaigns'),
                'pathVars'  => array('', ''),
                'method'    => 'getTotalRecordsOfCampaigns',
                'shortHelp' => 'Get total records of campaign',
                'longHelp'  => 'Get total records of campaign'
            )
        );
    }

    /**
     * Get the SP Access Token.
     * @param SugarApi $api
     * @param array $args
     * @return string
     */
    public function getAccessToken($api, $args)
    {
        /*
        $accessToken = SP_TokenStorage::getAccessToken();
        if (!is_null($accessToken) && !isset($args['update']))
        {
            return json_encode(array( 'access_token' => $accessToken ));
        }
        */

        $accessToken = SP_EngageApi::refreshAccessToken();
        return json_encode(array( 'access_token' => $accessToken ));
    }

    /**
     *
     * @param SugarApi $api
     * @param array $args
     * @return string
     */
    public function getMailTemplateList($api, $args)
    {
        $data  = "<Envelope><Body>";
        $data .= "<GetMailingTemplates>";
        $data .= "<VISIBILITY>1</VISIBILITY>";
        $data .= "<IS_CRM_ENABLED>true</IS_CRM_ENABLED>";
        $data .= "</GetMailingTemplates>";
        $data .= "</Body></Envelope>";

        return SP_EngageApi::createResponse($data);
    }

    /**
     *
     * @param string $api
     * @param SugarApi $args
     * @return string
     */
    public function getMetadataList($api, $args)
    {
        $generalSettings = new SP_GeneralSettings();
        $generalSettings->retrieve();

        $data  = "<Envelope><Body>";
        $data .= "<GetListMetaData>";
        $data .= "<LIST_ID>" . $generalSettings->sp_listid . "</LIST_ID>";
        $data .= "</GetListMetaData>";
        $data .= "</Body></Envelope>";

        return SP_EngageApi::createResponse($data);
    }

    /**
     *
     * @param SugarApi $api
     * @param array $args
     * @return string
     */
    public function getMailPreview($api, $args)
    {
        if (!isset($args['template_id'])) {
            return json_encode(array( 'success' => false, 'message' => "No required parameters!", 'content' => "" ));
        }

        $data  = "<Envelope><Body>";
        $data .= "<PreviewMailing>";
        $data .= "<MailingId>" . $args['template_id'] . "</MailingId>";
        $data .= "</PreviewMailing>";
        $data .= "</Body></Envelope>";

        return SP_EngageApi::createResponse($data);
    }

    /**
     *
     * @param SugarApi $api
     * @param array $args
     * @return string
     */
    public function scheduleMail($api, $args)
    {
        $generalSettings = new SP_GeneralSettings();
        $generalSettings->retrieve();

        $data  = "<Envelope><Body>";
        $data .= "<ScheduleMailing>";
        $data .= "<TEMPLATE_ID>" . $args['template_id'] . "</TEMPLATE_ID>";
        $data .= "<LIST_ID>" . $generalSettings->sp_listid . "</LIST_ID>";
        $data .= "<MAILING_NAME>" . $args['mail_name'] . "</MAILING_NAME>";
        $data .= "<RECIPIENTS></RECIPIENTS>";
        $data .= "<MORE_RECIPIENTS>true</MORE_RECIPIENTS>";
        $data .= "<SEND_HTML /><SEND_AOL /><SEND_TEXT />";
        $data .= "<SUBJECT>" . "<![CDATA[" . $args['mail_subj'] . "]]>" . "</SUBJECT>";
        $data .= "<FROM_NAME>" . "<![CDATA[" . $args['from_name'] . "]]>" . "</FROM_NAME>";
        $data .= "<FROM_ADDRESS>" . $args['from_addr'] . "</FROM_ADDRESS>";
        $data .= "<REPLY_TO>" . $args['reply_to'] . "</REPLY_TO>";
        $data .= "<VISIBILITY>1</VISIBILITY>";
        $data .= "<SCHEDULED>" . $args['mail_schedule'] . "</SCHEDULED>";
        $data .= "<SCHEDULED_IN_GMT>true</SCHEDULED_IN_GMT>";

        if (isset($args['personalization']))
        {
            $data .= "<SUBSTITUTIONS><SUBSTITUTION><NAME>SP_CRM_BLOCK</NAME><VALUE>";
            $data .= "<![CDATA[" . $args['personalization'] . "]]></VALUE></SUBSTITUTION></SUBSTITUTIONS>";
        }

        $data .= "</ScheduleMailing>";
        $data .= "</Body></Envelope>";

        return SP_EngageApi::createResponse($data);
    }

    public function addRecipientsToScheduledMail($api, $args)
    {
        $data  = "<Envelope><Body>";
        $data .= "<AddRecipientsToScheduledMailing>";
        $data .= "<CONTINUATION_TOKEN>" . $args['continuation_token'] . "</CONTINUATION_TOKEN>";
        $data .= "<RECIPIENTS>";

        $recipients = $args['recipients'];
        foreach ($recipients as $recipient)
        {
            $record = json_decode($recipient);
            $data .= "<RECIPIENT crm_sync_id=\"" . $record->sync_id . "\" recipient_id=\"" . $record->engage_id . "\" />";
        }

        $data .= "</RECIPIENTS>";
        $data .= "</AddRecipientsToScheduledMailing>";
        $data .= "</Body></Envelope>";

        return SP_EngageApi::createResponse($data);
    }

    /**
     *
     * @param SugarApi $api
     * @param array $args
     * @return string
     */
    public function commitMail($api, $args)
    {
        $data  = "<Envelope><Body>";
        $data .= "<CommitScheduledMailing>";
        $data .= "<CONTINUATION_TOKEN>" . $args['continuation_token'] . "</CONTINUATION_TOKEN>";
        $data .= "</CommitScheduledMailing>";
        $data .= "</Body></Envelope>";

        return SP_EngageApi::createResponse($data);
    }

    /**
     *
     * @param SugarApi $api
     * @param array $args
     * @return string
     */
    public function addMailing($api, $args)
    {
        $mailing = BeanFactory::newBean('SP_engagemailing');

        $mailing->sp_engageid           = $args['mailing_id'];
        $mailing->name                  = $args['mailing_name'];
        $mailing->sp_subject            = $args['mailing_subject'];
        $mailing->sp_sentdate           = $args['mailing_sentdate'];
        $mailing->sp_recipients         = $args['mailing_recipients'];
        $mailing->sp_recipientsfailed   = $args['mailing_recipients_failed'];
        $mailing->sp_fromname           = $args['mailing_fromname'];
        $mailing->sp_fromaddress        = $args['mailing_fromaddress'];
        $mailing->sp_replytoaddress     = $args['mailing_replytoaddress'];

        $mailing->save();

        return json_encode(array( 'status' => 'OK' ));
    }

    /**
     * Get the SP General Settings.
     * @param SugarApi $api
     * @param array $args
     * @return string
     */
    public function getGeneralSettings($api, $args)
    {
        $spGeneralSettings = new SP_GeneralSettings();
        $spGeneralSettings->retrieve();
        return $spGeneralSettings->toJson();;
    }

    /**
     * @param SugarApi $api
     * @param array $args
     * @return string
     */
    public function loadCampaignRecordList($api, $args)
    {
        $campaignId = $args['campaign_id'];
        $params     = array( 'disable_row_level_security' => true );
        $bean       = BeanFactory::retrieveBean("Campaigns", $campaignId, $params);

        if (is_null($bean))
        {
            return json_encode(array( 'success' => false, 'message' => "Cannot load campaign!", 'content' => "" ));
        }

        $bean->load_relationship('prospectlists');
        if (!isset($bean->prospectlists) || is_null($bean->prospectlists))
        {
            return json_encode(array( 'success' => false, 'message' => "Cannot load campaign's target list(-s)!", 'content' => "" ));
        }

        $result         = array();
        $prospectLists  = $bean->prospectlists->getBeans($params);

        foreach ($prospectLists as $prospectList)
        {
            $prospectList->load_relationship('contacts');
            $prospectList->load_relationship('leads');

            $contacts   = $prospectList->contacts->getBeans($params);
            $leads      = $prospectList->leads->getBeans($params);

            foreach ($contacts as $record)
            {
                if ($record->sp_synctosilverpop == 1)
                {
                    array_push($result, new SPRecord($record->id, $record->sp_engageid, "contact"));
                }
            }

            foreach ($leads as $record)
            {
                if ($record->sp_synctosilverpop == 1)
                {
                    array_push($result, new SPRecord($record->id, $record->sp_engageid, "lead"));
                }
            }
        }

        return json_encode(array( 'success' => true, 'message' => "", 'content' => $result));
    }
    
    /**
     * 
     * @param SugarApi $api
     * @param array $args
     */
    public function createSPCampaign($api, $args)
    {
        if (!isset($args['id']))
        {
            $GLOBALS['log']->fatal("SP: No required parameter (id)");
            
            return [
                "success" => false,
                "message" => "No required parameter (id)",
                "content" => null
            ];
        }
        
        $id = $args['id'];
        
        // Retrieve SP general settings (required List ID)
        $spGeneralSettings = new SP_GeneralSettings();
        $spGeneralSettings->retrieve();
        
        // Retrieve Campaign
        $campaign = BeanFactory::retrieveBean("Campaigns", $id);
        if ($campaign->load_relationship("sp_campaign_campaigns"))
        {
            // Retrieve related silverpop campaigns
            $spCampaignList = $campaign->sp_campaign_campaigns->getBeans();
            foreach ($spCampaignList as $spCampaign)
            {
                global $db;

                $query = "SELECT `sp_campaign_sp_campaignmembersp_campaignmember_idb` FROM `sp_campaign_sp_campaignmember_c` WHERE " .
                         "`deleted` = 0 AND `sp_campaign_sp_campaignmembersp_campaign_ida` = '" . $spCampaign->id . "'";
                $result = $db->query($query);

                while ($row = $result->fetch_row())
                {
                    BeanFactory::deleteBean("SP_CampaignMember", $row[0]);
                }
                
                // Delete silverpop campaign
                BeanFactory::deleteBean("SP_Campaign", $spCampaign->id);
            }
            
            $spCampaign = BeanFactory::newBean("SP_Campaign");
            $spCampaign->name               = "Acoustic Campaign Campaign: " . $campaign->name;
            $spCampaign->campaignname       = $campaign->name;
            $spCampaign->fullcampaignname   = $campaign->name . "_" . $campaign->id;
            $spCampaign->listid             = $spGeneralSettings->sp_listid;
            $spCampaign->campaignid         = $campaign->id;
            $spCampaign->save();
            
            $campaign->sp_campaign_campaigns->add($spCampaign->id);
            $campaign->save();
            
            return [
                'success' => true,
                'message' => '',
                'content' => [
                    'id'  => $spCampaign->id
                ]
            ];
        }
        
        return [
            'success' => false,
            'message' => 'Cannot load relationship',
            'content' => null
        ];
    }

    /**
     *
     * @param SugarApi $api
     * @param array $args
     * @return array
     */
    public function createSPCampaignMember($api, $args)
    {
        if (!isset($args['id']))
        {
            $GLOBALS['log']->fatal("SP: No required parameter (id)");
            
            return [
                "success" => false,
                "message" => "No required parameter (id)",
                "content" => null
            ];
        }
        
        // ToDo: Check
        
        $id = $args['id'];
        $collection = $args['collection'];
        $type = $args["type"];
        
        $spCampaign = BeanFactory::retrieveBean("SP_Campaign", $id);
        $spCampaign->load_relationship("sp_campaign_sp_campaignmember");

        $uniqueMembersCount = 0;

        global $db;

        foreach ($collection as $record)
        {
            if (!empty($record['sp_engageid']))
            {
                $query = "SELECT COUNT(*) FROM `sp_campaignmember`, `sp_campaign_sp_campaignmember_c`, `sp_campaign` WHERE `sp_campaignmember`.`deleted` = 0 AND ";
                if ($type == "contact")
                {
                    $query .= "`sp_campaignmember`.`contact_id_c` = '" . $record["id"] . "' AND `sp_campaign`.`id` = '" . $spCampaign->id . "' AND " .
                              "`sp_campaign_sp_campaignmember_c`.`sp_campaign_sp_campaignmembersp_campaignmember_idb` = `sp_campaignmember`.`id` AND " .
                              "`sp_campaign`.`id` = `sp_campaign_sp_campaignmember_c`.`sp_campaign_sp_campaignmembersp_campaign_ida`";
                }
                if ($type == "lead")
                {
                    $query .= "`sp_campaignmember`.`lead_id_c` = '" . $record["id"] . "' AND `sp_campaign`.`id` = '" . $spCampaign->id . "' AND " .
                        "`sp_campaign_sp_campaignmember_c`.`sp_campaign_sp_campaignmembersp_campaignmember_idb` = `sp_campaignmember`.`id` AND " .
                        "`sp_campaign`.`id` = `sp_campaign_sp_campaignmember_c`.`sp_campaign_sp_campaignmembersp_campaign_ida`";
                }

                $exists = $db->query($query)->fetch_row()[0] == 1;
                if ($exists)
                {
                    // Do not create campaign member if the same is created
                    continue;
                }
                
                $spCampaignMember = BeanFactory::newBean("SP_CampaignMember");
                
                $spCampaignMember->name             = "Acoustic Campaign Campaign Member: " . $record['sp_engageid'];
                $spCampaignMember->memberid         = $record['id'];
                $spCampaignMember->memberengageid   = $record['sp_engageid'];
                $spCampaignMember->membertype       = $type;
                
                if ($type == "contact")
                {
                    $spCampaignMember->contact_id_c = $record['id'];
                }
                if ($type == "lead")
                {
                    $spCampaignMember->lead_id_c = $record['id'];
                }
                
                $spCampaignMember->save();
                
                $spCampaign->sp_campaign_sp_campaignmember->add($spCampaignMember->id);

                $uniqueMembersCount++;
            }
        }
        
        $spCampaign->save();
        gc_collect_cycles();    // Free unused memory

        return [
            'success' => true,
            'message' => '',
            'content' => [
                "unique_members_count" => $uniqueMembersCount
            ]
        ];
    }

    /**
     *
     * @param SugarApi $api
     * @param array $args
     * @return array
     */
    public function getTotalRecordsOfCampaigns($api, $args)
    {
        $campaignId = $args['campaign_id'];
        $params     = array( 'disable_row_level_security' => true );
        $bean       = BeanFactory::retrieveBean("Campaigns", $campaignId, $params);

        if (is_null($bean))
        {
            return [
                "success" => false,
                "message" => "Cannot load campaign",
                "content" => null
            ];
        }

        $bean->load_relationship('prospectlists');
        if (!isset($bean->prospectlists) || is_null($bean->prospectlists))
        {
            return [
                "success" => false,
                "message" => "Cannot load campaign's target list(-s)",
                "content" => null
            ];
        }

        $totalRecordsLength = 0;
        $prospectLists = $bean->prospectlists->getBeans($params);

        global $db;

        foreach ($prospectLists as $prospectList)
        {
            $query = "SELECT COUNT(*) FROM `prospect_lists_prospects` WHERE `deleted` = 0 AND " .
	                 "`prospect_list_id` = '" . $prospectList->id . "' AND " .
                     "`related_type` IN ('Contacts', 'Leads')";

            $count = $db->query($query)->fetch_row()[0];

            $totalRecordsLength += $count;
        }

        return [
            "success" => true,
            "message" => "",
            "content" => [
                "length" => $totalRecordsLength
            ]
        ];
    }

    /**
     * Call the rest method and return the response.
     * @param string $data
     * @return string
     */
    private static function createResponse($data)
    {
        $response = SP_EngageApi::callRestMethod($data);
        if ($response === false)
        {
            $GLOBALS['log']->fatal("Error on SP REST method call response.");
            return json_encode(array( 'success' => false, 'message' => "Cannot send API request!", 'content' => "" ));
        }
        return json_encode(array( 'success' => true, 'message' => "", 'content' => $response ));
    }

    /**
     * Call the external rest api method.
     * @param string $data
     * @return String|boolean
     */
    private static function callRestMethod($data)
    {
        $accessToken        = SP_TokenStorage::getAccessToken();
        $generalSettings    = new SP_GeneralSettings();
        $generalSettings->retrieve();

        $http = [
            "Accept" => "application/xml; charset=utf-8",
            "Authorization" => "Bearer " . $accessToken,
            "Content-Type" => "text/xml; charset=utf-8"
        ];

        try
        {

            $url = $generalSettings->sp_host . "/XMLAPI";    
            $response = (new ExternalResourceClient())->post($url, $data, $http);
            $content = $response->getBody()->getContents();

            if (strpos($content, "<errorid>145</errorid>") != false)
            {
                $GLOBALS['log']->fatal('SP: The access token is expired and refreshing...');
                SP_EngageApi::refreshAccessToken();
                return SP_EngageApi::callRestMethod($data);
            }

            return $content;
        }
        catch (Exception $exception)
        {
            $GLOBALS['log']->fatal('Error on call SP REST method -> ' . $exception->getMessage());
            return false;
        }
    }

    /**
     * 
     * @return string
     */
    public static function refreshAccessToken()
    {
        $spGeneralSettings = new SP_GeneralSettings();
        $spGeneralSettings->retrieve();

        if (is_null($spGeneralSettings->sp_endpoint) || is_null($spGeneralSettings->sp_refreshtoken))
        {
            $GLOBALS['log']->fatal("SP: Failed to retrieve general settings!");
            return json_encode(array( 'error' => 'Unable to retrieve general settings!' ));
        }

        $path  = $spGeneralSettings->sp_endpoint . "/oauth/token";

        $data  = "grant_type=refresh_token";
        $data .= "&client_id="     . SP_TokenStorage::$sp_clientId;
        $data .= "&client_secret=" . SP_TokenStorage::$sp_clientSecret;
        $data .= "&refresh_token=" . $spGeneralSettings->sp_refreshtoken;

        $http = [
            "Content-Type" => "application/x-www-form-urlencoded"
        ];
  
		$response = (new ExternalResourceClient())->post($path, $data, $http);
        $content = $response->getBody()->getContents();

        if ($content === false || strpos($content, "invalid_token"))
        {
            $GLOBALS["log"]->fatal("Cannot send the SP request to refresh access token!");
            return json_encode(array( 'error' => 'Unable to send SP API request!' ));
        }

        $accessToken = json_decode($content)->access_token;

        //$GLOBALS['log']->fatal('SP Access Token -> ' . $accessToken);
        // Save the access token to the token storage
        SP_TokenStorage::putAccessToken($accessToken);

        return $accessToken;
    }
}
