<?php

/**
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is protected 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  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace Sso\RestBundle\Tests\ModelRest\Response\UserWs\Version2\UserApplication\Show;

use Sso\RestBundle\Api;
use Sso\RestBundle\ModelRest\Response\UserWs\Version2\UserApplication\Show\ApplicationType;
use Sso\RestBundle\ModelRest\Response\UserWs\Version2\UserApplication\Show\Response as RestResponse;
use Sso\RestBundle\ModelRest\Response\UserWs\Version2\UserApplication\Show\UserApplication;
use Sso\RestBundle\ModelRest\Response\UserWs\Version2\UserApplication\Show\UserType;
use Sso\RestBundle\ModelXml\Response\UserWs\Version2\UserApplication\Show\Success\UserType as XmlUserType;
use Sso\RestBundle\ModelXml\Response\UserWs\Version2\UserApplication\Show\Success\ApplicationType as XmlApplicationType;
use Doctrine\Common\Collections\ArrayCollection;
use GuzzleHttp\Client;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Sso\RestBundle\ModelRest\Common\Response\UserWs\ErrorCollector;
use Faker\Factory as FakerFactory;
use Faker\Generator as FakerGenerator;

/**
 * ResponseTest
 *
 * @copyright  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */
class ResponseTest extends WebTestCase
{
    /**
     * @var Api\Manager
     */
    public $apiM;

    /**
     * @var Client
     */
    protected $client;

    /**
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @var \JMS\Serializer\Serializer
     */
    protected $serializer;

    /**
     * @var FakerGenerator
     */
    private $faker;

    /**
     * @var string
     */
    private $trackId;

    /**
     * @var string
     */
    private $scriptTimeSec;

    /**
     * @var string
     */
    private $date;

    /**
     * @var string
     */
    private $name;

    /**
     * @var string
     */
    private $shortMessage;

    /**
     * @var string
     */
    private $message;

    /**
     * @var string
     */
    private $guid;

    /**
     * @var string
     */
    private $email;

    /**
     * @var string
     */
    private $username;

    /**
     * @var string
     */
    private $firstName;

    /**
     * @var string
     */
    private $lastName;

    /**
     * @var string
     */
    private $password;

    /**
     * @var boolean
     */
    private $active;

    /**
     * @var boolean
     */
    private $deleted;

    /**
     * @var string
     */
    private $createdAt;

    /**
     * @var string
     */
    private $updatedAt;

    /**
     * @var string
     */
    private $authId;

    /**
     * @var boolean
     */
    private $mfaEnabled;

    /**
     * @var integer
     */
    private $errorCount;

    /**
     * @var string
     */
    private $controller;

    /**
     * @var string
     */
    private $action;

    /**
     * @var integer
     */
    private $applicationActive;

    protected function setUp()
    {
        $this->client = static::createClient();
        $this->container = $this->client->getContainer();
        $this->apiM = new Api\Manager($this->container);
        $this->serializer = $this->apiM->serializer();
        $this->faker = FakerFactory::create();

        // Set same random data
        $this->scriptTimeSec = $this->faker->randomFloat(2);
        $this->trackId = $this->faker->md5;
        $this->controller = $this->faker->word;
        $this->action = $this->faker->word;
        $this->date = '2099-01-01T00:00:00+00:00';
        $this->errorCount = $this->faker->randomNumber();
        $this->name = $this->faker->name;
        $this->guid = $this->faker->md5;
        $this->email = $this->faker->email;
        $this->username = $this->faker->userName;
        $this->firstName = $this->faker->firstName;
        $this->lastName = $this->faker->lastName;
        $this->password = 'd5zJwgnzU2uTtv97';
        $this->active = $this->faker->boolean ? 1 : 0;
        $this->deleted = $this->faker->boolean ? 1 : 0;
        $this->createdAt = '2099-01-01T00:00:00+00:00';
        $this->updatedAt = '2099-01-01T00:00:00+00:00';
        $this->authId = $this->faker->randomNumber();
        $this->shortMessage = $this->faker->sentence(2);
        $this->message = $this->faker->sentence(4);
        $this->mfaEnabled = $this->faker->boolean ? 1 : 0;
        $this->applicationActive = $this->faker->boolean ? 1 : 0;
    }

