<?php

/**
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; under version 2
 * of the License (non-upgradable).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Copyright (c) 2014-2020 (original work) Open Assessment Technologies SA;
 *
 */

declare(strict_types=1);

namespace oat\ltiDeliveryProvider\model\tasks;

use common_report_Report;
use oat\oatbox\extension\AbstractAction;
use oat\oatbox\log\LoggerAwareTrait;
use oat\taoDelivery\model\execution\ServiceProxy;
use oat\taoLti\models\classes\LtiOutcome\LtiOutcomeXmlFactory;
use oat\taoLti\models\classes\LtiService;
use oat\taoOutcomeUi\model\ResultsService;
use oat\taoResultServer\models\classes\ResultAliasServiceInterface;
use taoResultServer_models_classes_OutcomeVariable;

class SendLtiOutcomeTask extends AbstractAction
{
    use LoggerAwareTrait;

    const VARIABLE_IDENTIFIER = 'LtiOutcome';

    public function __invoke($params)
    {
        $report = new common_report_Report(common_report_Report::TYPE_ERROR);
        $deliveryResultIdentifier = $params['deliveryResultIdentifier'];
        $consumerKey = $params['consumerKey'];
        $serviceUrl = $params['serviceUrl'];

        try {
            $deliveryExecution = ServiceProxy::singleton()->getDeliveryExecution($deliveryResultIdentifier);
            $resultsService = ResultsService::singleton();
            $implementation = $resultsService->getReadableImplementation($deliveryExecution->getDelivery());
            $resultsService->setImplementation($implementation);

            $variables = $resultsService->getVariableDataFromDeliveryResult($deliveryResultIdentifier, [taoResultServer_models_classes_OutcomeVariable::class]);

            $submitted = 0;
            /** @var taoResultServer_models_classes_OutcomeVariable $variable */
            foreach ($variables as $variable) {
                if (self::VARIABLE_IDENTIFIER == $variable->getIdentifier()) {
                    $this->sendLtiOutcome($variable, $deliveryResultIdentifier, $consumerKey, $serviceUrl);
                    $submitted++;
                    break;
                }
            }
            if (0 === $submitted) {
                throw new \common_Exception('No LTI Outcome has been submitter for execution' . $deliveryResultIdentifier);
            }
        } catch (\Exception $exception) {
            $report->setMessage($exception->getMessage());
        }

        $report->setType(common_report_Report::TYPE_SUCCESS);
        return $report;
    }

    /**
     * @param taoResultServer_models_classes_OutcomeVariable $testVariable
     * @param $deliveryResultIdentifier
     * @param $consumerKey
     * @param $serviceUrl
     * @return bool
     * @throws \common_exception_Error
     * @throws \oat\taoLti\models\classes\LtiException
     */
    private function sendLtiOutcome(taoResultServer_models_classes_OutcomeVariable $testVariable, $deliveryResultIdentifier, $consumerKey, $serviceUrl)
    {
        $grade = (string)$testVariable->getValue();

        /** @var ResultAliasServiceInterface $resultAliasService */
        $resultAliasService = $this->getServiceLocator()->get(ResultAliasServiceInterface::SERVICE_ID);
        $deliveryResultAlias = $resultAliasService->getResultAlias($deliveryResultIdentifier);
        $deliveryResultIdentifier = empty($deliveryResultAlias) ? $deliveryResultIdentifier : current($deliveryResultAlias);

        $message = $this->getLtiOutcomeXmlFactory()->buildReplaceResultRequest($deliveryResultIdentifier, $grade, uniqid('', true));

        $credentialResource = LtiService::singleton()->getCredential($consumerKey);
        $credentials = new \tao_models_classes_oauth_Credentials($credentialResource);
        //Building POX raw http message
        $unSignedOutComeRequest = new \common_http_Request($serviceUrl, 'POST', []);
        $unSignedOutComeRequest->setBody($message);
        $signingService = new \tao_models_classes_oauth_Service();
        $signedRequest = $signingService->sign($unSignedOutComeRequest, $credentials, true);
        //Hack for moodle compatibility, the header is ignored for the signature computation
        $signedRequest->setHeader("Content-Type", "application/xml");

        $response = $signedRequest->send();

        if ('200' != $response->httpCode) {
            $this->logWarning("Request sent (Body)\n" . $signedRequest->getBody() . "\n");
            $this->logWarning("Request sent (Headers)\n" . serialize($signedRequest->getHeaders()) . "\n");
            $this->logWarning("\nHTTP Code received: " . $response->httpCode . "\n");

            $this->logWarning("\nHTTP From: " . $response->effectiveUrl . "\n");
            $this->logWarning("\nHTTP Content received: " . $response->responseData . "\n");

            throw new \common_exception_Error('An HTTP level problem occurred when sending the outcome to the service url');
        } else {
            $this->logInfo('Submited LTI score with id "' . $deliveryResultIdentifier . '"');
        }
        return true;
    }

    private function getLtiOutcomeXmlFactory(): LtiOutcomeXmlFactory
    {
        return $this->getServiceLocator()->get(LtiOutcomeXmlFactory::class);
    }
}