<?php

namespace AuthStack\SP;

//use function Buckhill\config;
use LightSaml\Credential\KeyHelper;
use LightSaml\Binding\BindingFactory;
use LightSaml\Model\Protocol\Response;
use AuthStack\SP\Validators\SAMLResponse;
use RobRichards\XMLSecLibs\XMLSecurityKey;
use LightSaml\Context\Profile\MessageContext;
use Symfony\Component\HttpFoundation\Request;
use LightSaml\Error\LightSamlBindingException;
use AuthStack\SP\Erors\DecryptionKeyException;
use LightSaml\Model\Context\DeserializationContext;

abstract class AbstractSP
{
    protected $idp;

    public function __construct(array $config, $idp)
    {
        $this->idp = $idp;

        \Buckhill\config()->set('AuthStack', $config);
    }


    /**
     * @param Request|null $request
     *
     * @return AttributeCollection
     */
    abstract public function consume(Request $request = null);


    abstract public function authn();


    /**
     * @param Request|null $request
     *
     * @return Response
     */
    protected function checkRequest(Request $request = null)
    {
        if(is_null($request))
        {
            $request = Request::createFromGlobals();
        }

        $response = $this->getResponse($request);

        $validator = new SAMLResponse($this->idp);

        $validator->validate($response);

        return $response;
    }


    /**
     * @param Request $request
     * @return \LightSaml\Model\Protocol\Response
     * @throws \LogicException|LightSamlBindingException
     *
     */
    protected function getResponse(Request $request)
    {
        $factory = new BindingFactory();
        $context = new MessageContext();

        $binding = $factory->getBindingByRequest($request);

        $binding->receive($request, $context);

        return $context->getMessage();
    }


    /**
     * @return \RobRichards\XMLSecLibs\XMLSecurityKey
     * @throws DecryptionKeyException
     */
    protected function getDecryptionKey()
    {
        $key = \Buckhill\config('AuthStack.SP.keys.encryption', false);

        if(false === $key)
        {
            throw new DecryptionKeyException("Service Provider wants encrypted assertions, but there's no decryption key available.");
        }

        return KeyHelper::createPrivateKey($key['private'], $key['passphrase'], false, $key['algorithm']);
    }


    /**
     * @param Response $response
     *
     * @return \LightSaml\Model\Assertion\Assertion
     * @throws DecryptionKeyException
     */
    protected function getAssertions(Response $response)
    {
        if(\Buckhill\config('AuthStack.SP.wantsEncryptedAssertions', false))
        {
            $assertion = $this->decrypt($response, $this->getDecryptionKey());
        }
        else
        {
            $assertion = $response->getFirstAssertion();
        }

        return $assertion;
    }


    /**
     * @param Response       $response
     * @param XMLSecurityKey $key
     *
     * @return \LightSaml\Model\Assertion\Assertion
     */
    protected function decrypt(Response $response, XMLSecurityKey $key)
    {
        $ctx = new DeserializationContext();

        /** @var  $assertion \LightSaml\Model\Assertion\EncryptedAssertionReader  */
        $assertion = $response->getFirstEncryptedAssertion();

        $decrypted = $assertion->decryptAssertion($key, $ctx);

        return $decrypted;
    }
}