    public function testResponseJSONModel()
    {
        $this->assertJsonStringEqualsJsonString(
            $this->getSuccessResponse('json'),
            $this->getResponseModel('json')
        );
    }

    public function testResponseXMLModel()
    {
        $this->assertXmlStringEqualsXmlString(
            $this->getSuccessResponse('xml'),
            $this->getResponseModel('xml')
        );
    }

    public function testResponseJSONModelXml()
    {
        $this->assertJsonStringEqualsJsonString(
            $this->getSuccessResponse('json'),
            $this->getResponseModelXml('json')
        );
    }

    public function testResponseXMLModelXml()
    {
        $this->assertXmlStringEqualsXmlString(
            $this->getSuccessResponse('xml'),
            $this->getResponseModelXml('xml')
        );
    }

    public function testGetterSuccess()
    {
        /** @var  RestResponse $responseModel */
        $responseModel = $this->serializer->deserialize(
            $this->getSuccessResponse('json'),
            RestResponse::class,
            'json'
        );

        $this->assertEquals(200, $responseModel->getCode());
        $this->assertEquals('OK', $responseModel->getStatus());
        $this->assertEquals($this->scriptTimeSec, $responseModel->getScriptTimeSec());
        $this->assertEquals($this->trackId, $responseModel->getTrackId());
        $this->assertEquals(new \DateTime($this->date), $responseModel->getDate());

        $userApplication = $responseModel->getUserApplication();
        $userType = $userApplication->getUserType();

        $this->assertEquals(200, $userType->getCode());
        $this->assertEquals('OK', $userType->getStatus());
        $this->assertEquals($this->guid, $userType->getGuid());
        $this->assertEquals($this->username, $userType->getUsername());
        $this->assertEquals($this->email, $userType->getEmail());
        $this->assertEquals($this->firstName, $userType->getFirstName());
        $this->assertEquals($this->active, $userType->getActive());
        $this->assertEquals($this->deleted, $userType->getDeleted());
        $this->assertEquals(new \DateTime($this->createdAt), $userType->getCreatedAt());
        $this->assertEquals(new \DateTime($this->updatedAt), $userType->getUpdatedAt());
        $this->assertEquals($this->authId, $userType->getAuthId());
        $this->assertEquals('', $userType->getLdapSearchAttributes());
        $this->assertEquals('', $userType->getLdapSearchValue());
        $this->assertEquals($this->mfaEnabled, $userType->getMfaEnabled());

        foreach ($userApplication->getApplicationType() as $applicationType) {
            $this->assertEquals($this->name, $applicationType->getName());
            $this->assertEquals($this->applicationActive, $applicationType->getActive());

            break;
        }
    }

    public function testSetterError()
    {
        $responseErrorModel = new RestResponse();
        $userApplication = new UserApplication();

        $responseErrorModel
            ->setCode(400)
            ->setScriptTimeSec($this->scriptTimeSec)
            ->setDate(new \DateTime($this->date))
            ->setTrackId($this->trackId);

        $error = new ErrorCollector\Error();
        $error
            ->setCode(400)
            ->setMessage($this->message)
            ->setException('external');

        $userApplication->setError($error);
        $responseErrorModel->setUserApplication($userApplication);

        $this->assertJsonStringEqualsJsonString(
            $this->serializer->serialize($responseErrorModel, "json"),
            $this->getErrorResponse()
        );
    }

