<?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.
 *
 * @author      shgb
 * @copyright   2016 Lifestyle Webconsulting GmbH
 * @link        http://www.life-style.de
 */

namespace Sso\DemoBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sso\DemoBundle\Exception\DemoException;

/**
 * Class BaseController
 * @package Sso\DemoBundle\Controller
 */
abstract class BaseController extends Controller
{
    /**
     * IDP user identifier
     */
    const USER_IDENTIFIER = "5e211de5c4aa76564fc5e3657a7946aab3e2dbe60a773117568b3e67a2223a2c6878e5d25ee70588b19728f010417388f590128beced60402c7b8036b725ba71";
    /**
     * WS service token
     */
    const SERVICE_TOKEN = "V2030cOhc6Vttcxs6r6bmtdlc8Z1ryTN1TQ16pjivEcprZhye9e17ba7508a4358f99c8ba52ded0f70RnZDhDyKMXLEWobXI2ZOrJ7mXjcPJJNk7wxoAw8WUTk0IMYa";

    /**
     * Link to profiler from last webservice-request
     * @var string
     */
    protected $urlProfiler;

    /**
     * Form default values
     * @return array
     */
    protected function getFormDefaults()
    {
        return [
            'UserUsername' => $this->getUserUsername(),
            'UserIdentifier' => $this->getUserIdentifier(),
            'ApplicationUsername' => $this->getApplicationUsername(),
            'ApplicationUserIdentifier' => $this->getApplicationUserIdentifier(),
            'ServiceToken' => $this->getServiceToken(),
            'ServiceCountry' => $this->getServiceCountry(),
        ];
    }

    /**
     * @return \Symfony\Component\Form\FormBuilderInterface
     */
    protected function buildFormCredentials()
    {
        return $this->createFormBuilder()
            ->create('credentials', 'form', ['virtual' => true, 'attr' => ['class' => 'form-group-indent']])
            ->add('ServiceToken', 'text')
            ->add('ServiceCountry', 'hidden')
            ->add('UserUsername', 'text')
            ->add('UserIdentifier', 'text')
            ->add('ApplicationUsername', 'text')
            ->add('ApplicationUserIdentifier', 'text')
        ;
    }

    /**
     * Get user name string
     * @return string
     */
    protected function getUserUsername()
    {
        if (null === ($userUsername = $this->sessionVariable('UserUsername'))) {
            $userUsername = $this->get('request')->get('UserUsername', '');
            $this->sessionVariable('UserUsername', $userUsername);
        }

        return $userUsername;
    }

    /**
     * Get user identifier string
     * @return string
     */
    protected function getUserIdentifier()
    {
        if (null === ($userIdentifier = $this->sessionVariable('UserIdentifier'))) {
            $userIdentifier = $this->get('request')->get('UserIdentifier', static::USER_IDENTIFIER);
            $this->sessionVariable('UserIdentifier', $userIdentifier);
        }

        return $userIdentifier;
    }

    /**
     * Get user name string
     * @return string
     */
    protected function getApplicationUsername()
    {
        if (null === ($userUsername = $this->sessionVariable('ApplicationUsername'))) {
            $userUsername = $this->get('request')->get('ApplicationUsername', '');
            $this->sessionVariable('ApplicationUsername', $userUsername);
        }

        return $userUsername;
    }

    /**
     * Get user identifier string
     * @return string
     */
    protected function getApplicationUserIdentifier()
    {
        if (null === ($userIdentifier = $this->sessionVariable('ApplicationUserIdentifier'))) {
            $userIdentifier = $this->get('request')->get('ApplicationUserIdentifier', '');
            $this->sessionVariable('ApplicationUserIdentifier', $userIdentifier);
        }

        return $userIdentifier;
    }

    /**
     * Get service token string
     * @return string
     */
    protected function getServiceToken()
    {
        if (null === ($serviceToken = $this->sessionVariable('ServiceToken'))) {
            $serviceToken = $this->get('request')->get('ServiceToken', static::SERVICE_TOKEN);
            $this->sessionVariable('ServiceToken', $serviceToken);
        }

        return $serviceToken;
    }

    /**
     * Get country string
     * @return string
     */
    protected function getServiceCountry()
    {
        if (null === ($serviceCountryCode = $this->sessionVariable('ServiceCountryCode'))) {
            $serviceCountryCode = $this->get('request')->get('ServiceCountryCode', 'DE');
            $this->sessionVariable('ServiceCountryCode', $serviceCountryCode);
        }

        return $serviceCountryCode;
    }

    /**
     * Set or get session variable
     * @param string $varname
     * @param mixed $value
     * @return mixed
     */
    protected function sessionVariable($varname, $value = null)
    {
        if (!$this->has('session')) {
            return null;
        }
        $session = $this->get('session');
        if (2 == func_num_args()) {
            $session->set($varname, $value);
        }

        return $session->has($varname) ? $session->get($varname) : null;
    }

