<?php

/**
 * Backend API Reality.cz.
 *
 * @category   Dalten
 * @package	   Export
 * @subpackage Api
 */
class Dalten_Export_Api_Backend_RealityCz implements Dalten_Export_Api_Backend_LoggableBackendInterface
{
	/**
	 * Seznam adpatérů každého účtu pro volání metod přes SOAP.
	 *
	 * @var SoapClient
	 */
	protected $_client = null;

	/**
	 * Držák spojení.
	 *
	 * @var string
	 */
	protected $_connectionHandle = null;

	/**
	 * Konfigurovatelné nastavení specifické pro export.
	 *
	 * @var Dalten_Data_ArrayObject
	 */
	private $_config;

	private $_addressConverter;

	/**
	 * Držák pro username, podle kterého se vyrábí číslo zakázky.
	 *
	 * @var string
	 */
	private $_userName = '';

	/**
	 * Instance loggeru komunikace se serverem.
	 *
	 * @var Dalten_Export_Api_Backend_Logger_LoggerInterface
	 */
	protected $_logger;

	/**
	 * Iniciační hodnoty pro SOAP.
	 *
	 * @var array
	 */
	private $_soapInitialParams = array(
		'trace' => 1,
		'exceptions' => 1,
		'encoding' => 'UTF-8'
	);

	/**
	 * Seznam parametrů a jejich určení pořadí pro zápis do xml.
	 *
	 * @var array
	 */
	private static $_paramsOrder = array(
		'vnSignature',
		'vbExkluzivita',
		'vnMistoKU',
		'vnMistoOkres',
		'vnMistoObec',
		'vnDruhNemovitosti0',
		'vnTypKod',
		'vnVhodnostKPodnikani',
		'vnCharakteristika',
		'vsNazev',
		'vsPoznamka',
		'vsOptionalParameters',
		'vsTranslations'
	);

	private static $_stavyNabidky = array(
		0 => 'Vloženo: čeká se na ověření.',
		1 => 'Zpracováno: poloha ověřena, nabídka zpracována.',
		10=> 'Odstraněno.',
		11=> 'Aktivní.',
		13=> 'Rezervace.',
		14=> 'Prodáno/vydraženo.',
		15=> 'Pronajato.',
		20=> 'Pozastaveno - připraveno k odstranění.',
		21=> 'Pozastaveno.',
		23=> 'Pozastaveno - rezervace.',
		24=> 'Pozastaveno - prodáno/vydraženo.',
		25=> 'Pozastaveno – pronajato.',
		8 => 'Chybná poloha: vložené údaje nebyly správné, nabídka čeká na opravu polohy.',
		99=> 'Otevřeno k editaci.'
	);

	/**
	 * Konstruktor nastavuje a kontroluje soap config.
	 *
	 * @param Dalten_Data_ArrayObject $soapConfig Nastavení SOAPU.
	 */
	public function __construct(\Dalten_Data_ArrayObject $soapConfig)
	{
		$this->_config = $soapConfig;
	}

	/**
	 * Zkontroluje vzdálený server a provede přihlášení.
	 *
	 * @param string $user Přihlašovací jméno ke vzdálenému připojení.
	 * @param string $pass Heslo ke vzdálenému připojení.
	 *
	 * @return Dalten_Export_Api_ServerResponse Odpověď serveru.
	 */
	public function login($user, $pass)
	{
		$this->_client = new \SoapClient($this->_config->wsdl, $this->_soapInitialParams);

		$this->_userName = $user;

		$params = array(
			'vsProgram' => $this->_config->channel,
			'vsVersion' => $this->_config->version,
		);

		// test konektivity
		try {
			$this->_call('Ping', array('vsTestString' => 'bryden'));
		} catch (\Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			return new Dalten_Export_Api_ServerResponse(false, 500, 'Vzdálený server neodpovídá.');
		}

		// ziskani výzvy pro otp
		try {
			$response = $this->_call('Connect', $params);
		} catch (\Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			return new Dalten_Export_Api_ServerResponse(
				false,
				403,
				'Nepodařilo se přihlásit (Connect) na vzdálený server.'
			);
		}

		$params = array();
		$params['vsUserName'] = $user;
		$params['vsPassword'] = \md5($pass);
		$params['vsProgram'] = $this->_config->channel;
		$params['vsVersion'] = $this->_config->version;
		// otp
		$params['vsResponse'] = self::otp($response->ConnectResult, $this->_config->channelPass);

		// prihlaseni
		try {
			$response = $this->_call('Logon', $params);
		} catch (\Dalten_Export_Api_Backend_Exception_ResponseError $e) {

			$res = array(
				$this->_client->__getLastRequest(),
				$this->_client->__getLastResponse()
			);

			echo $e->getMessage() . PHP_EOL . $e->getTraceAsString() . PHP_EOL;

			return new Dalten_Export_Api_ServerResponse(
				false,
				403,
				'Nepodařilo se přihlásit (Logon) na vzdálený server.'
			);


		}

		$res = array(
			$this->_client->__getLastRequest(),
			$this->_client->__getLastResponse()
		);

		// uschovame drzak spojeni
		$this->_connectionHandle = $response->LogonResult;
		//		$this->_connectionHandle = $response;

		return new Dalten_Export_Api_ServerResponse(true, 200, 'Přihlášení proběhlo v pořádku.');
	}

