<?php

/**
 * Backend API Reality.sk.
 *
 * @category   Dalten
 * @package    Export
 * @subpackage Api
 */
class Dalten_Export_Api_Backend_RealitySk implements Dalten_Export_Api_Backend_LoggableBackendInterface
{
    /**
	 * Seznam adpatérů každého účtu pro volání metod přes SOAP.
	 *
	 * @var array
	 */
	private $_adapters = array();

	/**
	 * Konfigurovatelné nastavení pro SOAP.
	 *
	 * @var Dalten_Data_ArrayObject
	 */
	private $_soapConfig;

	/**
	 * Logger pro backend.
	 *
	 * @var Dalten_Export_Api_Backend_Logger_LoggerInterface
	 */
	private $_logger;

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

	/**
	 * Chybove hlasenie.
	 *
	 * @var array
	 */
	private $_soapFaults = array(

		'HTTP' => 'Nepodařilo se připojit ke vzdálenému serveru.',

		'MSG001' => 'Pre zadaný user login nebol nájdený žiaden prístupový účet.',
		'MSG002' => 'K user login bol zadaný nevyhovujúci password.',

		'MSG036' => 'Zadaný user login bol nájdený subjekt nachádzajúci sa v zrušenom stave.',

		'MSG040' => 'K zadanému user login bol nájdený uzamknutý prístupový účet.',
		'MSG043' => 'RK nemá validované mobilné číslo.',
		'MSG049' => 'Celkový počet inzerátov RK prekročil povolený limit.',

		'MSG072' => 'RK je pozastavená z dôvodu: pozastavenia adminom portálu, mobilné číslo na blackliste, alebo nezaplatená tarifa. Inzerát sa založí, jeho stav bude nastavený na pozastavný, k spoplatneniu dôjde až budú odstránené dôvody pozastavenia.',

		// nejspis se nevyuzije
		'MSG076' => 'Inzerát nebol nájdený.',
		'MSG089' => 'V servisnom profile realitnej kancelárie nebola nájdená služba importovací SW tretích strán.',
		'MSG089P' => 'Subjekt nemá v servisnom profile priradenú službu pre správu inzercie.',

		'MSG091' => 'Realitná kancelária má pozastavenú službu importovací SW tretích strán.',
		'MSG091P' => 'Pozastavena služba pre správu inzercie.',

		'MSG109' => 'Inzerát nebol nájdený.',
		'MSG110' => 'Účet RK na reality.sk nieje aktivny.',

		'MSG206' => 'Nesprávne userAgentID alebo heslo k userAgentID.',
		'MSG210' => 'Neboli zadané povinné údaje pre založenie inzerátu.',

		// zkontrolovat duplicitu
		'soap:Server' => 'Unknown code error',
	);

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

