<?php

/**
 * Lifestyle Webconsulting GmbH
 *
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is private by copyright law - it is NOT Freeware.
 *
 * Any unauthorized use of this software without a valid license
 * is a violation of the license agreement and will be prosecuted by
 * civil and criminal law.
 *
 * @copyright  2018 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace Sso\WebserviceBundle\Tests\Services\User\Update;

use Sso\WebserviceBundle\Api\ApiManagerInterface;
use Sso\WebserviceBundle\Database\Webservice\User as UserRepository;
use Sso\WebserviceBundle\ErrorHandler\ErrorHandlerInterface;
use Sso\WebserviceBundle\Services\ApiVersionInflectorInterface;
use Sso\WebserviceBundle\Services\User\Update\Factory;
use Sso\WebserviceBundle\Services\User\Update\Handler;
use Sso\WebserviceBundle\Services\RequestInterface;
use Sso\WebserviceBundle\Services\ResponseBuilderInterface;
use Doctrine\ORM\EntityManagerInterface;
use LifeStyle\Tools\MfaBundle\MfaCreator;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Sso\WebserviceBundle\Entity\Webservice\Type\User as UserType;
use Sso\WebserviceBundle\Services\User\Update\RequestData\User as RequestDataUser;
use Sso\WebserviceBundle\Services\User\Update\ResponseBuilderInterface as UserUpdateResponsebuilder;
use Symfony\Component\Validator\Validator\ValidatorInterface as Validator;
use Sso\WebserviceBundle\Api\PasswordPolicy\History\Service as PasswordHistoryService;
use Sso\WebserviceBundle\Database\Webservice\PasswordPolicy as PasswordPolicyDatabase;

/**
 * Class HandlerTest
 *
 * @copyright  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 * @package Sso\WebserviceBundle\Tests\Services\User\Show
 */
class HandlerTest extends WebTestCase
{
    /**
     * @var UserRepository
     */
    private $database;

    /**
     * @var ValidatorInterface
     */
    private $validator;

    /**
     * @var ApiManagerInterface
     */
    private $apiManager;

    /**
     * @var MfaCreator
     */
    private $mfaCreator;

    /**
     * @var PasswordPolicyDatabase
     */
    private $passwordPolicyDatabase;

    /**
     * @var PasswordHistoryService
     */
    private $passwordHistoryService;


    public function testHandleSuccess()
    {
        $faker = \Faker\Factory::create();
        $email = $faker->email;

        $apiVersion = 2;

        $apiVersionInflector = $this->createMock(ApiVersionInflectorInterface::class);
        $apiVersionInflector
            ->method('inflect')
            ->will(self::returnCallback(function ($namespace, $classname) use ($apiVersion) {
                return $namespace . '\\Version' . $apiVersion . '\\' . $classname;
            }));
        $factory = new Factory($this->apiManager, $apiVersionInflector);

        $errorHandler = $this->createMock(ErrorHandlerInterface::class);
        $errorHandler
            ->method('hasErrors')
            ->will(self::returnValue(false));

        $request = $this->createMock(RequestInterface::class);
        $request
            ->expects(self::once())
            ->method('getRequestBody')
            ->will(self::returnValue($this->getRequestBody($email)));

        $userTypeMock = $this->createMock(UserType::class);
        /** @var $databaseMock \PHPUnit_Framework_MockObject_MockObject */
        $databaseMock = $this->database;
        $databaseMock
            ->expects(static::any())
            ->method('getUserByIdentifier')
            ->willReturn($userTypeMock);
        $databaseMock
            ->expects(static::any())
            ->method('saveUser')
            ->willReturn(true);
        $this->database = $databaseMock;

        $passwordPolicyDatabase = $this->passwordPolicyDatabase;

        $passwordHistoryService = $this->passwordHistoryService;


        $handler = new Handler($factory, $this->database, $errorHandler, $this->mfaCreator, $passwordPolicyDatabase, $passwordHistoryService);

        $responseBuilder = $handler->handle($request);

        self::assertInstanceOf(ResponseBuilderInterface::class, $responseBuilder);
    }

