<?php

namespace ThurData\Servers\KerioEmail\App\Libs\Kerio\Components\Api\Soap;


use ThurData\Servers\KerioEmail\App\Libs\Kerio\Components\Api\Interfaces\ConnectionInterface;
use ThurData\Servers\KerioEmail\App\Libs\Kerio\Components\Api\Soap\Helpers\XmlParser;
use \SoapFault;
use \SoapHeader;
use \SoapParam;
use \SoapVar;

/**
 *
 * Created by PhpStorm.
 * User: ThurData
 * Date: 28.08.19
 * Time: 13:47
 * Class Connection
 */
class Connection implements ConnectionInterface
{
    /**
     * uri
     */
    const URI_ADMIN   = 'urn:kerioAdmin';
    const URI_ACCOUNT = 'urn:kerioAccount';

    /**
     * protocol
     */
    const HTTPS_PROTOCOL = 'https://';

    /**
     * path
     */
    const ADMIN_PATH        = '/admin/api/jsonrpc/';
    const ACCOUNT_PATH      = '/webmail/api/jsonrpc/';

    /**
     * ports
     */
    const CLIENT_PORT = '443';

    /**
     * @var MySoapClient
     */
    protected $soapClient;
    /**
     * @var mixed
     */
    protected $params;
    /**
     * @var
     */
    protected $header;
    /**
     * @var
     */
    protected $responseModel;

    /**
     * @var bool
     */
    protected $connected = false;

    /**
     * @var null
     */
    protected $connectionError = null;

    /**
     * @var string
     */
    protected $authToken    = '';

    /**
     * @var string
     */
    protected $serverUrl = '';

    protected $server;
    protected $port;

    /**
     * Connection constructor.
     * @param $server
     * @param int $port
     * @param $username
     * @param $password
     * @param string $user
     * @throws SoapFault
     */
    public function __construct($server, $port = 4040, $username, $password = null, $user = "admin", $authToken = null , $preauth = null)
    {
        /**
         * set params
         */
        $this->server = $server;
        $this->port = $port;

        /**
         *
         * get login credential by type
         */
        list($location, $uri, $params) = $this->getLoginDetails($server, $port, $username, $password, $user,$authToken, $preauth);

        /**
         *
         * create new soap client with data
         */
        $this->soapClient = new \ThurData\Servers\KerioEmail\App\Libs\Kerio\Components\Api\Soap\MySoapClient(null,
            [
                'location'       => $location,
                'uri'            => $uri,
                'trace'          => 1,
                'exceptions'     => 1,
                'soap_version'   => SOAP_1_2,
                'style'          => SOAP_RPC,
                'use'            => SOAP_LITERAL,
                'stream_context' => stream_context_create(
                    [
                        'ssl' => [
                            'verify_peer'      => false,
                            'verify_peer_name' => false,
                        ]])
            ]
        );

        /**
         * set credential params
         */
        $this->params = $params;
    }

    /**
     * @param $server
     * @param int $port
     * @param $username
     * @param $password
     * @param $user
     * @return array
     */
    protected function getLoginDetails($server, $port, $username, $password, $user, $authToken = null, $preauth = null)
    {
        $this->serverUrl    = self::HTTPS_PROTOCOL . $server . ":" . $port;

        /**
         *
         * admin details
         */
        if ($user == "admin")
        {
            $location = self::HTTPS_PROTOCOL . $server . ":" . $port . self::ADMIN_PATH;

            $uri = self::URI_ADMIN;

            $params = [
                new SoapParam($username, "name"),
                new SoapParam($password, "password"),
            ];
        }

        /**
         *
         * user details
         */
        if ($user == "user")
        {
            $location = self::HTTPS_PROTOCOL . $server . ":" . ($port ? $port : self::CLIENT_PORT)  . self::ACCOUNT_PATH;
            $uri = self::URI_ACCOUNT;
            $params = [
                new SoapVar('<ns1:account by="name">' . $username . '</ns1:account>', XSD_ANYXML),
            ];

            if($authToken)
            {
                $params[] =  new SoapParam($authToken, "authToken");
            }else{
                $params[] =  new SoapParam($password, "password");
            }

        }

        /**
         *
         * return data
         */
        return [$location, $uri, $params];
    }
    