    /**
     * @param array $params
     * @param string $prefix
     * @return string
     */
    protected function xmlBuildUserIdentifier($params, $prefix = '')
    {
        $xml = [];
        $keys = ['Identifier', 'Username', 'Email', 'Guid'];
        foreach ($keys as $key) {
            $idx = $prefix . $key;
            if (!empty($params[$idx])) {
                $xml[] = '<' . $key . '>' . $params[$idx] . '</' . $key . '>';
            }
        }

        return implode("\n", $xml);
    }

    /**
     * Parse xml string
     * @param string $response
     * @return string Formatted xml string
     */
    protected function formatXml($response)
    {
        libxml_clear_errors();
        libxml_disable_entity_loader(true);
        libxml_use_internal_errors(true);
        $xml = new \DOMDocument('UTF-8', LIBXML_NONET);
        $xml->preserveWhiteSpace = false;
        if (true === @$xml->loadXML($response, LIBXML_DTDLOAD | LIBXML_DTDATTR)) {
            $xml->formatOutput = true;
            libxml_clear_errors();
            libxml_disable_entity_loader(false);

            return $xml->saveXml();
        }
        libxml_clear_errors();
        libxml_disable_entity_loader(false);

        return $response;
    }

    /**
     * @param string $formData
     * @param string $controller
     * @param string $action
     * @param integer $version
     * @return array
     */
    protected function runAction($formData, $controller, $action, $version = 1)
    {
        $method = 'xml' . $controller . $action;

        return $this->callWebservice($this->$method($formData), $controller, $action, $version);
    }