    public function testHandleLdapValues()
    {
        $faker = \Faker\Factory::create();
        $email = $faker->email;

        $apiVersion = 2;

        $apiVersionInflector = $this->createMock(ApiVersionInflectorInterface::class);
        $apiVersionInflector
            ->method('inflect')
            ->will(self::returnCallback(function ($namespace, $classname) use ($apiVersion) {
                return $namespace . '\\Version' . $apiVersion . '\\' . $classname;
            }));
        $factory = new Factory($this->apiManager, $apiVersionInflector);

        $errorHandler = $this->createMock(ErrorHandlerInterface::class);
        $errorHandler
            ->method('hasErrors')
            ->will(self::returnValue(false));

        $request = $this->createMock(RequestInterface::class);
        $request
            ->expects(self::once())
            ->method('getRequestBody')
            ->will(self::returnValue($this->getRequestBodyLdapSetValues()));

        $validatorMock = $this->createMock(Validator::class);
        $userType = new UserType($validatorMock);
        $userType
            ->setAuthId('OLD-VALUE-01')
            ->setLdapSearchAttributes('OLD-VALUE-02')
            ->setLdapSearchValue('OLD-VALUE-03');
        /** @var $databaseMock \PHPUnit_Framework_MockObject_MockObject */
        $databaseMock = $this->database;
        $databaseMock
            ->expects(static::any())
            ->method('getUserByIdentifier')
            ->willReturn($userType);
        $databaseMock
            ->expects(static::any())
            ->method('saveUser')
            ->willReturn(true);
        $this->database = $databaseMock;

        $passwordPolicyDatabase = $this->passwordPolicyDatabase;

        $passwordHistoryService = $this->passwordHistoryService;

        $handler = new Handler($factory, $this->database, $errorHandler, $this->mfaCreator, $passwordPolicyDatabase, $passwordHistoryService);

        /** @var ResponseBuilderInterface $responseBuilder */
        $responseBuilder = $handler->handle($request);
        $responseData = json_decode($responseBuilder->build('json'), true);

        $this->assertEquals('TEST00000', $responseData['User']['Update'][0]['UserType']['AuthId']);
        $this->assertEquals('LDAPMEEPMOOP', $responseData['User']['Update'][0]['UserType']['LdapSearchAttributes']);
        $this->assertEquals('LDAPWUBWUB', $responseData['User']['Update'][0]['UserType']['LdapSearchValue']);
    }

    public function testHandleLdapEmptyValues()
    {
        $faker = \Faker\Factory::create();
        $email = $faker->email;

        $apiVersion = 2;

        $apiVersionInflector = $this->createMock(ApiVersionInflectorInterface::class);
        $apiVersionInflector
            ->method('inflect')
            ->will(self::returnCallback(function ($namespace, $classname) use ($apiVersion) {
                return $namespace . '\\Version' . $apiVersion . '\\' . $classname;
            }));
        $factory = new Factory($this->apiManager, $apiVersionInflector);

        $errorHandler = $this->createMock(ErrorHandlerInterface::class);
        $errorHandler
            ->method('hasErrors')
            ->will(self::returnValue(false));

        $request = $this->createMock(RequestInterface::class);
        $request
            ->expects(self::once())
            ->method('getRequestBody')
            ->will(self::returnValue($this->getRequestBodyLdapEmptyValues()));

        $validatorMock = $this->createMock(Validator::class);
        $userType = new UserType($validatorMock);
        $userType
            ->setAuthId('OLD-VALUE-01')
            ->setLdapSearchAttributes('OLD-VALUE-02')
            ->setLdapSearchValue('OLD-VALUE-03');
        /** @var $databaseMock \PHPUnit_Framework_MockObject_MockObject */
        $databaseMock = $this->database;
        $databaseMock
            ->expects(static::any())
            ->method('getUserByIdentifier')
            ->willReturn($userType);
        $databaseMock
            ->expects(static::any())
            ->method('saveUser')
            ->willReturn(true);
        $this->database = $databaseMock;

        $passwordPolicyDatabase = $this->passwordPolicyDatabase;

        $passwordHistoryService = $this->passwordHistoryService;

        $handler = new Handler($factory, $this->database, $errorHandler, $this->mfaCreator, $passwordPolicyDatabase, $passwordHistoryService);

        /** @var ResponseBuilderInterface $responseBuilder */
        $responseBuilder = $handler->handle($request);
        $responseData = json_decode($responseBuilder->build('json'), true);

        $this->assertEquals('', $responseData['User']['Update'][0]['UserType']['AuthId']);
        $this->assertEquals('', $responseData['User']['Update'][0]['UserType']['LdapSearchAttributes']);
        $this->assertEquals('', $responseData['User']['Update'][0]['UserType']['LdapSearchValue']);
    }