	/**
	 * Odešle inzerát na server
	 *
	 * @param array $advertData       Převedené informace o nemovitosti.
	 * @param array $userData         Informace o makléři (nepoužívají se).
	 * @param array $images           Pole obrázku k nemovitosti.
	 * @param array $additionalParams Další informace.
	 *
	 * @return Dalten_Export_Api_ServerResponse Odpověď serveru.
	 */
	public function sendEstate(array $advertData, array $userData, array $images, array $additionalParams)
	{
		$cadastre = array($advertData['vnMistoKU'], $advertData['vnMistoObec'], $advertData['vnMistoOkres']);
		$required = array_filter(
			$cadastre,
			function($item) {
				return !empty($item);
			}
		);

		if (
			empty($advertData['vsOptionalParameters']['RUIAN_ADRESA_KOD']) &&
			empty($advertData['vsOptionalParameters']['RUIAN_PARCELA_KOD']) &&
			count($required) < 3
		) {
			return new Dalten_Export_Api_ServerResponse(
				false,
				500,
				'Chybí určení části obce u adresy nemovitosti.
				(Pokud je část obce vyplněná, prosím, přeuložte celou nabídku a zadejte export znovu.)'
			);
		}

		// přidáme uživatele, pokud ho máme
		if (!empty($userData['realityid'])) {
			$advertData['vsOptionalParameters']['MAKLER_ID'] = strval($userData['realityid']);
			$advertData['vsOptionalParameters']['MAKLER_UKAZ'] = 1;
		}

		$params = $this->_getSoapParams($advertData);

		// odesleme nabidku
		try {
			$this->_call('SendEstate', $params);
		} catch (\Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			if (stripos('exkluziv', $e->getMessage())) {
				// Když si bude stěžovat na exluzivitu, dáme mu DATUM_2 0000-00-00
				$params['vsOptionalParameters']['DATUM_2'] = '0000-00-00';
				try {
					$this->_call('SendEstate', $params);
				} catch (\Dalten_Export_Api_Backend_Exception_ResponseError $e) {
					return new Dalten_Export_Api_ServerResponse(false, 500, $e->getMessage());
				}
			} else {
				return new Dalten_Export_Api_ServerResponse(false, 500, $e->getMessage());
			}
		}

        $tooMuchPhotos = false;
        if (count($images) >= 30) {
            $images = array_slice($images, 0, 30);
            $tooMuchPhotos = true;
        }
		// ted pridame fotky

		// ale nejdrive vsechny smazem
		try {
			$params = $this->_getBaseSoapParams($advertData);
			$params['viPictureOrder'] = 99; // smazem vsechny
			$this->_call('DeleteEstatePicture', $params);
		} catch (\Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			return new Dalten_Export_Api_ServerResponse(false, 500, $e->getMessage());
		}

		// ted je jednu po druhe pridame.
		$i = 1;
		foreach ($images as $image) {
			if (file_exists($image['soubor'])) {

				try {

					$params = $this->_getBaseSoapParams($advertData);
					$params['viPictureOrder'] = $i;
					$params['voPicture'] = file_get_contents($image['soubor']);
					$params['vsDescription'] = $image['popis'];

					$this->_call('SendEstatePicture', $params);

					$i++;

				} catch (\Dalten_Export_Api_Backend_Exception_ResponseError $e) {
					return new Dalten_Export_Api_ServerResponse(false, 500, $e->getMessage());
				}
			}
		}

		// a nakonec vse pustime ke zpracovani
		try {
			$response = $this->_call('ProcessEstate', array('vsConnectionHandle' => $this->_connectionHandle));
		} catch (\Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			return new Dalten_Export_Api_ServerResponse(false, 500, $e->getMessage());
		}

		// vysledek poslem jako hlasku
		$message = $this->_parseSoapMessage($response->ProcessEstateResult);
		if ($tooMuchPhotos) {
		    $message .= PHP_EOL . 'Počet fotografií byl omezen na max. počet podporovaný serverem reality.cz = 30.';
        }

		$ret = new Dalten_Export_Api_ServerResponseVerifiable(true, 200, $message);
		$ret->setVerificationStatus(false);
        if ($tooMuchPhotos) {
            $ret->setData(array('importNotice'=>'Počet fotografií byl omezen na max. počet podporovaný serverem reality.cz = 30.'));
        }

		return $ret;
	}