    /**
     *
     *
     * @return \Exception|mixed|SoapFault|null
     */
    public function login()
    {
        try
        {
            /**
             * set headers
             */
            $this->setSoapHeader();

            /**
             *
             * authentication request
             */
            $result = $this->soapClient->__soapCall("AuthRequest", $this->params, null, $this->getSoapHeader());

            /**
             *
             * Save the soapHeader with token
             */
            $this->authToken    = $result['authToken'];
            $this->setSoapHeader($this->authToken);

            /**
             *
             * set connected as true
             */
            $this->setConnected(true);
        }
        catch (SoapFault $exception)
        {
            $result = $exception;
            /**
             * set connected as false
             */
            $this->setConnected(false);
            $this->setConnectionError($exception->getMessage());
        }

        return $result;
    }

    /**
     * @return mixed
     */
    function getSoapHeader()
    {
        return $this->header;
    }

    /**
     * @param null $authToken
     */
    function setSoapHeader($authToken = null)
    {
        if (!$authToken)
        {
            $this->header = new SoapHeader('urn:kerio', 'context');
        }
        else
        {
            $this->header = [
                new SoapHeader(
                    'urn:kerio',
                    'context',
                    new SoapVar('<ns2:context><ns2:authToken>' . $authToken . '</ns2:authToken></ns2:context>', XSD_ANYXML)
                )
            ];
        }
    }

    /**
     * @param $request
     * @param array $params
     * @param array $options
     * @return Response
     */
    public function request($request, $params = [], $options = [])
    {
        /**
         * headers
         */
        $soapHeader = $this->getSoapHeader();
        /**
         * response model
         */
        $response = $this->getResponseModel();

        /**
         * main request
         */
        try
        {

            /**
             *
             * request to api
             */
            $this->soapClient->__soapCall(
                $request,
                $params,
                $options,
                $soapHeader
            );

        }
        catch (\SoapFault $ex)
        {
            $response->setLastError($ex->getMessage());
        }
        catch (\Exception $ex)
        {
            $response->setLastError($ex->getMessage());
        }

        /**
         * Kerio api response
         */
        $soapRes = $this->soapClient->__getLastResponse();

        /**
         *
         * set response model params
         */
        $response
            ->setRequest($request)
            ->setParams($params)
            ->setOptions($options)
            ->setHeaders($soapHeader)
            ->setXmlResponse($soapRes)
            ->response();

        return $response;
    }

    /**
     * @param $response
     */
    public function setResponseModel($response)
    {
        $this->responseModel = $response;
    }

    /**
     * @return Response
     */
    public function getResponseModel()
    {
        if (!$this->responseModel)
        {
            $this->responseModel = new Response();
        }

        return $this->responseModel;
    }

    /**
     * @return $this
     */
    public function cleanResponse()
    {
        $this->responseModel = null;
        return $this;
    }

    /**
     * @return bool
     */
    public function isConnected()
    {
        return $this->connected;
    }

    /**
     * @param $connected
     * @return $this
     */
    public function setConnected($connected)
    {
        $this->connected = $connected;
        return $this;
    }

    /**
     * @return null
     */
    public function getConnectionError()
    {
        return $this->connectionError;
    }

    /**
     * @param $connectionError
     * @return $this
     */
    public function setConnectionError($connectionError)
    {
        $this->connectionError = $connectionError;
        return $this;
    }

    /**
     * @return string
     */
    public function getAuthToken()
    {
        return $this->authToken;
    }

    /**
     * @return string
     */
    public function getServerUrl()
    {
        return $this->serverUrl;
    }
}