    public function testGetterError()
    {
        /** @var  RestResponse $responseModel */
        $responseModel = $this->serializer->deserialize(
            $this->getErrorResponse('json'),
            RestResponse::class,
            'json'
        );

        $this->assertEquals(400, $responseModel->getCode());
        $this->assertEquals('Bad Request', $responseModel->getStatus());
        $this->assertEquals($this->scriptTimeSec, $responseModel->getScriptTimeSec());
        $this->assertEquals($this->trackId, $responseModel->getTrackId());
        $this->assertEquals(new \DateTime($this->date), $responseModel->getDate());

        $userApplication = $responseModel->getUserApplication();
        foreach ($userApplication->getError() as $error) {
            $this->assertEquals(400, $error->getCode());
            $this->assertEquals($this->message, $error->getMessage());
            $this->assertEquals('external', $error->getException());
        }
    }

    /**
     * @param string $type
     * @return string
     */
    private function getResponseModel($type = 'json')
    {
        $response = new RestResponse();
        $response
            ->setCode(200)
            ->setStatus('OK')
            ->setTrackId($this->trackId)
            ->setDate(new \DateTime($this->date))
            ->setScriptTimeSec($this->scriptTimeSec);

        $response->setUserApplication($this->getUserApplication());
        $responseModel = $this->serializer->serialize($response, $type);

        return $responseModel;
    }

    /**
     * @return UserApplication
     */
    private function getUserApplication()
    {
        $userApplication = new UserApplication();
        $userType = new UserType();
        $applicationType = new ApplicationType();

        $userType
            ->setCode(200)
            ->setStatus('OK');
        $userType
            ->setGuid($this->guid)
            ->setUsername($this->username)
            ->setEmail($this->email)
            ->setFirstName($this->firstName)
            ->setLastName($this->lastName)
            ->setActive($this->active)
            ->setAuthId($this->authId)
            ->setLdapSearchAttributes('')
            ->setLdapSearchValue('');
        $userType
            ->setDeleted($this->deleted)
            ->setCreatedAt(new \DateTime($this->createdAt))
            ->setUpdatedAt(new \DateTime($this->updatedAt))
            ->setMfaEnabled($this->mfaEnabled);

        $applicationType
            ->setName($this->name)
            ->setActive($this->applicationActive);

        $applicationTypes = new ArrayCollection();
        $applicationTypes->add($applicationType);

        $userApplication
            ->setUserType($userType)
            ->setApplicationType($applicationTypes);

        return $userApplication;
    }

    /**
     * @param string $type
     * @return string
     */
    private function getResponseModelXml($type = 'json')
    {
        $response = new RestResponse();
        $response
            ->setCode(200)
            ->setStatus('OK')
            ->setTrackId($this->trackId)
            ->setDate(new \DateTime($this->date))
            ->setScriptTimeSec($this->scriptTimeSec);

        $response->setUserApplication($this->getUserApplicationXml());
        $responseModel = $this->serializer->serialize($response, $type);

        return $responseModel;
    }

    /**
     * @return UserApplication
     */
    private function getUserApplicationXml()
    {
        $userApplication = new UserApplication();
        $userType = new XmlUserType();
        $applicationType = new XmlApplicationType();

        $userType
            ->setGuid($this->guid)
            ->setUsername($this->username)
            ->setEmail($this->email)
            ->setFirstName($this->firstName)
            ->setLastName($this->lastName)
            ->setActive($this->active)
            ->setAuthId($this->authId)
            ->setLdapSearchAttributes('')
            ->setLdapSearchValue('');
        $userType
            ->setDeleted($this->deleted)
            ->setCreatedAt(new \DateTime($this->createdAt))
            ->setUpdatedAt(new \DateTime($this->updatedAt))
            ->setMfaEnabled($this->mfaEnabled);

        $applicationType
            ->setName($this->name)
            ->setActive($this->applicationActive);

        $applicationTypes = new ArrayCollection();
        $applicationTypes->add($applicationType);

        $userApplication
            ->setUserType($userType)
            ->setApplicationType($applicationTypes);

        return $userApplication;
    }