		$required = array('authNameSpace', 'authToken');
		foreach ($required as $paramName) {
			if (!$this->_soapConfig->$paramName) {
				throw new \Dalten_Export_Exception_General(
					sprintf('Neúplná povinná konfigurace pro export Reality.sk! Chybí "%s"!', $paramName)
				);
			}
		}
	}

	/**
	 * Vrátí SOAP adapter pro daného makléře.
	 *
	 * @param  string $accountName Přihl. jméno jméno makléře.
	 *
	 * @return \Dalten_Export_Api_Backend_RealitySk_Soap
	 * @throws \Dalten_Export_Api_Backend_Exception_NoSoapAdapter
	 */
	public function getAdapter($accountName)
	{
		if (isset($this->_adapters[$accountName])) {
			return $this->_adapters[$accountName];
		}

		throw new \Dalten_Export_Api_Backend_Exception_NoSoapAdapter(
			\sprintf('Požadovaný SOAP adapter pro uživatele "%s" nebyl nalezen.', $accountName)
		);
	}

	/**
	 * Nastaví SOAP adapter pro daného makléře.
	 *
	 * @param string                                    $accountName Přihl. jméno jméno makléře.
	 * @param \Dalten_Export_Api_Backend_RealitySk_Soap $adapter     Instance SOAP adaptéru.
	 */
	public function setAdapter($accountName, \Dalten_Export_Api_Backend_RealitySk_Soap $adapter)
	{
		$this->_adapters[$accountName] = $adapter;
	}

	/**
	 * Zjišťuje, zda má daný uživatel vytvořenou instanci připojovacího adaptéru, což značí, že je přihlášen.
	 *
	 * @param string $accountName Přihl. jméno jméno makléře.
	 *
	 * @return bool
	 */
	public function isLogged($accountName) {
		return isset($this->_adapters[$accountName]);
	}

	/**
	 * Provede přihlášení do vzdálené služby.
	 *
	 * @param string $userAgentId   Přihlašovací jméno realitbí kanceláře.
	 * @param string $userAgentPass Heslo pro přihlášení RK
	 * @param string $accountName   Přihl. jméno jméno makléře.
	 * @param string $password      Heslo pro přihlášení makléře.
	 *
	 * @return Dalten_Export_Api_ServerResponse Odpověď serveru.
	 * @throws Exception
	 */
	public function login($userAgentId, $userAgentPass, $accountName, $password)
	{
		// prihlaseni a nastaveni autorizacniho tokenu do soap hlavicky
		try {

			$params = array(
				'userAgentId' => $userAgentId,
				'userAgentPassword' => $userAgentPass,
				'accountName' => $accountName,
				'password' => $password,
			);

			$soap = new \Dalten_Export_Api_Backend_RealitySk_Soap(
				$this->_soapConfig->soap_wsdl, $this->_soapInitialParams
			);
			$this->setAdapter($accountName, $soap);

			$this->_logRemoteCall('provideAuthorization', $params);

			$authToken = $soap->call('provideAuthorization', $params);

			// nastavime soap hlavicku s autorizacnim tokenem.
			$soapHeader = new \SoapHeader($this->_soapConfig->authNameSpace, $this->_soapConfig->authToken, $authToken);
			$soap->__setSoapHeaders(array($soapHeader));

			$this->_logRemoteResponse('provideAuthorization', $authToken);

			return new \Dalten_Export_Api_ServerResponse(true, 200, 'Přihlášení proběhlo v pořádku.');
		} catch (\SoapFault $fault) {
			unset($this->_adapters[$accountName]);
			return $this->_logAndProccessSoapFault('provideAuthorization', $fault);
		} catch (\Exception $e) {
			// ostatní vyjímky pošleme dále do světa.
			throw $e;
		}
	}

	/**
	 * Vytvoří a vyexportuje novou nabídku.
	 *
	 * @param string $accountName Přihl. jméno jméno makléře.
	 * @param string $advertData  Pole již překonvertovaných hodnot pro odeslání na vzdálený server.
	 * @param array  $userData    Data uživatele (makléře, kterému nabídka patří) ve formátu iRest 1.
	 * @param array  $images      Formátovaný seznam pro obrázky.
	 *
	 * @return Dalten_Export_Api_ServerResponse
	 */
	public function addAdvert($accountName, $advertData, $userData, $images)
	{
		$advertData['assignedAgentEmail'] = $userData['email'];
		$convImages = $this->_adImages($images);
		$advertData['images'] = $convImages;

		$params = array('newAd' => $advertData);

		$this->_logRemoteCall('createNewAd', $params);

		if (!$this->isLogged($accountName)) {
			$response = new \Dalten_Export_Api_ServerResponse(false, 500, sprintf(
				'Neexistuje vzdálené připojení pro daný accountName "%s"', $accountName
			));
			$this->_logRemoteResponse('createNewAd', $response->getMessage());

			return $response;
		}

		$adapter = $this->getAdapter($accountName);

		try {
			$responseData = array(
				'images' => $this->_getImagesToData($convImages),
				'uniqueIdentifier' => $adapter->call('createNewAd', $params),
				'userData' => $userData
			);

			$serverResponse = new \Dalten_Export_Api_ServerResponse(true, 200, 'Nabídka byla úspěšně vyexportována');
			$serverResponse->setData($responseData);

			$this->_logRemoteResponse('createNewAd', $responseData['uniqueIdentifier']);

			return $serverResponse;

		} catch (\SoapFault $fault) {
			$r = $adapter->__getLastRequest();

			return $this->_logAndProccessSoapFault('createNewAd', $fault);
		}
	}

	/**
	 * Zpracuje vzdálené volání editace nabídky.
	 *
	 * @param string $accountName Přihl. jméno jméno makléře.
	 * @param array  $advertData  Pole již překonvertovaných hodnot pro odeslání na vzdálený server.
	 * @param array  $userData    Data uživatele (makléře, kterému nabídka patří) ve formátu iRest 1.
	 * @param array  $images      Formátovaný seznam pro obrázky.
	 * @param array  $data        Přídavná data, která musejí obsahovat klíče 'uniqueIdentifier' a 'images'
	 *
	 * @return Dalten_Export_Api_ServerResponse
	 */
	public function editAdvert($accountName, array $advertData, array $userData, array $images, array $data)
	{
		$advertData['assignedAgentEmail'] = $userData['email'];
		$advertData['uniqueIdentifier'] = $data['uniqueIdentifier'];

		// toto nelze menit a navic to export zakazuje mit tu tyhle vlastnosti.
		unset($advertData['propertyType'], $advertData['transactionType']);

		$params = array('ad' => $advertData);

		$this->_logRemoteCall('changeAd', $params);

		if (!$this->isLogged($accountName)) {
			$response = new \Dalten_Export_Api_ServerResponse(false, 500, sprintf(
				'Neexistuje vzdálené připojení pro daný accountName "%s"', $accountName
			));
			$this->_logRemoteResponse('changeAd', $response->getMessage());

			return $response;
		}

		$convImages = $this->_editImages($images, $data['images']);
		$advertData['images'] = $convImages;

		$adapter = $this->getAdapter($accountName);

		try {
			$responseData = array(
				'images' => $this->_getImagesToData($convImages),
				'uniqueIdentifier' => $data['uniqueIdentifier'],
				'userData' => $userData
			);

			$return = $adapter->call('changeAd', $params);

			$serverResponse = new \Dalten_Export_Api_ServerResponse(true, 200, 'Nabídka byla úspěšně přexportována');
			$serverResponse->setData($responseData);
			$this->_logRemoteResponse('changeAd', $return);

			return $serverResponse;

		} catch (\SoapFault $fault) {
			return $this->_logAndProccessSoapFault('changeAd', $fault);
		}
	}


	/**
	 * Provede smazání nabídky ze vzdáleného serveru.
	 * Povinné parametry pro $data:
	 * - uniqueIdentifier
	 *
	 * @param string $accountName Přihl. jméno jméno makléře.
	 * @param array  $data        Přídavná data, která musejí obsahovat klíče 'uniqueIdentifier'
	 *
	 * @return Dalten_Export_Api_ServerResponse
	 */
	public function deleteAdvert($accountName, $data)
	{
		$params = array(
			'uniqueIdentifier' => isset($data['uniqueIdentifier']) ? $data['uniqueIdentifier'] : null,
			'realizationPrice' => 0,
		);

		$this->_logRemoteCall('cancelAds', $params);
		if (!isset($data['uniqueIdentifier'])) {
			$response = new \Dalten_Export_Api_ServerResponse(
				false, 500, 'Neni predano id inzeratu, ktere se ma smazat.'
			);
			$this->_logRemoteResponse('cancelAds', $response->getMessage());

			return $response;
		}

		if (!$this->isLogged($accountName)) {
			$response = new \Dalten_Export_Api_ServerResponse(false, 500, sprintf(
				'Neexistuje vzdálené připojení pro daný accountName "%s"', $accountName
			));
			$this->_logRemoteResponse('cancelAds', $response->getMessage());

			return $response;
		}

		try {

			$adapter = $this->getAdapter($accountName);

			$return = $adapter->call('cancelAds', $params);

			$serverResponse = new \Dalten_Export_Api_ServerResponse(true, 200, 'Nabídka byla úspěšně smazána');
			$serverResponse->setData(array()); // smažem všechna data
			$this->_logRemoteResponse('cancelAds', $return);

			return $serverResponse;

		} catch (\SoapFault $fault) {
			return $this->_logAndProccessSoapFault('cancelAds', $fault);
		}
	}

	/**
	 * Převede obrázky do požadovaného tvaru pro nově exportovanou nabídku.
	 *
	 * @param array $images Seznam obrázků ve formátu iRest 1.
	 *
	 * @return array
	 */
	private function _adImages(array $images)
	{
		$pos = 0;
		$out = array();
		foreach ($images as $img) {
			$out[] = $this->_parseImageParams($img, $pos);
			$pos++;
		}

		return $out;
	}

	/**
	 * Převede editované obrázky do požadovaného tvaru pro přeexportovávanou nabídku.
	 *
	 * @param array $images    Seznam obrázků ve formátu iRest 1.
	 * @param array $oldImages Uložený seznam obrázků z předchozího exportu.
	 *
	 * @return array
	 */
	private function _editImages(array $images, array $oldImages)
	{
		$imgs = array();

		// nejdrive zjistime, zda nastaly nejake zmeny oproti poslednimu preexportovani
		// pokud je stejny pocet obrazku, zvysuje se sance ze neni zadana zmena
		$noChange = false;
		if (count($oldImages) === count($images)) {
			$noChange = true;
			foreach ($oldImages as $pos => $img) {
				$ord = $pos + 1;
				// zkontrolujeme jmena souboru, pokud je nejaka zmena, nastavime noChange na false;
				if (!(isset($photos[$ord]) && $this->_getImageName($photos[$ord]['foto']) == $img['name'])) {
					$noChange = false;
					continue;
				}
			}
		}

		if (true === $noChange) {
			return $images;
		}

		foreach ($oldImages as $position => $img) {
			// pokus - nejdriv vsechny stavajici smazat
			// a aktualizovane pridat
			$toDelete = array();
			$toDelete['position'] = $position;
			$toDelete['operation'] = 'delete';
			$imgs[] = $toDelete;
		}

		$pos = 0;
		foreach ($images as $img) {
			$i = $this->_parseImageParams($img, $pos);
			$i['operation'] = 'new';
			$pos++;
			$imgs[] = $i;
		}

		return $imgs;
	}

	/**
	 * Převede položku jednoho nového obrázku do požadovaného tvaru.
	 *
	 * @param array $img Obrázek ve formátu iRest 1.
	 * @param int   $pos Pozice Obrázku v seznamu obrázků.
	 *
	 * @return array
	 */
	private function _parseImageParams(array $img, $pos)
	{
		$i = array();
		$i['first'] = $pos === 0 ? 'true' : 'false';
		$i['position'] = $pos;

		$h = \fopen($img['soubor'], "r");
		$binary = \fread($h, \filesize($img['soubor']));
		$i['bytes'] = \base64_encode($binary);
		\fclose($h);

		$i['description'] = $img['popis'];
		$i['name'] = $this->_getImageName($img['soubor']);

		return $i;
	}

	/**
	 * Pomocná metoda vysosne filename z cesty.
	 *
	 * @param string $path Cesta k souboru.
	 *
	 * @return string
	 */
	private function _getImageName($path)
	{
		preg_match('~.*/([_a-z0-9\.]*)$~', $path, $matches);
		return $matches[1];
	}

	/**
	 * Převede formát seznamu exportovaných obrázků na formát pro uložení do response data.
	 *
	 * @param array $convImages zkonvertovaná seznamu exportovaných obrázků.
	 *
	 * @return array
	 */
	private function _getImagesToData($convImages)
	{
		// potreba ulozit si seznam
		$toSave = array();
		foreach ($convImages as $img) {
			$pos = $img['position'];
			if (!isset($img['operation']) || (isset($img['operation']) && $img['operation'] != 'delete')) {
				$toSave[$pos]['name'] = $img['name'];
				$toSave[$pos]['first'] = $img['first'];
			}
		}

		return $toSave;
	}

	/**
	 * Zpracuje výjimku SoapFault.
	 *
	 * @param \SoapFault $fault Výjimka SoapFault.
	 *
	 * @return \Dalten_Export_Api_ServerResponse
	 */
	private function _soapFault(\SoapFault $fault)
	{
		$error = '';
		if (false !== strpos($fault->faultcode, 'ns1:')) {
			$code = substr($fault->faultcode, 4);
			if (isset($this->_soapFaults[$code])) {
				$error = $this->_soapFaults[$code];
				// nespravne hodnoty
				// meli bychom vetsinu odchytit, ale nidky nevis, co si vymyslej
				if ($code == 'MSG210') {
					$error .= ' ' . $fault->faultstring;
				}
			}
		}

		if (empty($error)) {
			$error = 'Nespecifikovana chyba serveru: ' . $fault->faultstring;
		}

		return new \Dalten_Export_Api_ServerResponse(false, 401, $error);
	}

	/**
	 * Nastaví logger pro backend.
	 *
	 * @param \Dalten_Export_Api_Backend_Logger_LoggerInterface $logger Instance loggeru.
	 *
	 * @return Dalten_Export_Api_Backend_RealitySk Fluent interface.
	 */
	public function setLogger(Dalten_Export_Api_Backend_Logger_LoggerInterface $logger)
	{
		$this->_logger = $logger;

		return $this;
	}

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

		return $this;
	}

	/**
	 * Pomocná metoda, obaluje detekci správného loggeru a v případě jeho existence i zalogování volání metody.
	 *
	 * @param string $methodName Název volané metody.
	 * @param array  $data       Data předaná metodě.
	 */
	private function _logRemoteCall($methodName, array $data)
	{
		if ($this->_logger instanceof Dalten_Export_Api_Backend_Logger_LoggerInterface) {
			$this->_logger->logRemoteCall($methodName, $data);
		}
	}

	/**
	 * Pomocná metoda, obaluje detekci správného loggeru a v případě jeho existence i zalogování návratové hodnoty
	 * metody.
	 *
	 * @param string $methodName   Název volané metody.
	 * @param mixed  $responseData Návratová hodnota metody.
	 */
	private function _logRemoteResponse($methodName, $responseData)
	{
		if ($this->_logger instanceof Dalten_Export_Api_Backend_Logger_LoggerInterface) {
			$this->_logger->logRemoteResponse($methodName, $responseData);
		}
	}

	/**
	 * Zpracovává chybu při volání SOAP metody a loguje ji, pokud je připojen logger.
	 *
	 * @param string $methodName Název volané metody.
	 * @param SoapFault $fault   Chyba SOAP.
	 *
	 * @return Dalten_Export_Api_ServerResponse Zpracovaná odpověď serveru.
	 */
	private function _logAndProccessSoapFault($methodName, \SoapFault $fault)
	{
		$this->_logRemoteResponse($methodName, $fault);

		return $this->_soapFault($fault);
	}
}