    /**
     * Send request to webservice
     * @param string $xmlRequest
     * @param string $controller
     * @param string $action
     * @return array
     * @throws DemoException
     */
    protected function callWebservice($xmlRequest, $controller, $action, $version = 1)
    {
        $request = $this->get('request');
        // check header
        $httpsTrue = $request->headers->get('x-forwarded-proto');
        if (!empty($httpsTrue)) {
            $url = $httpsTrue . "://" . $request->getHost() . $this->generateUrl('_sso_webservice_service');
        } else {
            $url = $request->getScheme() . "://" . $request->getHost() . $this->generateUrl('_sso_webservice_service');
        }

        $headers = array(
            'API-VERSION: ' . $version,
            'API-CONTROLLER: ' . $controller . '',
            'API-ACTION: ' . $action . '',
            'Content-Type: text/xml',
        );

        $connection = curl_init();
        curl_setopt($connection, CURLOPT_URL, $url);
        curl_setopt($connection, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($connection, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($connection, CURLOPT_HEADER, 1);
        curl_setopt($connection, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($connection, CURLOPT_POST, 1);
        curl_setopt($connection, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($connection, CURLOPT_TIMEOUT, 200);
        curl_setopt($connection, CURLOPT_ENCODING, "");

        curl_setopt($connection, CURLOPT_POSTFIELDS, $xmlRequest);
        $response = curl_exec($connection);
        $c_error = curl_error($connection);
        if ($c_error) {
            $c_error_no = curl_errno($connection);
            throw new DemoException("Server Error " . $c_error_no . ' : ' . $c_error);
        }
        $responseHeaderSize = curl_getinfo($connection, CURLINFO_HEADER_SIZE);
        curl_close($connection);

        $responseHeaders = substr($response, 0, $responseHeaderSize);
        $responseXml = substr($response, $responseHeaderSize);
        $this->urlProfiler = $this->getProfilerLink($responseHeaders);

        return array($this->formatXml($xmlRequest), $this->formatXml($responseXml));
    }

    /**
     * Pull out profiler link from response header
     * @param string $headers
     * @return string Link to profiler
     */
    protected function getProfilerLink($headers)
    {
        return preg_match('/X-Debug-Token-Link:\s*([^\s^\n]*)/i', $headers, $matches) ? $matches[1] : '';
    }

    /**
     * Generate response for template
     * @param \Symfony\Component\Form\Form $form
     * @param \Symfony\Component\HttpFoundation\Request $request
     * @param string $controller
     * @param string $action
     * @return array
     */
    protected function getResponseFromForm($form, $request, $controller, $action, $stressMe = false, $resetForm = false)
    {
        $cloneForm = clone $form;
        $form->handleRequest($request);
        $formData = $form->getData();
        if (false !== $stressMe) {
            list($xmlRequest, $xmlResponse) = $form->isValid() ? $this->$stressMe($formData, $controller,
                $action) : ['', ''];
        } else {
            list($xmlRequest, $xmlResponse) = $form->isValid() ? $this->runAction($formData, $controller, $action,
                $form->has('apiVersion') ? $form->get('apiVersion')->getNormData() : 1) : ['', ''];
        }

        // Save some values
        if (isset($formData['ServiceToken'])) {
            $this->sessionVariable('ServiceToken', $formData['ServiceToken']);
        }
        if (isset($formData['UserIdentifier'])) {
            $this->sessionVariable('UserIdentifier', $formData['UserIdentifier']);
        }
        if (isset($formData['UserUsername'])) {
            $this->sessionVariable('UserUsername', $formData['UserUsername']);
        }
        if (isset($formData['ApplicationUserIdentifier'])) {
            $this->sessionVariable('ApplicationUserIdentifier', $formData['ApplicationUserIdentifier']);
        }
        if (isset($formData['ApplicationUsername'])) {
            $this->sessionVariable('ApplicationUsername', $formData['ApplicationUsername']);
        }
        if (isset($formData['ServiceCountryCode'])) {
            $this->sessionVariable('ServiceCountryCode', $formData['ServiceCountryCode']);
        }
        if ($resetForm) {
            return array(
                'wsRequest' => $xmlRequest,
                'wsResponse' => $xmlResponse,
                'wsProfiler' => $this->urlProfiler,
                'form' => $cloneForm->createView(),
            );
        } else {
            return array(
                'wsRequest' => $xmlRequest,
                'wsResponse' => $xmlResponse,
                'wsProfiler' => $this->urlProfiler,
                'form' => $form->createView(),
            );
        }

    }

    /**
     * Build xml request credentials
     * @param array $params
     * @return string
     */
    protected function xmlCredentials($params)
    {
        // More details for tokens with restricted access
        $extendedCredentials = isset($params['ApplicationUsername']) || isset($params['ApplicationUserIdentifier']) ? "
                <ApplicationUser>
                    <Username>" . $params['ApplicationUsername'] . "</Username>
                    <UserIdentifier>" . $params['ApplicationUserIdentifier'] . "</UserIdentifier>
                </ApplicationUser>
        " : "";

        return "<Credentials>
                <ServiceProvider>
                    <ServiceToken>" . $params['ServiceToken'] . "</ServiceToken>
                    <ServiceName>MyService</ServiceName>
                </ServiceProvider>
                <ServiceTrigger>
                    <Username>" . $params['UserUsername'] . "</Username>
                    <UserIdentifier>" . $params['UserIdentifier'] . "</UserIdentifier>
                </ServiceTrigger>
                $extendedCredentials
                <ServiceCountry>
                    <Code>" . $params['ServiceCountry'] . "</Code>
                </ServiceCountry>
            </Credentials>";
    }

    /**
     * @param array $params
     * @return string
     */
    protected function xmlAdvancedUserSearchHelper($params)
    {
        //it is getting nasty here sorry if it hurts your eyes!
        $result = '<Offset>' . ($params['Offset'] ? $params['Offset'] : 0) . '</Offset>';
        $result .= "<Limit>" . ($params['Limit'] ? $params['Limit'] : 100) . "</Limit>";
        $result .= "<OrderBy>" . ($params['OrderBy'] ? $params['OrderBy'] : '') . "</OrderBy>";
        $result .= "<OrderDir>" . ($params['OrderDir'] ? $params['OrderDir'] : '') . "</OrderDir>";
        $result .= "<FullResponse>" . ($params['FullResponse'] ? $params['FullResponse'] : 0) . "</FullResponse>";

        $result .= "<Filters>";
        //ok now add the user type if not empty
        if (isset($params['UserType'])) {
            foreach ($params['UserType'] as $userType) {
                $result .= "<Filter>";
                $result .= $userType['Operator'] ? "<Operator>" . $userType['Operator'] . "</Operator>" : '';
                $result .= $userType['Strategy'] ? "<Strategy>" . $userType['Strategy'] . "</Strategy>" : '';
                $result .= "<UserType>";
                $result .= $userType['UserTypeGuid'] ? "<Guid>" . $userType['UserTypeGuid'] . "</Guid>" : '';
                $result .= $userType['UserTypeEmail'] ? "<Email>" . $userType['UserTypeEmail'] . "</Email>" : '';
                $result .= $userType['UserTypeUsername'] ? "<Username>" . $userType['UserTypeUsername'] . "</Username>" : '';
                $result .= $userType['UserTypeFirstname'] ? "<Firstname>" . $userType['UserTypeFirstname'] . "</Firstname>" : '';
                $result .= $userType['UserTypeLastname'] ? "<Lastname>" . $userType['UserTypeLastname'] . "</Lastname>" : '';
                $result .= $userType['UserTypeActive'] ? "<Active>" . $userType['UserTypeActive'] . "</Active>" : '';
                $result .= $userType['UserTypeDeleted'] ? "<Deleted>" . $userType['UserTypeDeleted'] . "</Deleted>" : '';
                $result .= $userType['UserTypeMfaEnabled'] ? "<MfaEnabled>" . $userType['UserTypeMfaEnabled'] . "</MfaEnabled>" : '';
                $result .= $userType['UserTypeCreatedAt'] ? "<CreatedAt>" . $userType['UserTypeCreatedAt']->format(\DateTime::W3C) . "</CreatedAt>" : '';
                $result .= $userType['UserTypeUpdatedAt'] ? "<CreatedAt>" . $userType['UserTypeUpdatedAt']->format(\DateTime::W3C) . "</CreatedAt>" : '';
                $result .= "</UserType>";
                $result .= "</Filter>";
            }
        }
        if (isset($params['AddFilters'])) {
            $result .= $params['AddFilters'];
        }
        $result .= "</Filters>";

        return $result;
    }
}