	/**
	 * Pozastaví nebo smaže nabídku na serveru.
	 *
	 * @param array $advertData        Informace o nabídce.
     * @param bool  $isPermanentDelete Chceme nabídku smazat trvale?
	 *
	 * @return Dalten_Export_Api_ServerResponse|Dalten_Export_Api_ServerResponseVerifiable
	 */
	public function deleteEstate(array $advertData, $isPermanentDelete = false)
	{
		$params = $this->_getBaseSoapParams($advertData);

		try {
            if ($isPermanentDelete) {
                $response = $this->_call('DeleteEstateSimple', $params);
            } else {
                $response = $this->_call('SuspendEstateSimple', $params);
            }
		} catch (\Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			return new Dalten_Export_Api_ServerResponse(false, 500, $e->getMessage());
		}
		try {
			$response = $this->_call('ProcessEstate', array('vsConnectionHandle' => $this->_connectionHandle));
		} catch (\Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			return new Dalten_Export_Api_ServerResponse(false, 500, $e->getMessage());
		}

        if ($isPermanentDelete) {
            $staticMessage = 'Nabídka byla trvale smazána. Upozorňujeme, že pokud chcete nabídku obnovit, musíte udělat kopii nabídky.';
        } else {
            $staticMessage = 'Nabídka byla pozastavena. Pro trvalé smazání nabídky změňte stav nabídky na Realizováno nebo Zrušeno a znovu zadajte požadavek Smazat na Reality.cz.';
        }

		return new Dalten_Export_Api_ServerResponseVerifiable(true, 200, $staticMessage);
	}

    /**
     * Zkontroluje zda už byla nabídka schválena o ověřena na jejich serveru.
     *
     * @param array $advertData       Informace o nemovitosti, kterou chceme prověřit.
     * @param array $additionalParams Další informace z exportu.
     *
     * @return Dalten_Export_Api_ServerResponse|Dalten_Export_Api_ServerResponseVerifiable Zda
     *         server či jakém stavu nabídka aktuálně je.
     * @throws Exception
     */
	function checkEstate(array $advertData, array $additionalParams)
	{
		$params = $this->_getBaseSoapParams($advertData);
		try {
			$response = $this->_call('CheckEstate', $params);
		} catch (\Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			return new Dalten_Export_Api_ServerResponse(false);
		}

		$responseNumber = $response->CheckEstateResult;
		$responseMessage = (
			isset(self::$_stavyNabidky[$responseNumber]) ?
				self::$_stavyNabidky[$responseNumber] :
				sprintf('Neznámý stav nabídky (č. %d)!', $responseNumber)
		);

		// 0 = čeká na ověření
		$verificationInProgress = ($responseNumber==0 ? true : false);
		// 8 = selhalo ověření adresy
		$isVerified = ($responseNumber==8 ? false : true);

		if ($additionalParams && !empty($additionalParams['importNotice'])) {
		    $responseMessage .= PHP_EOL . $additionalParams['importNotice'];
        }

		$ret = new Dalten_Export_Api_ServerResponseVerifiable(true, $responseNumber, $responseMessage);
		$ret->setVerificationStatus(false);
		if (!$verificationInProgress) {
			$ret->setVerificationStatus(true, $isVerified);
		}
		$ret->setData(array('detailUrl' => 'http://www.reality.cz/' . $params['vsCisloZakazky']  .'/?src=1'));
		return $ret;
	}


    public function topListing($data)
    {
       return new Dalten_Export_Api_ServerResponseVerifiable(false, 500, 'Tato verze exportu na Reality.cz nepodporuje topování!');
    }