    /**
     * @param string $type
     * @return string
     */
    private function getSuccessResponse($type = 'json')
    {
        if ($type === 'xml') {
            return '<?xml version="1.0" encoding="UTF-8"?>
                <userApplication>
                  <code>200</code>
                  <status><![CDATA[OK]]></status>
                  <trackId><![CDATA[' . $this->trackId . ']]></trackId>
                  <date><![CDATA[' . $this->date . ']]></date>
                  <scriptTimeSec>' . $this->scriptTimeSec . '</scriptTimeSec>
                  <userApplications>
                    <userType>
                        <code>200</code>
                        <status><![CDATA[OK]]></status>
                        <guid><![CDATA[' . $this->guid . ']]></guid>
                        <username><![CDATA[' . $this->username . ']]></username>
                        <email><![CDATA[' . $this->email . ']]></email>
                        <firstname><![CDATA[' . $this->firstName . ']]></firstname>
                        <lastname><![CDATA[' . $this->lastName . ']]></lastname>
                        <active>' . $this->active . '</active>
                        <deleted>' . $this->deleted . '</deleted>
                        <createdAt><![CDATA[' . $this->createdAt . ']]></createdAt>
                        <updatedAt><![CDATA[' . $this->updatedAt . ']]></updatedAt>
                        <authId><![CDATA[' . $this->authId . ']]></authId>
                        <mfaEnabled>' . $this->mfaEnabled . '</mfaEnabled>
                    </userType>
                    <applicationType>
                        <name><![CDATA[' . $this->name . ']]></name>
                        <active>' . $this->applicationActive . '</active>
                    </applicationType>
                  </userApplications>
                </userApplication>';
        } else {
            return '{
              "code": 200,
              "status": "OK",
              "trackId": "' . $this->trackId . '",
              "date": "' . $this->date . '",
              "scriptTimeSec": ' . $this->scriptTimeSec . ',
              "userApplications": {
                  "userType": {
                      "code": 200,
                      "status": "OK",
                      "guid": "' . $this->guid . '",
                      "username": "' . $this->username . '",
                      "email": "' . $this->email . '",
                      "firstname": "' . $this->firstName . '",
                      "lastname": "' . $this->lastName . '",
                      "active": ' . $this->active . ',
                      "deleted": ' . $this->deleted . ',
                      "createdAt": "' . $this->createdAt . '",
                      "updatedAt": "' . $this->updatedAt . '",
                      "authId": "' . $this->authId . '",
                      "mfaEnabled": ' . $this->mfaEnabled . '                   
                  },
                  "applicationType": [
                    {
                        "name": "' . $this->name . '",
                        "active": "' . $this->applicationActive . '"
                    }
                  ]
              }
            }';
        }
    }

    /**
     * @param string $type
     * @return string
     */
    private function getErrorResponse($type = 'json')
    {
        if ($type === 'xml') {
            return '<?xml version="1.0" encoding="UTF-8"?>
                      <userApplication>
                          <code>400</code>
                          <status><![CDATA[Bad Request]]></status>
                          <trackId><![CDATA[' . $this->trackId . ']]></trackId>
                          <date><![CDATA[' . $this->date . ']]></date>
                          <scriptTimeSec>' . $this->scriptTimeSec . '</scriptTimeSec>
                          <userApplications>
                              <errors>
                                  <error>
                                      <code>400</code>
                                      <message><![CDATA[' . $this->message . ']]></message>
                                      <exception><![CDATA[external]]></exception>
                                  </error>
                              </errors>
                          </userApplications>
                      </userApplication>';
        } else {
            return '{
                      "code": 400,
                      "status": "Bad Request",
                      "trackId": "' . $this->trackId . '",
                      "date": "' . $this->date . '",
                      "scriptTimeSec": ' . $this->scriptTimeSec . ',
                      "userApplications": {
                          "errors": [
                              {
                                  "code": 400,
                                  "message": "' . $this->message . '",
                                  "exception": "external"
                              }
                          ]
                      }
                  }';
        }
    }
}
