<?php

namespace hosting\ispConfig;

abstract class AbstractSoapClient implements SoapClientInterface {

    /**
     *
     * @var string Url to ISP configs SOAP remote API ( usually https://your-ip:8080/remote/index.php )
     */
    protected $ispConfigSoapLocation = '';

    /**
     *
     * @var string Username to login to ISPConfig with ( Create one in ISPConfigs web interface under System > Remote Users )
     */
    protected $ispConfigUser = '';

    /**
     *
     * @var string Password to login to ISPConfig with ( Create/Update in ISPConfigs web interface under System > Remote Users )
     */
    protected $ispConfigPassword = '';

    /**
     *
     * @var \SoapClient Refernce to the SOAP interface
     */
    public $soapClient;

    /**
     *
     * @var string Session ID generated by logging in and used in subsequent requests
     */
    public $sessionId;

    /**
     *
     * @var array Cache of client IDs
     */
    public $clientIds = array();

    /**
     *
     * @var array Cache of clients
     */
    public $clients = array();

    /**
     *
     * Holds the last exception encounted
     *
     * @var \Exception
     */
    protected $lastException;

    /**
     * The server ID to run these commands on
     * @var int
     */
    protected $serverId;

    /**
     *
     * Returns the current Session Id
     *
     * @return string
     * @throws Exception
     */
    public function getSessionId() {
        if (!$this->sessionId) {
            throw new Exception("Tried to call method when not logged in");
        }
        return $this->sessionId;
    }

    /**
     *
     * Returns the last exception encounted
     *
     * @return \Exception
     */
    public function getLastException() {
        return $this->lastException ? : new \Exception("");
    }

    /**
     *
     * Logs in to ISPConfig creating a SoapClient and retrieves a Session Id
     *
     * @param string $ispConfigSoapLocation Url to ISP configs SOAP remote API ( usually https://your-ip:8080/remote/index.php )
     * @param string $ispConfigUser Username to login to ISPConfig with ( Create one in ISPConfigs web interface under System > Remote Users )
     * @param string $ispConfigPassword Password to login to ISPConfig with ( Create/Update in ISPConfigs web interface under System > Remote Users )
     */
    public function __construct($ispConfigSoapLocation, $ispConfigUser, $ispConfigPassword,$context=null) {
        $this->ispConfigUser         = $ispConfigUser;
        $this->ispConfigPassword     = $ispConfigPassword;
        $this->ispConfigSoapLocation = $ispConfigSoapLocation;
        $this->soapClient            = new \SoapClient(null, array(
            'location' => $this->ispConfigSoapLocation,
            'uri'      => $this->ispConfigSoapLocation,
            'stream_context' => $context
        ));
        $this->sessionId             = $this->login($this->ispConfigUser, $this->ispConfigPassword);
        if (!$this->sessionId) {
            throw new \Exception("Login failed: " . ($this->lastException ? $this->lastException->getMessage() : '' ));
        }
    }

    abstract public function login($username, $password);

    abstract public function logout();

    public function getAllClients($update = false) {
        if (empty($this->clients) || $update) {
            $clientIds = $this->clientGetAll();
            if ($clientIds){
                // wait for 2 seconds
                usleep(100000);
                foreach ($clientIds as $i => $clientId) {
                    if ($i % 20 == 0) {
                        usleep(100000);
                    }
                    $client = $this->clientGet($clientId);
                    if ($client !== false) {
                        $this->clients[] = $client;
                    } else {
                        echo "$clientId failed!<br/>";
                    }
                }
            }
        }
        return $this->clients;
    }

    public function userNameExits($usernamesToCheck) {
        $args = func_get_args();
        if (count($args > 1)) {
            $usernamesToCheck = $args[1];
        }

        $usernamesToCheck = is_array($usernamesToCheck) ? $usernamesToCheck : array($usernamesToCheck);
        $result           = array();
        foreach ($usernamesToCheck as $username) {
            $result[] = UsernameStatus::create($username, $this->clientGetByUsername($username) ? 1 : 0);
        }
        return $result;
    }

    public function getAllGroupIds() {
        $clientIds  = $this->getAllClientIds();
        $groupIds   = array();
        $groupIds[] = 0;
        foreach ($clientIds as $clientId) {
            $groupIds[] = $this->getClientGroupId($clientId);
        }
        return $groupIds;
    }

    /**
     *
     * @param type $domainsToCheck
     * @return DomainStatus[]
     */
    public function domainExists($domainsToCheck) {
        $domainsToCheck  = is_array($domainsToCheck) ? $domainsToCheck : array($domainsToCheck);
        $reservedDomains = array("test.gdmedia.tv");
        $domains         = array_merge($reservedDomains, array_map(function($s) {
                    return $s['domain'];
                }, $this->getSites()
                ), $this->getAllClients()
        );
        $result = array();
        foreach ($domainsToCheck as $domain) {
            $domain   = strtolower(trim($domain));
            $result[] = DomainStatus::create($domain, in_array($domain, $domains) ? 1 : 0);
        }
        return $result;
    }

    /**
     * Calls the remote SOAP call
     * Will pass from the second params and any after directly to the remote call
     *
     * @param type $function <p>The function name to call</p>
     * @param type $param1 <p>First remote param</p>
     * @param type $param2 <p>Second remote param</p>
     * @return mixed The result of the remote function or false on failure. If an exception occurs false is returned and the exception can queried by getLastException.
     */
    public function makeCall($function) {
        $args = func_get_args();
        array_shift($args);
        try {
            $result = call_user_func_array(array($this->soapClient, $function), $args);
        }
        catch (\Exception $exc) {
            $result              = false;
            $this->lastException = $exc;
        }
        return $result;
    }

    public function createSite($serverId, $domain, $dbname, $dbuser, $dbpass = null) {
        if (!is_int($serverId)) {
            $server = $this->serverGetServeridByName($serverId);
            if (isset($server[0]['server_id'])) {
                $serverId = $server[0]['server_id'];
            } else {
                throw new \InvalidArgumentException("Unable to find the server " . $serverId);
            }
        }

        return array();
    }

}