	/**
	 * Srovná parametry pro volání soap funkcí podle správného pořadí. (To co vrací config není spolehlivé).
	 *
	 * @param array $data Zkonvertěná data nabídky.
	 *
	 * @return array
	 */
	protected function _getSoapParams(array $data)
	{
		$params = $this->_getBaseSoapParams($data);
		foreach (self::$_paramsOrder as $paramName) {
			if (isset($data[$paramName])) {
				$params[$paramName] = $data[$paramName];
			}
		}

		// vsOptionalParameters je string v podobě níže seskládaného XML.
		if (isset($params['vsOptionalParameters'])) {
			$optionals = '<optionalParameters>' . PHP_EOL;
			foreach ($params['vsOptionalParameters'] as $name => $val) {
				if (is_array($val)) {
					$val = implode(', ', $val);
				}
				$optionals .= '<param name="' . $name . '">' .
					htmlspecialchars($val, ENT_NOQUOTES) .
					'</param>' . PHP_EOL;
			}
			$optionals .= '</optionalParameters>' . PHP_EOL;
			$params['vsOptionalParameters'] = $optionals;
		}

		return $params;
	}

	/**
	 * Vrácí základní soap parametry vsConnectionHandle a vsCisloZakazky
	 *
	 * @param array $data Zkonvertěná data nabídky.
	 *
	 * @return array
	 */
	protected function _getBaseSoapParams(array $data)
	{
		return array(
			'vsConnectionHandle' => $this->_connectionHandle,
			'vsCisloZakazky' => $this->_getAdvertCode($data)
		);
	}

	/**
	 * Vrátí zpracovaný kód zakázky z listing dat.
	 *
	 * @param array $data Zkonvertěná data nabídky.
	 *
	 * @return string
	 */
	private function _getAdvertCode(array $data)
	{
		$code = '';
		if (empty($code)) {
			$code = $this->_userName . '-' . $data['vsCisloZakazky'];
		}

		return $code;
	}

	/**
	 * Vytvoří jednorázové heslo z výzvy vrácené voláním Connect.
	 *
	 * @param string $result Výsledek volání Connect.
	 * @param string $pass   Heslo přidělené pro projekt.
	 *
	 * @return string
	 */
	private static function otp($result, $pass)
	{
		$challenge = \substr($result, \strpos($result, ' ') + 1);
		$challenge = \substr($challenge, 0, \strrpos($challenge, ' '));

		$otp = new Dalten_Export_Api_Backend_Otp();

		list($sequence, $seed) = \explode(' ', $challenge);
		$a = $otp->generateOtp($pass, $seed, $sequence, 'md5');

		return $a['words_otp'];
	}

	/**
	 * Volá danou metodu s danymi parametry.
	 *
	 * @param string $method Volaná metoda.
	 * @param array  $params Parametry metody.
	 *
	 * @return mixed Odpověď serveru.
	 *
	 * @throws Dalten_Export_Api_Backend_Exception_ResponseError Pri chybne odpovedi.
	 */
	protected function _call($method, array $params = array())
	{
		if ($this->_logger instanceof Dalten_Export_Api_Backend_Logger_LoggerInterface) {
			$this->_logger->logRemoteCall($method, $params);
		}

		try {
			$result = $this->_client->__soapCall($method, array($params));
		} catch (\SoapFault $e) {
			$error = self::_parseSoapMessage($e->getMessage());
			throw new Dalten_Export_Api_Backend_Exception_ResponseError($error);
		} catch (\Exception $e) {
			throw new Dalten_Export_Api_Backend_Exception_ResponseError($e->getMessage());
		}

		if ($this->_logger instanceof Dalten_Export_Api_Backend_Logger_LoggerInterface) {
			$this->_logger->logRemoteResponse($method, $result);
		}

		return $result;
	}

	/**
	 * Dekóduje odpověď soap serveru.
	 *
	 * @param string $txt Zakovaná odpověď
	 *
	 * @return string
	 */
	protected static function _parseSoapMessage($txt)
	{
		return \urldecode($txt);
	}

	/**
	 * Nastaví logger pro backend.
	 *
	 * Logger bude použit pouze pokud to backend dovoluje.
	 *
	 * @param Dalten_Export_Api_Backend_Logger_LoggerInterface $logger Instance loggeru.
	 *
	 * @return Dalten_Export_Api_LoggableApiInterface Fluent interface.
	 */
	public function setLogger(Dalten_Export_Api_Backend_Logger_LoggerInterface $logger)
	{
		$this->_logger = $logger;

		return $this;
	}

	/**
	 * Odstraní nastavený logger.
	 *
	 * @return Dalten_Export_Api_LoggableApiInterface Fluent interface.
	 */
	public function removeLogger()
	{
		$this->_logger = null;

		return $this;
	}
}