    public function testHandleLdapNoValues()
    {
        $faker = \Faker\Factory::create();
        $email = $faker->email;

        $apiVersion = 2;

        $apiVersionInflector = $this->createMock(ApiVersionInflectorInterface::class);
        $apiVersionInflector
            ->method('inflect')
            ->will(self::returnCallback(function ($namespace, $classname) use ($apiVersion) {
                return $namespace . '\\Version' . $apiVersion . '\\' . $classname;
            }));
        $factory = new Factory($this->apiManager, $apiVersionInflector);

        $errorHandler = $this->createMock(ErrorHandlerInterface::class);
        $errorHandler
            ->method('hasErrors')
            ->will(self::returnValue(false));

        $request = $this->createMock(RequestInterface::class);
        $request
            ->expects(self::once())
            ->method('getRequestBody')
            ->will(self::returnValue($this->getRequestBodyLdapNoValues()));

        $validatorMock = $this->createMock(Validator::class);
        $userType = new UserType($validatorMock);
        $userType
            ->setAuthId('OLD-VALUE-01')
            ->setLdapSearchAttributes('OLD-VALUE-02')
            ->setLdapSearchValue('OLD-VALUE-03');
        /** @var $databaseMock \PHPUnit_Framework_MockObject_MockObject */
        $databaseMock = $this->database;
        $databaseMock
            ->expects(static::any())
            ->method('getUserByIdentifier')
            ->willReturn($userType);
        $databaseMock
            ->expects(static::any())
            ->method('saveUser')
            ->willReturn(true);
        $this->database = $databaseMock;

        $passwordPolicyDatabase = $this->passwordPolicyDatabase;

        $passwordHistoryService = $this->passwordHistoryService;

        $handler = new Handler($factory, $this->database, $errorHandler, $this->mfaCreator, $passwordPolicyDatabase, $passwordHistoryService);

        /** @var ResponseBuilderInterface $responseBuilder */
        $responseBuilder = $handler->handle($request);
        $responseData = json_decode($responseBuilder->build('json'), true);

        $this->assertEquals('OLD-VALUE-01', $responseData['User']['Update'][0]['UserType']['AuthId']);
        $this->assertEquals('OLD-VALUE-02', $responseData['User']['Update'][0]['UserType']['LdapSearchAttributes']);
        $this->assertEquals('OLD-VALUE-03', $responseData['User']['Update'][0]['UserType']['LdapSearchValue']);
    }

    /**
     * @param string $identifier
     * @return string
     */
    private function getRequestBody($identifier)
    {
        return '<?xml version="1.0"?>
<SsoRequest>
  <User>
    <Update>
      <Key>
        <UserType>
          <Identifier>e3d93635dac64c998830b623e6b5e6a8</Identifier>
        </UserType>
      </Key>
      <Value>
        <UserType>
          <Email>test@test.tst</Email>
        </UserType>
      </Value>
    </Update>
  </User>
</SsoRequest>';
    }

    /**
     * @param string $identifier
     * @return string
     */
    private function getRequestBodyLdapSetValues()
    {
        return '<?xml version="1.0"?>
<SsoRequest>
  <User>
    <Update>
      <Key>
        <UserType>
          <Identifier>e3d93635dac64c998830b623e6b5e6a8</Identifier>
        </UserType>
      </Key>
      <Value>
        <UserType>
          <AuthId>TEST00000</AuthId>
          <LdapSearchAttributes>LDAPMEEPMOOP</LdapSearchAttributes>
          <LdapSearchValue>LDAPWUBWUB</LdapSearchValue>
        </UserType>
      </Value>
    </Update>
  </User>
</SsoRequest>';
    }

    /**
     * @param string $identifier
     * @return string
     */
    private function getRequestBodyLdapNoValues()
    {
        return '<?xml version="1.0"?>
<SsoRequest>
  <User>
    <Update>
      <Key>
        <UserType>
          <Identifier>e3d93635dac64c998830b623e6b5e6a8</Identifier>
        </UserType>
      </Key>
      <Value>
        <UserType>
        </UserType>
      </Value>
    </Update>
  </User>
</SsoRequest>';
    }

    /**
     * @param string $identifier
     * @return string
     */
    private function getRequestBodyLdapEmptyValues()
    {
        return '<?xml version="1.0"?>
<SsoRequest>
  <User>
    <Update>
      <Key>
        <UserType>
          <Identifier>e3d93635dac64c998830b623e6b5e6a8</Identifier>
        </UserType>
      </Key>
      <Value>
        <UserType>
          <AuthId></AuthId>
          <LdapSearchAttributes></LdapSearchAttributes>
          <LdapSearchValue></LdapSearchValue>
        </UserType>
      </Value>
    </Update>
  </User>
</SsoRequest>';
    }

    protected function setUp()
    {
        parent::setUp();
        static::bootKernel();
        $this->apiManager = static::$kernel->getContainer()->get('service_api_manager');
        $entityManager = $this->createMock(EntityManagerInterface::class);
        $validator = $this->createMock(ValidatorInterface::class);
        $readonly = false;
        $this->database = $this->createMock(UserRepository::class, array(), array($entityManager, $validator, $readonly));
        $this->validator = $validator;
        $this->mfaCreator = $this->getMockBuilder(MfaCreator::class)->getMock();
        $this->passwordHistoryService = $this->getMockBuilder(PasswordHistoryService::class)
                                                ->disableOriginalConstructor()
                                                ->getMock();
        $this->passwordPolicyDatabase = $this->getMockBuilder(PasswordPolicyDatabase::class)
                                                ->disableOriginalConstructor()
                                                ->getMock();
    }

}
